From 59caacaf8f23066c79fb09a764a3ffd5ab64bf6f Mon Sep 17 00:00:00 2001 From: karmenrabar Date: Thu, 14 Sep 2023 09:57:35 +0200 Subject: [PATCH 001/104] Add memgraph tutorial --- README.md | 24 + .../memgraph/visualizing_iam_dataset.ipynb | 601 ++++++++++++++++++ 2 files changed, 625 insertions(+) create mode 100644 demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb diff --git a/README.md b/README.md index 4756c86654..73388abcdb 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,30 @@ It is easy to turn arbitrary data into insightful graphs. PyGraphistry comes wit g.plot() ``` +* [Memgraph](https://memgraph.com/) ([notebook demo](demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb)) + + ```python + from neo4j import GraphDatabase + MEMGRAPH = { + 'uri': "bolt://localhost:7687", + 'auth': (" ", " ") + } + graphistry.register(bolt=MEMGRAPH) + ``` + + ```python + driver = GraphDatabase.driver(**MEMGRAPH) + with driver.session() as session: + session.run(""" + CREATE (per1:Person {id: 1, name: "Julie"}) + CREATE (fil2:File {id: 2, name: "welcome_to_memgraph.txt"}) + CREATE (per1)-[:HAS_ACCESS_TO]->(fil2) """) + g = graphistry.cypher(""" + MATCH (node1)-[connection]-(node2) + RETURN node1, connection, node2;""") + g.plot() + ``` + * [Azure Cosmos DB (Gremlin)](https://azure.microsoft.com/en-us/services/cosmos-db/) ```python diff --git a/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb b/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb new file mode 100644 index 0000000000..c552e82bee --- /dev/null +++ b/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb @@ -0,0 +1,601 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tutorial: Visualizing Identity and Access Management data set with Memgraph" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook showcases the utilization of Graphistry to visualize data from Memgraph using a sample dataset related to a company's Identity and Access Management. We'll demonstrate how Graphistry streamlines the visualization of Cypher queries, making it easier to analyze extensive data effectively." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### About the dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Identity and Access Management (IAM) outlines who can access what, why, and when. Each organization's unique identity and structure shape how access is managed, forming the company's IAM. If the current IAM system becomes slow and unresponsive – unable to handle changes in team roles and permissions – graph databases are a leading solution. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### About Memgraph" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "[Memgraph](https://memgraph.com/) is an open-source, in-memory graph database. It is compatible with Neo4j's Bolt protocol and supports the widely used Cypher query language for interacting with the database. Cypher provides a powerful and expressive way to work with graph structures and perform various operations on the nodes and relationships within a graph database.\n", + "\n", + "A convenient entry point to kickstart your journey with Memgraph is through Docker. By simply entering the following command in your terminal, you can set up the Memgraph Platform within a Docker container:\n" + ] + }, + { + "cell_type": "raw", + "metadata": {}, + "source": [ + "docker run -it -p 7687:7687 -p 7444:7444 -p 3000:3000 -e MEMGRAPH=\" --bolt-server-name-for-init=Neo4j/\" memgraph/memgraph-platform " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If everything went well, after a couple of seconds you should see a message that Memgraph Lab is running at localhost:3000. You can access it through your web browser and start exploring !" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Configuration and installation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To begin, make sure to install the Graphistry Python client and the Neo4j Bolt drivers. You can achieve this by removing the comment symbol (#) from the first two lines in the provided code snippet." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "#!pip install --user graphistry\n", + "#!pip install --user graphistry[bolt]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, import the necessary dependencies, including pandas, graphistry, and GraphDatabase. These libraries will be utilized to load and work with the data." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import graphistry\n", + "from neo4j import GraphDatabase" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lastly, establish a connection with your Graphistry GPU server account. Make sure to substitute the connection string and password with your personal credentials. You can create your account [here](https://hub.graphistry.com/). For additional configuration options, refer to [GitHub](https://github.com/graphistry/pygraphistry#configure)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# To specify Graphistry account & server, use:\n", + "# graphistry.register(api=3, username='...', password='...', protocol='https', server='hub.graphistry.com')\n", + "graphistry.register(api=3, username='k', password='123Rocky') " + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Connecting to Graphistry and Memgraph" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We'll establish a connection to a Memgraph database using the Bolt protocol. The Bolt protocol is a binary communication protocol that facilitates interaction between the Python code and the Memgraph database.\n", + "\n", + "The URI includes the hostname (localhost) and the port number (7687) where the Memgraph database is listening for Bolt connections. The authentication part includes a tuple with the username and the password that you would use to authenticate and gain access to the Memgraph database. \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "MEMGRAPH = {\n", + " 'uri': \"bolt://localhost:7687\", \n", + " 'auth': (\" \", \" \")\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After that, we can use the Graphistry library to register a connection to a database using the Bolt protocol and the provided configuration.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "graphistry.register(bolt=MEMGRAPH)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Uploading the dataset" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can now initialize a Memgraph driver instance. Following this, we'll be able to utilize the session.run() method to execute Cypher queries." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "driver = GraphDatabase.driver(**MEMGRAPH)\n", + "\n", + "with driver.session() as session: \n", + " session.run(\"\"\" CREATE (per1:Person {id: 1, name: \"Julie\"})\n", + "CREATE (per2:Person {id: 2, name: \"Peter\"})\n", + "CREATE (per3:Person {id: 3, name: \"Anna\"})\n", + "CREATE (per4:Person {id: 4, name: \"Carl\"})\n", + "CREATE (tea1:Team {id: 1, name: \"Engineering\"})\n", + "CREATE (tea2:Team {id: 2, name: \"Operations\"})\n", + "CREATE (tea3:Team {id: 3, name: \"Marketing\"})\n", + "CREATE (rep1:Repository {id: 1, name: \"Memgraph\"})\n", + "CREATE (rep2:Repository {id: 2, name: \"MAGE\"})\n", + "CREATE (rep3:Repository {id: 3, name: \"Marketing\"})\n", + "CREATE (com1:Company {id: 1, name: \"Memgraph\"})\n", + "CREATE (sto1:Storage {id: 1, name: \"Google Drive\"})\n", + "CREATE (sto2:Storage {id: 2, name: \"Notion\"})\n", + "CREATE (fol1:Folder {id: 1, name: \"engineering_folder\"})\n", + "CREATE (fol2:Folder {id: 2, name: \"operations_folder\"})\n", + "CREATE (acc1:Account {id: 1, name: \"Facebook\"})\n", + "CREATE (acc2:Account {id: 2, name: \"LinkedIn\"})\n", + "CREATE (acc3:Account {id: 3, name: \"HackerNews\"}) \n", + "CREATE (fil1:File {id: 1, name: \"welcome_to_engineering.txt\"})\n", + "CREATE (fil2:File {id: 2, name: \"welcome_to_memgraph.txt\"})\n", + "CREATE (fil3:File {id: 3, name: \"operations101.txt\"})\n", + "CREATE (fil4:File {id: 4, name: \"expenses2022.csv\"})\n", + "CREATE (fil5:File {id: 5, name: \"salaries2022.csv\"})\n", + "CREATE (fil6:File {id: 6, name: \"engineering101.txt\"})\n", + "CREATE (fil7:File {id: 7, name: \"working_with_github.txt\"})\n", + "CREATE (fil8:File {id: 8, name: \"working_with_notion.txt\"})\n", + "CREATE (fil9:File {id: 9, name: \"welcome_to_marketing.txt\"})\n", + "CREATE (per1)-[:HAS_ACCESS_TO]->(fil2)\n", + "CREATE (per2)-[:HAS_ACCESS_TO]->(fil2) \n", + "CREATE (per2)-[:IS_PART_OF]->(tea1)\n", + "CREATE (per2)-[:IS_PART_OF]->(com1)\n", + "CREATE (per2)-[:IS_PART_OF]->(tea2)\n", + "CREATE (per3)-[:IS_PART_OF]->(tea2)\n", + "CREATE (per3)-[:IS_PART_OF]->(tea3)\n", + "CREATE (per3)-[:IS_PART_OF]->(com1)\n", + "CREATE (per4)-[:IS_PART_OF]->(tea1)\n", + "CREATE (per4)-[:IS_PART_OF]->(com1)\n", + "CREATE (per4)-[:HAS_ACCESS_TO]->(fil2)\n", + "CREATE (com1)-[:HAS_TEAM]->(tea1)\n", + "CREATE (com1)-[:HAS_TEAM]->(tea3)\n", + "CREATE (com1)-[:HAS_TEAM]->(tea2)\n", + "CREATE (fil1)-[:IS_STORED_IN]->(sto1)\n", + "CREATE (fil1)-[:IS_STORED_IN]->(sto2)\n", + "CREATE (fol2)-[:IS_STORED_IN]->(sto1)\n", + "CREATE (fil9)-[:IS_STORED_IN]->(sto1)\n", + "CREATE (fil9)-[:IS_STORED_IN]->(sto2)\n", + "CREATE (fol1)-[:IS_STORED_IN]->(sto1)\n", + "CREATE (fil2)-[:CREATED_BY]->(per3)\n", + "CREATE (fol1)-[:HAS_ACCESS_TO]->(fil6)\n", + "CREATE (fol1)-[:HAS_ACCESS_TO]->(fil7)\n", + "CREATE (fol1)-[:HAS_ACCESS_TO]->(fil8)\n", + "CREATE (fol2)-[:HAS_ACCESS_TO]->(fil3)\n", + "CREATE (fol2)-[:HAS_ACCESS_TO]->(fil4)\n", + "CREATE (fol2)-[:HAS_ACCESS_TO]->(fil5)\n", + "CREATE (tea2)-[:HAS_ACCESS_TO]->(fol2)\n", + "CREATE (rep3)-[:HAS_ACCESS_TO]->(acc1)\n", + "CREATE (rep3)-[:HAS_ACCESS_TO]->(acc2)\n", + "CREATE (rep3)-[:HAS_ACCESS_TO]->(acc3)\n", + "CREATE (rep3)-[:HAS_ACCESS_TO]->(fil9)\n", + "CREATE (tea1)-[:HAS_ACCESS_TO]->(rep1)\n", + "CREATE (tea1)-[:HAS_ACCESS_TO]->(rep2)\n", + "CREATE (tea1)-[:HAS_ACCESS_TO]->(rep3)\n", + "CREATE (tea1)-[:HAS_ACCESS_TO]->(fil1)\n", + "CREATE (tea1)-[:HAS_ACCESS_TO]->(fol1)\n", + " \"\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "g = graphistry.cypher(\"\"\" MATCH (node1)-[connection]-(node2) RETURN node1, connection, node2;\n", + " \"\"\")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Visualization of the data \n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After populating Memgraph instance, it's time to visualize the dataset with graphistry. But first, let's see the graph schema in Memgraph Lab. It defines the structure of your data and its relationships, providing a blueprint for how your data elements are connected and organized within the graph database and offers interactive graph visualizations.\n" + ] + }, + { + "attachments": { + "Screenshot from 2023-08-31 13-12-54.png": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABgUAAAJ9CAYAAAAVNHIjAAAAi3pUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjaVY7dDYRACITfqcIShp+FpZyLp4kdXPnHuibq9wADITPQ9jt2WgYMI2vRPd1RWFrKp0THRAEW8OhVJ1dXLiU1XjOpTOHZA3YfmuFFU+++h0V489VXKXfZVM/KCjqvhtl4JR/JDT6j3/v4Qp4B9Af43S0bt0tsygAACghpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSIxNTQxIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iNjM3IgogICB0aWZmOkltYWdlV2lkdGg9IjE1NDEiCiAgIHRpZmY6SW1hZ2VIZWlnaHQ9IjYzNyIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIvPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg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AB7H6XRqxYoVamtrU2BgoGbOnKlp06Zp27ZtamhoUHZ2tt577z2NGTNGI0aMUGNjo/72t7+ppaVFK1eu1Jo1azRp0qQf9N6vvvqq6urqtGXLFr366qsaP368JOngwYMyGo06evSoVq9ereLiYtdogNOJiYlRYmKiFi5cqAkTJnhE/5pMJl177bUaO3as9u3bpyVLlnzvyAoAAAB4D0IBAACAExwvISJJbS3td6TAiW0/cZsAokXpqwAAIABJREFUT1JVVaXbb79dZWVlkqS5c+dq5MiRuv766xUZGan58+fr3XffVZ8+fWQymbRy5Uq9/fbbslgsmj17tnJyctSrV68f/P4DBgzQiBEjNH36dNeIgGeffVb//e9/FR0drX/84x9KT093TTx+JhkZGbJYLEpLS/Oofu7UqZMmTJigq666Sh988IFWrVql+vp6DkAAAAAvZWhqanIev3NizU0AAABvVFhYqMzMTElS/ZHX1daS3C63w8dvvcwdb5UkLVmy5JLVNwc8UXR0tF577TUNHz78vK73hRde0KeffqoXXnjBY7e9vr5e69at04EDB5SQkKABAwYwrwgAAMBlprm5+bSP8+0PAADgBMevWJYkpzO43W7HiW0/cZsAnH8Oh0NlZWWaP3++fvnLX3p0W81ms0aNGqUbbrhBW7du1dKlS/XNN9+wEwEAALwIoQAAAAAA/AgbN25Uv379NGbMGI8rHfR9YmNjlZmZqa5du+q9995Tfn6+Ghsb2ZkAAABewJcuAAAA+D8xMTGuvw2G2na7HSe2/cRtAiCVl5ef1/UNGTKkXdbo9/X1VXJysvr27av8/HwtWbJEKSkp6tu3LwcJAADAZYyRAgAAAADgxcLCwjRu3DilpKSoqKhI7777rqqrq+kYAACAyxShAAAAwAni4uJcf/v4FbXb7Tix7SduEwB8n759+yozM1NBQUHKycnR+vXr1draSscAAABcZggFAAAAThAaGnrZbdORI0dUV1fHzgVwRiaTSSNGjNCYMWO0Z88eLVmyRPv376djAAAALiPMKQAAAPAtp9OpgwcPKiIiQpWVlfL1L1Jzw73t80ue/7GRAlarVVu3bpXNZpPRaJTValVERISsVqusVquCg4PZ8QBOEhUVpYyMDG3evFl5eXmKjo7WsGHDZDab6RwAAIB2ztDU1OQ8fsff358eAQAAXqWh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3G2eAovFQvkweNT/q0pKSlRcXKzIyEilpaUpJCSEjgEAAF7zHf50GCkAAADOmsPh0MqVK+Xj46Mbb7zRq2qPp6amfu9J97q6Oi1YsEAhISHq1q2bSkpKdPvtt2vVqlUaOHCgbrnlFj355JP605/+5PqCtm3bNu3bt0+LFy+Wv7+//vznP6uiokLPPvusevfurc2bN2vMmDGaPXu21qxZo1WrVunGG2/Utm3bFBMTI0nKy8tTfn6+KioqlJKSoilTpmjQoEEcqDiv/P391aVLF3Xp0sW1rKWlxRUUHDx4UFu3blVNTY3CwsLcSg9ZLBb5+vKTAxef0WjUoEGD1KtXL61bt07Z2dmKj49XfHw8o1wAAADflegCAABwNk4MBNLT071uMtKOHTvquuuuc91vamqSwWCQwWDQFVdcoWXLlum9995TYGCgXnnlFd18881KTU2V2WzWH/7wB7377ruu1959993q3Lmzdu/eLX9/f9XV1enIkSPq2rWrnnzySVksFuXk5KhDhw6aOXOmAgICNG7cOF1//fV68803Xet56KGHZLVaNWjQIPXr10+7du3iQMVF4efnp6ioKA0cOFDXX3+9Jk6cKKPRqCeeeEJWq1U2m02LFy9W//791aFDB4WFhWny5Mn68ssv1dLS8r3rNZlMrs+VyWTSoEGDlJOT4/acN998U/7+/qqsrHRb/vTTT7te6+fnp+7du2vOnDlyOp0KCgpyPXbi7Uwjnex2u6ZOnSqr1arQ0FCNGzdO+/btO+V7nniz2WwcJB7CbDZr1KhRGjlypL744gstXbpU33zzDR0DAAC8GqEAAAA4I4fDoeXLl3ttIHAqAQEBcjqdcjqdqq2t1UcffaRrrrlGklReXq5XX31VJpNJJpNJI0eOPOVJqFtuuUXvvvuufvGLX+j555/XY489JpPJpHvuuUfR0dHq3r272/O7deum/fv3u+6fuB98fHzU2trKwYpL98PCaJSfn5/69++vpKQkPfvss7r77rv1xRdf6O2339bu3bv1m9/8Ri+//LLeeustrVmzRp9//rkqKirchjd/9NFHamlp0eHDhzV37lxNmTJFxcXFrscXLVqkoKAgvfbaaye1YeTIkWppaVFdXZ3eeustPf/881q8eLHq6urkdDpdQcLXX38tp9PpVgLsu5xOp8aOHauDBw+qoKBAn332maKjozV8+HA1NDS4npeenu76t+D4zWq1ckB4mNjYWGVmZqpr16567733lJ+ff9r9DwAAcFl/d6cLAADA6bS1tWn58uXy8/MjEDhLXbp00fTp09XY2Oi6fd+Vw9u3b9cDDzygFStWaNasWXr88ce1fv16xcbGugUAkrRnzx517tyZDobH27t3rw4fPqzf/va36tq1q0aMGKEFCxaoW7duuuOOO3TDDTcoKipKNTU1+vTTT/XKK6/ozTfflMPh0FdffaWDBw/KaDRq3LhxSkxM1Pr16yVJZWVl+uijj5SVlaVFixad9L4Gg0G+vr4KCAhQcnKyRo8e7RYonIuCggJt2bJFb7zxhuLi4tSjRw/9/e9/l9ls1ltvvcVObod8fX2VnJysCRMmqLq6WkuWLNGOHTvoGAAA4HUIBQAAwPdqa2vT+++/TyAgqbq6Wvn5+Wf13MmTJ2vx4sX65JNP1NraquXLl+vpp58+5XPvvfdePfzww2pra1NYWJgCAgLU3NysESNGqLGxUX/729/U0tKilStXas2aNZo0aRIHJjxenz59FB8fr8mTJ+vzzz+XJA0ePFgvvfSSjEajrFarrrzySg0bNkzjx4/XHXfc4fo35ujRoyouLtYrr7yiBx98UBs2bFCHDh20f/9+vfDCCxo5cqQmTZqkvXv3auPGjad8f6fTqU2bNmnVqlW6+uqrf9A2rF+/XklJSQoNDXUtMxgMSktLU1FRETu5HQsLC9O4ceOUkpKioqIivfvuu6qurqZjAACA1yAUAAAAp3Q8EAgICGCEgKSPP/5Y06dPP6vnJiQkaMGCBbrzzjsVGhqqOXPm6Kc//ekpn/vWW29px44dGjp0qP74xz/qnnvu0bBhw2QymbRy5Uq9/fbbslgsmj17tnJyctSrVy8OTng8X19fFRQUaNCgQbrpppvUt29fzZ8/X21tbaf+UWI0Kjw8XAaDQdOmTdNNN92k6dOna9myZfp//+//qV+/fvrss8/073//W9HR0Vq7dq2uueYa/eUvf1FdXZ1rPatXr5bJZFJAQIASExP18MMPa/LkyT9oG44cOaLIyMiTlkdERLidQM7NzXWbT+D7AkB4nr59+yrz/7N35/FNlXn//1/pRpekCV0otAUKFLCMshWEouxKUUBkL4ML6K0y3qIijI6jXxV01Jlxit4u37lVpIoDyo6AQEEtuJR9GygCpYBlLQ2mTbqnye8Pfs3XDKsLpSnv5+ORB8l1kpNzPuekJNfnXNdnzBgsFgsLFixg48aNmoZNRERErgmGiooKd82DoKAgRUREREQ8V7crIVB7cnJy2L17N6NHj1YwxCdlZGTw1ltvnffq/W+//ZbHHnuMjh078v77719wHcHBwWRlZdG9e/dzlmVlZTFo0CByc3MpKSlh6dKlPP/887z++usEBQXx5Zdfsm/fPubMmYPRaKR9+/a89957DB482LOOwsJCoqOjOXHixCWn4vrrX//Kl19+yerVq73aH374YZxOJ++++y6vvvoqWVlZrFq1SieAjzt16hTr1q2jqqqKnj170qxZMwVFREREfNZPa3adj0YKiIiIiBen08myZcuUEKhlirPUJ4sXL+a5557zPL7pppv429/+xvz583/xOmfNmkVpaSmxsbG0bt2aqVOnUlJSgtFoZNiwYcTExABn63SsXLmS3r1788QTT7BhwwZyc3MpKir6We/XvXt3Nm3ahN1u97S53W7Wr19Pt27ddJDrmZiYGEaOHMn111/P2rVrWbVqFSUlJQqMiIiI1EtKCoiIiIiX1atXExISooTAVeB2uxUEqRdatWpFeno6s2fPxuFwcPr0aT766CM6duz4i9Znt9tZuHAhmZmZuN1uz23SpEnMmjULk8lEw4YNMZvN3H777dxzzz38z//8D4WFhWzatInc3FyWLVvGnDlzANi6dSsHDhzAZrNd8HPXq1cv2rdvz7hx49i/fz/5+fk8/vjjOBwO1faorz+O/fzo0KEDY8aMAeDTTz9l586duFwuBUdERETqlQCFQERERGps3ryZiooK7rzzTiUEapnBYFBSQOqN9u3bs2LFCv7P//k/PPTQQ4SEhNCnTx8+/vjjX7S+efPmER8fzy233OLVPmnSJNq1a0d+fv45r2nUqBFTp05lzpw5nsLAP/zwA4899hgBAQHk5eWxadMmKioqiIyMJDo6mqioKKKiorBYLPj5+fHZZ58xefJkUlJSqKqqok+fPnz11VeEhYXpINdjYWFhDBw4kB9++IGvv/6affv20bt3b89oFBERERGf//2pmgIiIiICZzvLvvrqK0aNGkVoaKgCUsv27dvHtm3bfnFRVBFfkZ6e7jW1UI1t27bRpk2bWt+eoUOHsmbNGgDPFeHh4eH87//+rydJEB0dTUREBH5+Gmh9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Ef2fgdVqJS8vj+3bt5OVlcXSpUv56KOP+Pjjj1m/fj2HDx/G5XIRHx9Pjx49GDt2LOPHj2fYsGH069eP5ORkEhMTiYqKUkKgjjly5AiLFy8mLCzMKyFQw2w2k56ezuOPPw6A3W1nYul4ppU/w3HXsSu2Xcddx5hW/gxTyx71JAQef/xx0tPTlRAQERGpQzRSQERExEeUlZUBXPMjBY4dO0bv3r158skn+fTTT7Hb7dx333089thjng7zAQMGsGLFCqqrq9mxYwfDhg2jWbNmTJgwAYBXXnmFP/3pTxd8jw8//BCz2cysWbO48847AVi+fLln+fXXX8+zzz5LWlqa12vat2/P9u3bzzt/ds02Wa1W5s+fz6233sr69esvWYw4NTWVVatW4XQ6Wb9+PYMGDaJbt2785S9/oW3btmzfvp1OnToBMG3aNO6++26uv/56fWBErgEul+u8V/sXFRVRXl5OeHg4ZrMZi8VCTEwMbdu2xWw2ExoaquD5sG3btrFlyxY6dAS3HtUAACAASURBVOjAjTfeiMFguOBzn3jiCeLj45k2bRp2u53lVUtYXrWEsYF380DQw79ZrQG7q5j3Kt9hbtVsT5vJZCI9PV1FhUVEROogJQVERER8RHl5OcA135nzf//v/6VLly48/fTTAERERJCens6tt95KVVUVAAaDgYCAAAICAujWrRu33XYbW7du9SQFLiUjI4M33niDBx98kFOnTl2yuLPNZqOgoICGDRtesKBmzTbFxMTwyCOPkJ+fzwsvvMCiRYsu70tbQAD9+vUjISGB3bt3M3bsWB555BGmTZvGkiVL2LNnDytWrOD777/Xh0WkHnG73djt9vMW+C0tLcVoNBIeHo7FYiEyMpKWLVtiNpsxGo0X7SwW31OTHD58+DD9+vUjMTHxsl43evRoUlJSmDFjBgsWLABgbtVsljuX0DugP8l+Xekd0O9nJwjsrmLWOb9kq2sz65xfeEYGwNnpgiZPnkzTpk114EREROogJQVERER8RFlZGQEBARfsdL5WbNu2jZ49e3q1dejQgYKCgnOe63a72b59O6tWreK11167rPV//fXX/Pjjj4wdO5bZs2cze/Zspk6detHXrFu3joYNG3qmMLocAwcOZObMmZf9fJfLxcqVKzl16hTdunUD4M9//jOJiYls376dV155hUcffZT4+Hh9WER8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bGxs1FugmXNxcWH06NH07t2b9evXk5iYqLJRzURFRQUrV67k4MGDjBgxAl9fXwVFGqXc3Fzz7y2NTfc8Ljz2C89JRERE5FKUFBAREZFGx2Aw0Lt3b3bt2kVtba0C0syvdUBAAOPGjaOyspK4uDiysrIUmCasqKiIZcuWUVNTw0MPPUTr1q0VFBERERGRRkRJAREREWmUunXrhtFoVG3kO0TLli158MEHCQoKYsuWLaxZs4by8nIFponZv38/33zzDW3btmXkyJE4ODgoKNKoeXl5mX8vrWm653HhsV94TiIiIiKXoqSAiIiINEqWlpb07NmTzMxMTCaTAnKH6NmzJ1FRUQDExcWxe/duBaUJMJlMbN26lQ0bNhAQEEBYWBhGo1GBERERERFphJQUEBERkUbLz8+PmpoasrOzFYw7iIODA8OHD+fuu+8mLS2NFStWUFpaqsA0UtXV1axZs4Y9e/YQERFBnz59FBRpUv/PnJd6oukmoC889gvPSURERORSlBQQERGRRsvKyopevXqRkZGh3gJ3oO7duxMVFYWNjQ3x8fHs2LFD7aCROXXqFMuXL+f06dOMHj2aDh06KCjSpDg5OemcRERE5I6jpICIiIg0ar169eL06dMcPHhQwbgD2dnZER4ezj333MPOnTtZvny5eg00EocPH2b58uXY29szevRonJ2dFRRpktq1awc0j54C589FRERE5EqUFBAREbnDREZG8vbbb9ebVllZicFgICsrq970RYsWYWVlxfHjx+tNr62t5S9/+Qvt27fH1taWfv368d13313T/n/++WcsLS35+uuvL5pXXFzMpEmTcHV15a677mLkyJEcPXoUX19fMjMzqaur469//SsdOnTA3t6eAQMGsGHDBvP6NjY2GAyGej/jx48HoLCwkIkTJ+Lu7o6DgwMPPvhgvUTDokWL6NmzJ7a2tnTu3Jn333//iufh4OBw0b4MBgM2NjbmZb755hsCAwOxtbWlS5cu/O///q8a4A3y9vYmKioKBwcHc6+Buro6BeY2SU9PJyEhAT8/P4YPH06LFi0UFGmyIiIiANhWDCVnm15ioOSsiW3F9c9FRERE5EqUFBAREZHLmjdvHg4ODnzxxRf1pr/33nusWLGC7777juLiYl544QVGjx59TbX/FyxYgJOTE/Pmzas3vba2luHDh1NeXk5GRgaHDh2ic+fOPPDAA/To0YOioiL+8Ic/sGjRIpYtW0ZhYSHTpk1j1KhR9fabkpKCyWQy/yxevBiA3/3udxgMBvbs2WNONERGRlJTU0NqaipTpkzhgw8+4OTJkyxevJjY2FgWLFhw2fMoLy/HZDKZEyZ5eXmYTCYqKysBWLduHY8++ijTp0/n4MGDfPDBB7z55ptKDPwKNjY25l4Du3fvZtmyZRQXFyswt1BNTQ0bNmxgx44dhIWFMWDAAAwGgwIjTVpwcLD593X5TS8pcOExKykgIiIi10JJAREREbmk3Nxcvv/+e2JjYy96gL99+3buv/9+unbtiq2tLePGjePpp5++phI/8+fP5/333ycxMZGCggLz9H//+9/89NNPzJ8/n/bt2+Ps7Mzf/vY3ysrK2LNnD56ensybN4+PP/6Yvn37Ym9vz/jx4xk2bNglex380vbt23n00Udxd3fH2dmZ119/HX9/f/Ly8khPT6dHjx4MHToUGxsbgoKCePPNNzlx4sQNx+/NN99k+vTpPPLII7Rp04bIyEhiY2N555131Lh+pfO9BlxdXVm2bBlpaWnU1tYqMA2svLycb775hmPHjjFy5Eg6d+6soEizEBERgaOjIwCpRU3v+M8fs6OjY70Eh4iIiMjlKCkgIiIilzR//nzCw8MZP348OTk5pKWlmeeNHz+ejz/+mE8//ZTy8nLg3EPwyMjIK25z06ZNnDx5kgkTJjBkyBA+//xz87z09HQCAwNxcHAwT7O0tCQ3N5fg4GBsbGyoqamhffv29ba5ZMkSXnjhhauez4QJE4iJiSEhIYHa2lqsrKyIj4/Hy8uL4cOHk5WVxYwZM/j5558BmDhxItOnT7/h+G3dupV777233rTQ0FCOHDnCsWPH1MB+JSsrK4YOHUpkZCTZ2dnEx8f/qiSOXFlBQQFLly7FaDQyZswYXF1dFRRpVs4/TP+uwNSkSgiVnDXxXYGp3jmIiIiIXI2SAiIiInegl19+uV4dfFtb23rzTSYT8+fPZ/z48djY2DBy5Mh6vQXGjBnD8uXL+frrr2nXrh1RUVH8+OOPV93vvHnzeOihhzAajURHR9fbZllZGS4uLpddt66uDnt7e3bt2nXFfQQHB9c7t/z8fADmzJnD9OnTmTlzJm3atGHatGkUFZ37eqW3tzcZGRkUFhbSu3dvhgwZwqpVq244vlVVVZw5c4ZWrVrVm+7u7g7AqVOn1AhvEk9PT6KiomjXrh3Lly8nNTWVmpoaBeYm2rt3LytXrqRTp06MGDECOzs7BUWancmTJwNQWg3vZzedpMD72SZKq+ufg4iIiMjVKCkgIiJyB3rrrbfq1d0/c+ZMvfkbN24kPz+fESNGABAdHc2iRYuoqqoyLxMSEsLKlSs5dOgQffr0ITg4+IpjCpSXlxMfH28e+HfMmDHs27ePbdu2AeDm5nbFh+Vubm6cOXOGI0eOXPEb4b8cU8DDwwMAg8HApEmT2Lp1KykpKRQWFjJ48GCqq889TfH29uaTTz4hLy+PqVOn8vjjj180lsK1sra2xtbW9qLjPD/+gLOzsxrhTWQ0GhkyZAgjRowgJyeHuLg4czJIblxdXR2bNm3i+++/Z9CgQQwZMgQLC90+SPNTVVWFhYUFHTp0AGD+IRNHKxp/YuBohYn5h84d54ABA9RTQERERK6ZPtWLiIjIRebNm0dFRQUODg4YDAZGjBjByZMnWb58OXAuSbBnzx4AXFxceOmllwgICGD16tWX3WZ8fDzl5eUMHToUg8GAi4sL1dXVzJ07F4DevXuTkZFRL0FRV1dH586d2bZtG126dMFoNHLmzBkyMzPNyzzyyCPMnj37iuezb98+xo4dS11dHQA+Pj7Mnz+f7Oxsdu/ezeuvv05cXBxwrixNdHQ0Tz31FPHx8TccwwEDBrB+/fp605KSkvDy8qJNmzZqZA3Aw8ODcePG0alTJ1auXMmmTZs4e/asAnMDKioqWLlyJQcPHmT48OH4+voqKNLsVFVVsXXrVhYuXEhBQQEvvfSSed6cnxp/UuDCY3zuued0QUVEROSaKSkgIiIi9ZSVlbF06VISExPrfeP+6aefNpf7MRgMPPPMM2RnZ1NVVcXGjRvJzMzE39//studN28ef/7zn+ttc+nSpSxevJgzZ84wdOhQunfvzpNPPkleXh4nT57k5ZdfxsrKisDAQOzs7Jg2bRqffvop33//PQUFBcTFxbFy5Uruv//+K56Tl5cXaWlpPP/88xQWFnL69Gk+/vhj7Ozs8PHxwd3dnZiY/ZEQygAAIABJREFUGDZv3kxlZSX79u1j1apVBAQE3HAcZ8yYwbvvvsuiRYs4fvw469evZ/r06bzwwgsYDAY1tAZiaWnJwIEDGT16tLmNHD16VIG5DkVFRSxbtoyamhoeeugh2rVrp6BIs3LmzJl6yYDhw4fz4IMPcv/99zN27FgAlh01sS6/8SYG1uWbWHb03PGNHTtWvQRERETkuhiqqqrMn3SsrKwUERERkWYuMjKS0NDQet+IrKysxNbWlr1797J582beffdd9u7dW+/h9b59+/Dz8+PgwYO4uroyc+ZMFi9ezIkTJ+jYsSPPPfccv//97y+5z/379+Pr68vBgwfx8vIyT6+trcXHx4c33niDiRMnkp+fz9NPP823336LtbU1v/nNb4iNjcXb2xuAmpoaZsyYwUcffURVVRWBgYG8+eabhIaGAmBjY1OvxBFAREQEa9euJScnhxdffJHExESqq6vx9/fnrbfeIiQkBIB//vOffPDBB+zfvx93d3eioqJ4++23r/r56MSJE7i7u5OXl2cuVXTe119/zaxZs8jKysLT05NnnnmGp59+Wo3wFqmrqyM9PZ0dO3bg4+NjHrBaLm///v0kJyfj7e1NSEgIRqNRQZFm48yZM2RkZLB3717atGlDnz59Lnrfzs3NJTIykrKyMlq2gC+DLfBzalyJ3D0lJh5OqaO0GhwdHVm7dm29/1tFRERErtZjWkkBERERuanc3NyorKysN23s2LHMnz//pu0jNzeXtWvXMnHixAYf9HT06NGsW7eu3rTWrVtz4MABXewmori4mKSkJMrLywkJCTEnmeT/mUwmtm3bRmZmJkFBQfTp00dBkWbjl8mAfv36mQd+v5S4uDhiYmIA8G15LjHgZNU4EgMlZ008kFzHz/+ptPfJJ58QERGhiywiIiL1KCkgIiIizdKyZcvw8PDg7rvvVjDkqkwmE5mZmWzfvh0vLy8GDx7c4AmlpqK6uprExEQKCwsJCwszD7Yq0tSdPn2azMxM9u7di6enJ3369LliMuBCsbGxzJkzB4ABrrDobstGcU73b6xlb+m535999lmNJSAiIiKXpKSAiIiINEsHDhwgKSmJRx99VJ9h5JqVlpaSlJREcXExAwcOpHv37nd0PE6dOkViYiIA4eHhODs7q5FIk3f69GnS09P56aef8PLyol+/fri4uFz3dp577jm++uor4Fxi4J9Bt6/HQMlZE0+l1bG16NzrsWPHEhsbq4stIiIil6SkgIiIiDRLJpOJJUuW0KVLF/r27auAyHXZvXs327Ztw8PDg5CQEBwcHO64GBw+fJgNGzbQqlUr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17/+NadOnWL58uUaHBERERFpN1RoWEREROQ6ubq6MmTIEKKiopgwYQI2NjZs376dVatWsWvXrsuKkyYnJxuBQOiQYa0+EAC4y/UuQocMAyAlJUWzBaRNeP/994mOjqa2tpasrCxWrFhBeHi4BkZEREREpBlrDYGIiIjI1XXp0oUuXboQGhpKcXEx2dnZbNmyBQA/Pz/8/f1ZunQp0FRceMzosWbTtzGjx5J+YB+1tbUsXbqUsLAwHXAxaw899BCbN2/G3d0de3t7Zs+ezbRp0zQwIiIiIiLNaPkgERERkRtQWFhIdnY2x44d43/+53+orq4muH+IUcTXXKz/eB37D6bj7OzM119/rQMrIiIiIiJi5rR8kIiIiMgt4O7uzogRI3Bzc6O6uhoAXx9fs+vHpTaXl5eTmJioAysiIiIiItLGKRQQERER+QGar8XfNyDQ7NrfvM2qKyAiIiIiItL2KRQQERER+QGSkpIA8PH2wc7Wzuzab2drh4+3T4u+iIiIiIiISNulUEBERETkBygoKADA16en2fbhUtsv9UVERERERETaLoUCIiIiIjeorKxMfRIRERERERGzolBARERE5AZlZGQY3/f0Nd+ZAs3b3rxPIiIiIiIi0vYoFBARERERERERERERaScUCoiIiIjcoPz8fON7W1tbs+1H87Y375OIiIiIiIi0PQoFRERERERERERERETaCYUCIiIiIjfI29vb+L6mpsZs+9G87c37JCIiIiIiIm2PQgERERERERERERERkXZCoYCIiIjIDQoKCjK+z8nNMdt+NG978z6JiIiIiIhI26NQQEREROQGubi4qE8iIiIiIiJiVhQKiIiIiPwAnp6eAOTmme9MgUttv9QXERERERERabsUCoiIiIj8AOHh4QDk5edRXVNtdu2vrqkmLz+vRV9ERERERESk7VIoICIiIvIDhIWFGd8f/ibT7NrfvM0KBURERERERNo+hQIiIiIiP0B4eDhOTk4A5Oblml37L7XZycmpRcAhIiIiIiIibZNCAREREZEf6NLF9G+yDpvVEkLVNdV8k3W4RR9ERERERESkbVMoICIiIvIDzZo1C4Camhq27fjcbNq9bcfn1NTUtOiDiIiIiIiItG0KBURERER+oLCwMEJDQwFI/SqFc6XnWn2bz5WeI/WrFABCQ0M1U0BERERERKSdUCggIiIichPMmzfP+N4cZgs0b2PztouIiIiIiEjbplBARERE5CYICwsjIiICgP0H0zl8JLPVtvXwkUz2H0wHICIiQrMERERERERE2hGFAiIiIiI3SXR0NE5OTgB8tHE9hUWnWl0bC4tO8dHG9QA4OTkRHR2tAyciIiIiItKOKBQQERERuUm8vb1ZsGAB0FR0eP3G9VTXVLea9lXXVPNh/GqjuHBMTAze3t46cCIiIiIiIu2IQgERERGRm2jq1KnMnTsXgKKiQj6MX91q2vbuquWUlpUCMHfuXMLDw3XARERERERE2hmFAiIiIiI32bx584z6AnnHc1m+ctkdnTFQXVPN8pXLKCoqBJrqCKi4sIiIiIiISPtkUVtba7p0w8bGRiMiIiIichOUlZUxdepUMjObCg536+bOpIcn4d6t+21tR2HRKT6MX23MEAgMDCQuLg4XFxcdJBERERERkTbowoUL33m/ZgqIiIiI3AIuLi7ExcUZMwaKigp5d9VycvNyblsbDh/JbLFkUEREhAIBERERERGRdk4zBURERERusZiYGJYsWWLcDhkwkHtHjeEu17tuyeudKz3H9i+2kX5gn7Ft7ty5WjJIRERERESkHbjWTAGFAiIiIiK3QVxcHIsWLaKiosLYNmxoGPeOGoOdrd1NeY3qmmq2f7GNlC+TjW1OTk7ExMSoqLCIiIiIiEg7oVBAREREpJXIz88nNjaWhIQEY5utrS19+wTi6+NLQJ++1x0QVNdU882Rw+Tm5XL4SCY1NTXGfREREURHR+Pt7a3BFxERERERaScUCoiIiIi0MsnJycTExJCamnrZfT49fOnp2xMAX5+mr7YdOwJQU1sLYNQlyMnNIe947mX7CA0NZd68eYSFhWmwRURERERE2hmFAiIiIiKtVHJyMkuXLiU5ObnFskI3wsnJibCwMGbNmqUwQEREREREpB1TKCAiIiJiBhITE0lOTiYxMZETJ058r+d4enoSHh5OeHi4ggAREREREREBFAqIiIiImKWysjIyMjKAploEgFEbICgoCBcXFw2SiIiIiIiIXEahgIiIiIiIiIiIiIhIO3GtUMBSQyQiIiIiIiIiIiIi0j4oFBARERERERERERERaScUCoiIiIiIiIiIiIiItBMKBURERERERERERERE2gmFAiIiIiI3ICUlBQsLCywsLLC0tMTd3Z2f/OQnnD17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" + } + }, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Screenshot from 2023-08-31 13-12-54.png]()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Plotting with grapistry is done by the following simple command:\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "g.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Screenshot:" + ] + }, + { + "attachments": { + "Screenshot from 2023-08-31 18-48-56.png": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABpoAAAIkCAYAAAAOFfh4AAAAinpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjaVY7bDYVACET/qcIShsfCUo4xmtjBLf+yrol6PmAgZAbaf+dBy4BhZC26pzsKS0tZS3RMFGABj151cnflUlLjPZPKFJ49YM+hGT409e5HWIQ333yTcpdd9aqsoOtqmI1X8jHKFT6jv3sxyDuA/v3ZLRsB5QoSAAAKCGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNC40LjAtRXhpdjIiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjE2OTAiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSI1NDgiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMTY5MCIKICAgdGlmZjpJbWFnZUhlaWdodD0iNTQ4IgogICB0aWZmOk9yaWVudGF0aW9uPSIxIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg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wYV8gfn5BbZu3YLneXR0tLNlaJCBgX6SyQQAzWYTx3HIZNrItmcZHRlT1kVERERERK4CDpY5kyKc7aMeaWuhyCz5qRGytRlSVitriIiIiIispSsqNAHklnIcP3aCIAiIRqKEQh5BEDA/t8Dzzx3hq195mEwmTTabufhndPSMMi8iIiIiInIVWHSSJKIRSqktLROT16xgzz9Lj79I1DbUSSIiIiIia8gcOniHXfOTGMODb7uP7du3AnDkhRf51jcfU/ZFREREREQ2ufNuB3Pt+/B33w+YlogpXJqhcfTL7G+cUaFJRERERGSNOetxEmstX/3y1zkzdhaA9vasMi8iIiIiInIVSNsykdIMxrbOfrxufpy4rRKxTXWQiIiIiMga89brREEQ8OUv/SPbt29lampamRcREREREbkKJIMqNCpkZ55noeemDY/H9WvY2RF6/EUMVh0kIiIiIrLGnPU8WRAEnD49SqlUVuZFRERERESuiptKS6efY2FyBFMvbXg80fmTUMnTFui+U0RERERkfe4JRERERERERK6A8RvQKGFzkxsei50+QUdQwNFsJhERERGRdeEpBSIiIiIiIrISVcJMeFmq4QxOxzBBunfDYjFY+s98laXKEl1BTp0jIiIiIrJOVGgSERERERGRy2Ix5Jw4570u2tLtVLfcRhDv2dCYormzTM5MsaM5j0ugThIRERERWScqNImIiIiIiMhlmXHbGPe66RzYwWzvLVizsauyRyvzVEaeZMCfJ221N5OIiIiIyHpSoUlERERERETekADDtJOlGmmjbeggsx17ALOhMUVqi9ROP0pnbYYeX0vmiYiIiIisNxWaRERERERE5HVZDHNOmkIkg7PtTnLZnRseUzh/ntrok3RWp9gSzGGw6igRERERkXWmQpOIiIiIiIi8rpyTIBfKwPBd5DLDGx/Q9DHqZ79DT3OJgWBBRSYRERERkQ3iKAUiIiIiIiLyWup4jHsd2P7rKWS2t0ZQxsUBsraoIpOIiIiIyAZSoUlEREREREReVYDhnNdJNNNLqfdGNnpPpou6dpDtGuS820kTVx0lIiIiIrJBVGgSERERERGRV1UwcZacFNXBQwROC62+bhzy/YcohTMsOEl1lIiIiIjIBlGhSURERERERF6Rj8OUl6Gnbwu1WEfLxdcIJ2kf2M2sl6VhNKtJRERERGQjqNAk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tq8T63szOgaz6kKY2B/K2XW/JkVEREREZJ5QRZOIiIiIyCzb88A9k8crNl2qgMiMMwZc10wmekrjo2DtzPXfvg73gjdgAHN4Czz8XU2KiIiIiMg8oYomERERERGR01jrn30GB0NNJFh+wEDnd/9jRscQvPj3sQYMBu+3X9ekiIiIiIjMI6poEhEREREROY25lCuajKFczjTDgtf9JSZYTnL5d34avKImRURERERkHlGiSURERERkDrj1z94yeXzLZ29TQGRGuLEajONQFQ3wdJ7p0I++OHM3pA2LcJuXYjAweBjbd0CTIiIiIiIyz2jpPBERERERkdNUuLkdx3VxKFc02YnPMyVy019jKfdb/PG/akJEREREROYhVTSJiIiIiIictgwGCAddMAYHw/C2B2ek58hlb8cYgwOUfv01TYWIiIiIyDylRJOIiIiIyBxQKuQ5vPUxAAKhMIvWbVBQ5KRrufFPiQTLt4UGGN396Iz0G2g/g1DHRgzgde2gtP8xTYaIiIiIyDylpfNEREREREROU8ZAPOJiJ74+8P3/mpF+K65+N8aCMZbcz76giRARERERmcdU0SQiIiIiInIaar72FsIBB4ODg2F0zxNYr3TS+61+699P7gmVvu2jmggRERERkXlOiSYREREREZHTUPWa84lHAuU18wzs/dZnTnqfoaVn4cbrMAaKB5/CHx/WRIiIiIiIzHNKNImIiIiIzBF3fvoTk8fX/MUHFRA5yTeDBkz5ptDMQH+hxWcQv/oPMQZKh7aR/vmXNAkiIiIiIqcA7dEkIiIiIiJymlnz15+nIuLiANbAvu+d3H2SomdeQuVFvwvWkt12L+O//Y4mQURERETkFKFEk4iIiIiIyGkkWFmDcQzhwMQCFxYGtz54Uvusvvh38QGMUZJJREREROQUo6XzRERERERETiPRliXUxkLl5fIMbP74H57U/lr+9N8xxmAMDHz+vZoAEREREZFTjBJNIiIiIiJzyPZ77pw8XnvZNQqITLuWV12NccqJn/SRA1jfO2l9NbzhfTiBIMbAyHf/FesVNQEiIiIiIqcYJZpEREREROaQ/s59k8eNS5YrIDLt4gs7MNYAhkJqFKw9Kf00/s5fEW5dhsEw9L1PUejZr+CLiIiIiJyClGgSERERERE5TSy+6Y+pDDvggINlx//79EnrK7JgBcaAMZA/slfBFxERERE5RQUUAhERERERkdND3ZKVgMHB0v3Q3Selj3BTOy2/934Acv2H6f3mPyvwIiIiIiKnMCWaRERERETmkNTQwORxZV2DAiLTKpaoxxjAGga2PzLt7YebF9P25g+AteR6O+n95v9W0EVERERETnFaOk9EREREZA7p2b198rh1xVoFRKbNxg99kYADhvJydmOH9kx7Hwvf8gHKeSzDUSWZREREREROC6poEhEREREROcVVti4hFgpMVDPBtm/957S2H2lqZ9FbP4AFcv2H6L5NSSYRERERkdOFEk0iIiIiIiKnuPq15xIMuBggmxqhf/vD09Z2tGUJi976AbCQ6zlI923/pICLiIiIiJxGtHSeiIiIiIjIKW7RhdeWq5kMpI4cxPr+tLW9+G0fxFBu+7CSTCIiIiIipx0lmkRERERE5phb/+wtk8e3fPY2BUSmpOWCq4iEHIwp78/01Nf/bVrajTYvZs3ffAED5PsOsfuTf6Jgi4iIiIichrR0noiIiIiIyCls4foLcDBYYMu3PjctbVa0LGHp2z8EFjI9Bzj4P59QoEVERERETlOqaBIRERERETmF1S1ZARPVTD1PbZ6WNpe/80PlpfgcOKAkk4iIiIjIaU0VTSIiIiIiIqeoKz/+5fL+ScCT3/zslNuraFnMind8CAtkezvZ+9V/VJBFRERERE5zqmgSERERERE5BTWv3Ug4GARj6N/xGD1PPTjlNmtXb8Q4DgbD2K7HwFoFWkRERETkNKeKJhERERERkVPQxne8FwsYC4997d+m3N6GD/83WIsPHP7RFxne9qCCLCIiIiIiSjSJiIiIiIicapZc+NryHkoWDt7/sym1FWtdwpo/KC+Xt/97/4kFRnc+qiCLiIiIiAigRJOIiIiIiMisM8bgBIK4wRBuJEIgFMYEgziOizWA7+OXSniFHKVcHq+Yxy8Wsb7/ou21rj8fMBhj6dny8AmPq7JtKWtv+Vuw4GMZUYJJRERERESeR4kmERERERGR2eI4RKtridY3EI5X4wQDRKIxsj1HiCXqCVfEcIxDIZclMzpMuLmF3HiKUqlIMZMmNzhIZmQQv1h8TrN1S1ZiLIBhuHP3CQ1t04e/BK6LP7EN0yMf+wPNl4iIiIiIvIASTSIiIiIiIjMsGI0RbWgkWlNLNBqjZWkHdS0LqapvJByLEwyHcVwXMBPPsFjfp1Qskk+PMz4yxOhgH13bnyBbbCc3MkJ6oI98aoyb/vmr5acY+PH7335C41t3y4dw3AAWS6bnIFu/9DFNmoiIiIiIvCglmkRERERERGaIGwoTa2giWltHoqmFRSvW0rb6TELR2Cs802Acl2DYJRiOUJmop3nZSjo2bKL/4D56uvbT17kfnCC58TGiVbUc2foYvucd9xjX3/JB4gs7sAbGuw6y9Uv/oIkTEREREZGXpESTiIiIiIjIDAhX1VDTvpTqllZWrz+flhVrptymGwjS0rGalo7VJAf7GEmOs+Vn36b9rFfxyFc/ddztXfTRWzGmXMl0/z/8IdYraeJEREREROTl70taWxZ8VGEQEREREZk7fM+jcclyqptacdwAA4f2M9bXo8DMU8Z1ibcsoH75KpauXcdZl1xNTUvbtPcTrqikujbB4vXncXjrwxQLBQrjKaz/ylVNCza9hg3v/juMU16q78lb/5H86KAmT0REREREXpEqmkRERERERE4SJxCgqm0RVc2trD33QtrXbTip/Rkg7DqsOus8Ul2HcEIhRjv3U8pmXvI5jWs3suK6t5a/sIYnbv1Hkof3avJEREREROSYKNEkIiIiIiJyklQvWkptSysX3vxWKqoTM9ZvbcsCLv39P+ZXt/4HztIVDO3bhZfPveC8qz7xNbDgY7EW7v3oLfhaLk9ERERERI6DoxCIiIiIiIhML+O41LQvo7aplY2ved2MJpmeForGuPwP30N9UwuJJctxAs/9f4YXv/9TGAAD3Zvv5lcffoeSTCIiIiIictyUaBIREREREZlmFfUNVNQmOOvSq0m0Lpy1cYQiUTZe9waqa+upbl+KcV1WXft7XPO//4eKmnow0L/tUXb/5OuaNBEREREROSFaOk9ERERERGQahSrj1C5exroLLqFxacesj6eiqoZzb3gj9//k26y44neoW7YGa8vf+/kH3q4JExERERGRKVFFk4iIiIiIyDQxxlDVtoiGxhYWnblhzoyrqr6Jy972Z2SGe8iNDWMM3PvP79WEiYiIiIjIlKmiSUREREREZJpEEw1UNTSz5sIr59S4jDFEgy51za3suuubHH18syZLRERERESmhSqaREREREREpuPmKhikauEilq3fQLyuYU6OsTYexwkYAtGoJkxERERERKbnXkghEBERERERmbpwVQ3RqlqaF3fM2TE2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" + } + }, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Screenshot from 2023-08-31 18-48-56.png]()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can easily investigate which files Carl has access to." + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "direct_file_access_Carl = graphistry.cypher(\"\"\" MATCH (j:Person {name:\"Carl\"})-[r:HAS_ACCESS_TO]->(n)\n", + "RETURN *; \"\"\")\n", + "direct_file_access_Carl.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Screenshot:\n" + ] + }, + { + "attachments": { + "Screenshot from 2023-08-31 18-50-30.png": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABogAAAIeCAYAAACIr41mAAAAi3pUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjaVY7LDcQwCETvVJESho8Bl7OKHGk72PKD40jZvAMMCM1A4/c9aJswjKxFendHYd26fEokFgqwgGevuri7cimp8Z5JZQnvGbDn0Awvmnr6ERbhzXffpdxlqF6VFXRdTbP5Sn+MMuEr+r3nAfkPoBP6Ry0hbWvXWQAACghpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSIxNjcyIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iNTQyIgogICB0aWZmOkltYWdlV2lkdGg9IjE2NzIiCiAgIHRpZmY6SW1hZ2VIZWlnaHQ9IjU0MiIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIvPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg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AABQMAIRAABAnXpk20M18769+1KpVCwGAAC4bAIRAAAAAABAwQhEAAAAAAAABSMQAQAA1KnNd9xeM3/n209YCgAAcEUIRAAAAAAAAAUjEAEAANShP/vWN2rmp5542lIAAIArRiACAAAAAAAoGIEIAACgzrS3tyelUs1rg4NDFgMAAFwxAhEAAECd6enpTulDgejgwT6LAQAArhiBCAAAAAAAoGAEIgAAgDqzprenZt67Z5+lAAAAV5RABAAAUGfuf2BrzfzE409ZCgAAcEUJRAAAAAAAAAUjEAEAANSRR7Y9VDPv27svlUrFYgAAgCtKIAIAAAAAACgYgQgAAKCO9PauqZmf/ckOSwEAAK44gQgAAKCOdHR21Mx9ff2WAgAAXHECEQAAAAAAQMEIRAAAAHVi2/aHa+bnd7xgKQAAwKwQiAAAAOrEpk0bauaB/gFLAQAAZoVABAAAAAAAUDACEQAAQB3YuHFDSuXaS7SdO3dbDAAAMCsEIgAAAAAAgIIRiAAAAAAAAApGIAIAAKgDX3lsW838R1/9uqUAAACzRiACAAAAAAAoGIEIAABgjj2y7aGaed/efalUKhYDAADMGoEIAAAAAACgYAQiAACAOdbZ2VEz9x3stxQAAGBWCUQAAABzbE3vmpr52Wd3WAoAADCrBCIAAAAAAICCEYgAAADm0KZNG2vm/r4BSwEAAGadQAQAADCHtm1/qGZ+fscLlgIAAMw6gQgAAAAAAKBgBCIAAIA5snHThpTKtZdlu3btthgAAGDWCUQAAAAAAAAFIxABAADMkfsf2FozP/H4U5YCAAD8/+zdd3Bc15nn/e+9twMa3QiNDBAAQSKQYCaYxCDmIMmWZY3kcfZ6duzdnXf23dnZ2vDuW7W771RtbZjd2Z0Ze4LtsT2eHaexbFmSbYkUcxYp5gQwA0QOjdRAo9O97x+gIEKASDCn36eKVei+5557znMPUH359DnngVCCSERERERE5CEpK5s86vXJE6cUFBEREREReSCUIBIREREREXkIvvyVL456ferkKWzbVmBEREREROSBUIJIRERERERERERERETkKaMEkYiIiIiIyEMwv2beqNff/97fKSgiIiIiIvLAKEEkIiIiIiIiIiIiIiLylFGCSERERERE5AH7k//9x6Ne/58f/FBBERERERGRB0oJIhERERERERERERERkaeM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" + } + }, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Screenshot from 2023-08-31 18-50-30.png]()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Carl has direct access to a file. But, Since Team nodes have access to specific folders, if Carl is a part of a team, he indirectly has access to all files in that folder. With the next query we can see how a depth-first search is performed from a node with the label Person with the name Carl to the node with the label File. It finds a path from Carl to a file directly or through other nodes. The symbol * represents depth-first search and the number 3 is a maximum depth (maximum number of jumps)." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all_file_access_Carl = graphistry.cypher(\"\"\"\n", + "MATCH p=(:Person {name:\"Carl\"})-[* ..3]->(:File)\n", + "RETURN p;\n", + " \"\"\")\n", + "all_file_access_Carl.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Screenshot:" + ] + }, + { + "attachments": { + "Screenshot from 2023-08-31 18-51-35.png": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABo4AAAIjCAYAAAA9VelSAAAAi3pUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjaVY7LDcQwCETvVJESho8Bl7OKHGk72PKD40jZvAMMCM1A4/c9aJswjKxFendHYd26fEokFgqwgGevuri7cimp8Z5JZQnvGbDn0Awvmnr6ERbhzXffpdxlqF6VFXRdTbP5Sn+McsBX9HsvCvkPoBP+QC0dO+foBQAACghpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSIxNjc4IgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iNTQ3IgogICB0aWZmOkltYWdlV2lkdGg9IjE2NzgiCiAgIHRpZmY6SW1hZ2VIZWlnaHQ9IjU0NyIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIvPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+71K/HQAAAARzQklUCAgICHwIZIgAACAASURBVHja7N1nkCTnfef5b2aWt13tq81Mu/EOY+AdQYAESRAgCXJPS3FXuqDEVdy9uLuQxBf34hQMbsRt3FGnuFVcrJZBinGUdNwVJUiisKRIAgNHeMxgvDftvSvvM/Ne9LDJIWaA8V3T8/tETExXVZqn/k9WZ3b98/88xu6d97mIiIiIiIiIiIiIiIjIHc9UCERERERERERERERERASUOBIREREREREREREREZELlDgSERERERERERERERERQIkjERERERERERERERERuUCJIxEREREREREREREREQGUOBIREREREREREREREZELlDgSERERERERERERERERQIkjERERERERERERERERuUCJIxEREREREREREREREQGUOBIREREREREREREREZELlDgSERERERERERERERERQIkjERERERERERERERERuUCJIxEREREREREREREREQGUOBIREREREREREREREZELlDgSERERERERERERERERQIkjERERERERERERERERuUCJIxEREREREREREREREQHAc70bME2TtvZWYrEotm2zML/IwsKiIisiIiIiIiIiIiIiInKbuebEkWEYbN+xhbvu2o4/4L/otfn5Bd55ex9jo+OKsIiIiIiIiIiIiIiIyG3C2L3zPvdqVzJNkyc/9Tjda7ouu4zrurz95rscOXJcURYREREREREREREREbkNXFPF0b337VlOGo2PT7DvvQPMzszh8XhYs7aLe+/dQzgS5r4H7mExlVblkYiIiIiIiIiIiIiIyG3gqhNHsViUrds2AzB4fogXX3gF13XZtn0Lpmly6OARJiem+PyzTxMOh7j3vj1KHImIiIiIiIiIiIiIiNwGzKtdoX+gD8MwsG2b13/xFq67NNJdLB6joSEOQD5f4J233wOgqamRxsaEIi0iIiIiIiIiIiIiIlLnrrriqLm5EYDpqRmKxRLrNwxgWRaNiQZM02TT5g04tsP584O/WqeliYWFRUVbRERERERERERERESkjl114sjj9QJQrlQAWL9+AI/XQzQSAcNgw8Z12LbN6dNncRwH0zTxXlhHRERERERERERERERE6tdVJ44KhQLA8rB0/+35nwLw4MP347EsXn3ldQCi0SimaV5Yp6hIi4iIiIiIiIiIiIiI1LmrnuNofGwCgESigc7Ojl+94LrL8x0BbNu+GQDHcZianFKkRURERERERERERERE6txVJ44Gzw+Ry+UB+PgTj9DUtDTn0fv7D/Lee+8DsGnzBrZs3QTA6VNnKRZLirSIiIiIiIiIiIiIiEidu+qh6mzb4dVXXuczT32SYDDIF774NOfODjI3O4fH62HNmm7a2luXlw8GA5imieM4iraIiIiIiIiIiIiIiEgdM3bvvM+9lhXX9qzh448/gtfrveTrtVoNj2cpLzU4OMyLP3/5oqHsREREREREREREREREpL5YHcmub1zLiulUmlMnT+M4DgF/AK/Xg+M4zM8tcOTwMfa++CoNDTESiYblf4ODw4q4iIiIiGFg+nx4A0G84TDeUARvIITH78e4cOONq2ptEREREREREVkB11xxdEUbNwye+ORj9PauBeDY0RO88frbirqIiIjccSyvD08oRCDWgC8awx+JguPgCwSxLiSL7FqNarGIa5qUc5mlf5k01UIep1pVEEVERERERETkpvPczI27rsveF17hE598jLU9a2hsTCjiIiIickcxPR4ibZ2E25J4fT6scpl19z1C+8BGgrE4BsZvXkBRSKeYPHuc02+9htO1FrtcJjc1QXZmErdWU1BFRERERERE5Ka5qRVHv2SaJr29a5mamiafLyjqIiIisupZXi+h1iThllaikTibHvo4Dc1tS8kiw7yibbiuSyG9wMzgWc4dfJdcIUdhdobc1ASOrQSSiIiIiIiIiNx4tyRxJCIiInIn8cfiNHT3Em1uoX/LTtZu34NpWde1TcexOb//bYZOHyU9M0VmdIhyNqNgi4iIiIiIiMgNpcSRiIiIyA0Uam0j3t1Le1sH937hKzd8+45j89YPv89cap708HkKczMKuoiIiIiIiIjcMFZHsusbCoOIiIjI9TEMiHWtobFnHTvuf4xNDz2OYZo3YT8m3Zu3E/D5yZdL2I5NNZdVB4iIiIiIiIjIDeFRCERERESuk2EQ7eiiuXc92x54lOTA5pu7O9OiZ8fd+IJhjni84LrkJsfVDyIiIiIiIiJy3ZQ4EhEREblOwcYmYp1r2Xb/zU8a/bqO9Ztx7BoHq2Xsconiwrw6Q0RE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" + } + }, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Screenshot from 2023-08-31 18-51-35.png]()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This can also be done for all Person nodes with executing the following query. This is an example why graph databases are great for Identity and Access Management." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "all_file_access = graphistry.cypher(\"\"\"\n", + "MATCH p=(:Person)-[* ..3]->(:File)\n", + "RETURN p;\n", + " \"\"\")\n", + "all_file_access.plot()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Screenshot:" + ] + }, + { + "attachments": { + "Screenshot from 2023-08-31 18-52-42.png": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABpsAAAIbCAYAAAAU+yY+AAAAi3pUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjaVY7dDYRACITfqcIShp+FpZzTaHIdXPmyronn9wADITPQ/vsetAwYRtaie7qjsLSUT4mOiQIs4NGrTu6uXEpqvGdSmcKzB+w5NMOLpt79CIvw5ptvUu6yq16VFXRdDbPxSj5GucJn9HvPK+Q/gE4A3y1JMryusAAACghpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSIxNjkxIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iNTM5IgogICB0aWZmOkltYWdlV2lkdGg9IjE2OTEiCiAgIHRpZmY6SW1hZ2VIZWlnaHQ9IjUzOSIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIvPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg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K5lOyMUxXRjLH8YZ7uBMy9vUZHpJ4yLjKZhTfc968vAYkMPERMEmERERERERkRtFwSYRERGR28SoGaDNXYF34d2kihquqaxjWFDcAKZF14W9+NJRTHdgTvXftrxgmOQwcGs5iIiIiIiIiNwwpoZARERE5PZwwaqE+juuOdA0SWEdzNtI0vLjmNac6r9jWjiGgYOhxSAiIiIiIiJyAynYJCIiInIbaLEqcAproLzxw1dW3ACFNaRyzhwbBQfDQaEmERERERERkRtMwSYRERGROS6HScQMQMXS61OhYcKCu7HNuXXisplLYzpZ3dckIiIiIiIicoPpziYRERGROS5u+Mi6A1BQff0qtdzg9s+pcTAzY6QcS8EmERERERERkRv9/+QaAhEREZG5rdtVjFWz6jrXaoBhwBwK3Pjj/XjsNJbjaFGIiIiIiIiI3EAKNomIiIjMcXG85PyF179ifxFkknNkFBxCkXb8ThIDBZtEREREREREbiQFm0RERETmMOe9HUjGLHys8wZhbHROjIMrm6QzniPoJLUoRERERERERG4wBZtERERE5rBZ3cNjANHeOTEO3kQYIx0n4KS1KERERERERERuMAWbREREROb0hzkHcMCZhbuVcjmIDc2JcciN9hF0kridnBaFiIiIiIiIyA2mYJOIiIjIHOdzMrjT0etfcX8zxPrJH7lwS/ffyqUoCJ+m1B7FxNaCEBEREREREbnBFGwSERERmeOqs0Nkuk9d51odjEgfZi5NtucEZu5WPZ7OoXjgKJFUmkI7rsUgIiIiIiIichMo2CQiIiIyx+U7Y1iJYayxwetXaS6DMzZMnR0mExvBF+26Jftu5VJEBjopyUUuHikoIiIiIiIiIjeagk0iIiIic5yFTcBJQ8913N3U+jZ5mQildpTS3AhFHXuxcolbru8lPe/iSgxRake1EERERERERERuEgWbRERERG4DDbk+GGqD8Ie/X8kY7cIIt1KXGwCgPDfKcCKJp/voLdXnvNFWBvq6qM4OYZHTIhARERERERG5SRRsEhEREbkNeJws9dl+jJa38UU6Z17R2BCc30tVbhi/M35Pk4scNdkhPH2nKBw6DbfAcXWeVASn4zDlmTAhJ6EFICIiIiIiInITKdgkIiIicpsotqPUZvtINr8JIx3XXN7IjsHJVyhND1BlD09Ky3fGyM+MEGk5RCDW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0idy3dkkIiIiIiIic5upIRAREREREREREREREZGZUrBJREREREREREREREREZkzBJhEREREREREREREREZkxl4bg5ikpLWHhggXU1tWQn5eHPxAgm80yNDTEmeaznDhxEtu2P3QZERERERERERERERGR2WJsWHeno2G4OR57/BM0NMy7Ynp3dzcvPPcLMtnMhyojIiIiN5eDA45+GUSu9IncxMDQOIiIiIiIiMicpWP0bgF9vX0cPHiIfXubuHD+wsTz6upqNmxcf93KiIiIyM1hXPKnyFSrQ0RERERERGQuu+7H6FVVV/IbX3hqWnm/953vMzg4xKbN67ln212T0mzbJpVMEw4P0dLSzrEjJ0gkklPWEwwGWLN2JfPm11NcVIjb4yaZTBKNxDh/roXjx04Ri8UnlXnqM4/TML/+A9t4pvk8Lzz30vv9q6pg3YbVVFVXEgoFMU2TVCpFeHCIM83nOHzoOI4zvc1iF85fYM/uvQwPD096vmXL3axbvxaAhQsX0LTv7Q9VRkRERG42Yzym4PDeHyIT60K7mkRERERERGSuu6l3NrW2XyAUKJwyzTRN/AEftYFqauuq2XDHGn7+/Et0tHdPyrdwUQOPfOJBvF7PpOfBYIBgMEBlVTnr1q/i2Z/+nN6egWtuYywenfh62fJGHvnEAxjG+A8EkskUdi5HIBigtm68nQVF+by5Y8+06j558tSUz9vb2yYCRz6v90OXERERkZvPAJz3/9CAaEUo0CQiIiIiIiK3jVkNNr3x+lvsfLPpiunBYMFlz771ze/Q3TlANpslEHSzcfNG7r//PgIBP5966jG++Q/fJZkYv4+orLyUx574OC6XhW077NjxOvvfPcjoaIyCghCbNt/B9u33EQgG+OSnH+Nv//ff4/UGLnvnf/uTP8Pn803ZRrfbjfdi8Gbb9i0YhkFkNMJXv/p1/N48AKKxYb70ld+irq6WDRvW8torO3BZvhmPWyiUN/H1aCQya2VERETkRjIwMHAMBZrk/fUgIiIiIiIicjuY1WBTNpslFApdU5lUKj2pzJGDJxkZGuXpX/skbrebjz2wjV+88BoAd2/ZiMtlAfDv//Z9+nuH8bgClJWMB5QOHzhJLDrG+vVrOHHyFLGx6JTBpmAwiN/vv2q73B43weB42TNnzk4EmgDyQkW89spOCovy6OrqJBZNUlg4s2CTZVmsW7d24vvm02dmpYyIiIjcHOMBBgUZREREREREROT2Yc6FRra1dtHb2w9A45JGotEohmHQMH8eAAMDg/T1DE1Z9tyZVn74g+c4cfQMJUUVM25DJp1hbCwBwOo1q6isLsHlej9WNxQe5sK5dlIJm8LCwhm/595776G4pPhivwY4ceLkrJQRERERERERERERERG5HmZ1Z5PL5SIWi1FSUnJZWi6XI5vNTruu7s4eKivLMU0Df9CF1+ue2NV0/vyFiXuUZiIej+P2uAgGLt+FlUqlJr7eu/sdHnhoGx6Ph89/4XNkszn6+wbo7emjo6ObttYOMpnMjNtxxx0bWLFy+XibYnF++YuXsG37upcRERERERERERERERG5XmY12LT9/m1sv3/blGmHDx1nx6tvTbuuVCo98bXX68W4ZE9W4uKOo/cYhsEf/vHvXlZHR3snP/3xzy97/ud/+adXfO/Xv/ZtEokkAEcOHyccHmLd+pUsXLQQl8uiuqaS6ppK1t+xhmQyxd7db3Po4LFrHquVq1Zw512bx/uTSPDccy8QiUavexkREREREREREREREZHryXWzXhyJjFxTfn/Af0nZCAH/+7t3gqHgpLyGYUzserpUKpW85nZ2dXVTXFw88X1nRzedHd0kEglC+V4aGhpYtmwJjUsa8fm83P/AvXR2dTLQNzztdzQuWcy2bfcC40GjZ599jqGhoeteRkRERERERERERERE5Hqb1WDTq6/s4Ojh09elrvp5tQCk0xksw4edM0inM3g8bhoXL+Kt1/dhmuPbnWzb5q//8u8nyj792ceZN6/+inX/jz/7K/x+/5RplwaaLuX3+8ll4PzZds6fbScQ2sl//r0vYxiwctVy3ujbM61+Ncxv4IEHPoZhGCQTCX727PMMhYeuexkREREREREREREREZHZYM6FRq5as5zi4kIADh86gs/nw7Iszp9rAaCwqJCGBdVXLB8IBD90G5YuW8yTn3qU3/7y5yktvTwAlYinJ+5rysvLm1adtbU1fPzjD2GaJslEgmeffZ5wOHzdy4iIiIiIiIiIiIiIiMyWWd3Z5HK5iMVilJSUTJmey2XJZnOTnnm9HsIDA5SWlhAMBlm2opGNm9YDkEgk+dnPnqekqAKApr3vsnDRfDweN09/5in27mmitaWTeCyOx+uhoqKMVauXU1Y2/v73gkG/Kh6P4/a4CAZCU6anUimy2RyLFs8H4NHHH+Tll3YQGYmRyWbJzwuxbsMaPB43AC0XWj5wbEzT5NHHHsXlGp+CZCrF3VvunnKMXvzFL2dcRkREREREREREREREZDbNarBp+/3b2H7/tiumv/v2QXa+tW/Ss6/8zn+aMm8kEuVb//jtiUATwNDQCM89+yKfePwhAgE/W7beyZatU7/ryOFj/ORHL1BQUHBZ2p//5Z9etR9/89ff4NzZCxw8cJT1G1ZTVlbKF7742Snzdnf3sHf3/g/c3WSaBh63e+L7wsJCCgsLL8t3aYBsJmVERERERERERERERERmk+tmvnxkdPiq6WPxMQYHwxw6dJj97xydMrDS3tbJP33zX1jcOJ+ly5ZQUVGO1+slMTbGYHiIs2fP8nbTu/i9eVMGmqYjFovj9/t5Y8cuTp08zbIVjTTMm0d+QT6maTIWj9Pd08uRw0c429w27WP0RERERERERERERERE5jpjw7o7HQ2DiIiIiIiIiIiIiIiIzISpIRAREREREREREREREZGZUrBJREREREREREREREREZkzBJhEREREREREREREREZkxBZtERERERERERERERERkxhRsEhERERERERERERERkRlTsElERERERERERERERERmTMEmERERERERERERERERmTEFm0RERERERERERERERGTGFGwSERERERERERERERGRGVOwSURERERERERERERERGZMwSYRERERERERERERERGZMQWbREREREREREREREREZMYUbBIREREREREREREREZEZc12viuobKolER8hms9PKHwgEMXDR1xPWLIiIiIiIiIiIiIiIiMxR1yXYVN9QCYbNgw8+hN/vn1aZnp5u9u3bR15+gGhkTDMhIiIiIiIiIiIiIiIyB12XYFMkOsKDDz5EfX3dtMuUlZXS19fPQP+ggk0iIiIiIiIiIiIiIiJz1HW5symbzU57R9OlvF4vjuNoFkREREREREREREREROYoU0MgIiIiIiIiIiIiIiIiM6Vgk4iIiIiIiIiIiIiIiMyYgk0iIiIiIiIiIiIiIiIyY65buXHl5aV88bc+y9/+738gm81NPC8sKuBLX/kCX//at0kkkmzbvoW8UIifv/DyRJn38tu2zeBAmKNHTnDi+OmJOp781KMsXDR/yjujvvUP3yMWi0/ZJsuy2LJ1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" + } + }, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "![Screenshot from 2023-08-31 18-52-42.png]()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Takeaway and further reading" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "Pygraphistry complements Memgraph by providing a specialized tool for creating rich and interactive visualizations of graph data stored in Memgraph. It allows users to gain deeper insights into their graph data by leveraging the advanced visualization capabilities of the Graphistry platform, especially when dealing with complex and extensive graph data sets. \n", + "\n", + "Feel free to get your hands on Graphistry and Memgraph and share your insights or questions with us on [Discord](https://discord.com/invite/memgraph) !\n", + "\n", + "You can find out more about building and scaling modern IAM systems with Memgraph [here](https://memgraph.com/identity-access-management?utm_source=memgraph&utm_medium=referral&utm_campaign=bfb_blog&utm_content=iam) and on blogposts [What Makes Memgraph Great for Real-Time Performance in IAM Systems](https://memgraph.com/blog/what-makes-memgraph-great-for-real-time-performance-in-iam-systems), [Benefits Graph Databases Bring to Identity and Access Management](https://memgraph.com/blog/benefits-graph-databases-bring-to-identity-and-access-management) and [How Graphs Solve Two Biggest Problems of Traditional IAM Systems](https://memgraph.com/blog/how-graphs-solves-two-biggest-problems-of-traditional-iam-systems).\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "vsc", + "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.9.16" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 568c83d0abdd1a006c364ab24bacdbb63f9246ad Mon Sep 17 00:00:00 2001 From: karmenrabar Date: Thu, 14 Sep 2023 11:26:48 +0200 Subject: [PATCH 002/104] Changed user/pass and updated screenshots --- .../memgraph/visualizing_iam_dataset.ipynb | 38 +++---------------- 1 file changed, 6 insertions(+), 32 deletions(-) diff --git a/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb b/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb index c552e82bee..12e5062121 100644 --- a/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb +++ b/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb @@ -1,7 +1,6 @@ { "cells": [ { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -118,7 +117,7 @@ "source": [ "# To specify Graphistry account & server, use:\n", "# graphistry.register(api=3, username='...', password='...', protocol='https', server='hub.graphistry.com')\n", - "graphistry.register(api=3, username='k', password='123Rocky') " + "# graphistry.register(..., personal_key_id='pkey_id', personal_key_secret='pkey_secret') # Key instead of username+password+org_name" ] }, { @@ -283,15 +282,10 @@ ] }, { - "attachments": { - "Screenshot from 2023-08-31 13-12-54.png": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABgUAAAJ9CAYAAAAVNHIjAAAAi3pUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjaVY7dDYRACITfqcIShp+FpZyLp4kdXPnHuibq9wADITPQ9jt2WgYMI2vRPd1RWFrKp0THRAEW8OhVJ1dXLiU1XjOpTOHZA3YfmuFFU+++h0V489VXKXfZVM/KCjqvhtl4JR/JDT6j3/v4Qp4B9Af43S0bt0tsygAACghpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSIxNTQxIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iNjM3IgogICB0aWZmOkltYWdlV2lkdGg9IjE1NDEiCiAgIHRpZmY6SW1hZ2VIZWlnaHQ9IjYzNyIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIvPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg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AB7H6XRqxYoVamtrU2BgoGbOnKlp06Zp27ZtamhoUHZ2tt577z2NGTNGI0aMUGNjo/72t7+ppaVFK1eu1Jo1azRp0qQf9N6vvvqq6urqtGXLFr366qsaP368JOngwYMyGo06evSoVq9ereLiYtdogNOJiYlRYmKiFi5cqAkTJnhE/5pMJl177bUaO3as9u3bpyVLlnzvyAoAAAB4D0IBAACAExwvISJJbS3td6TAiW0/cZsAokXpqwAAIABJREFUT1JVVaXbb79dZWVlkqS5c+dq5MiRuv766xUZGan58+fr3XffVZ8+fWQymbRy5Uq9/fbbslgsmj17tnJyctSrV68f/P4DBgzQiBEjNH36dNeIgGeffVb//e9/FR0drX/84x9KT093TTx+JhkZGbJYLEpLS/Oofu7UqZMmTJigq666Sh988IFWrVql+vp6DkAAAAAvZWhqanIev3NizU0AAABvVFhYqMzMTElS/ZHX1daS3C63w8dvvcwdb5UkLVmy5JLVNwc8UXR0tF577TUNHz78vK73hRde0KeffqoXXnjBY7e9vr5e69at04EDB5SQkKABAwYwrwgAAMBlprm5+bSP8+0PAADgBMevWJYkpzO43W7HiW0/cZsAnH8Oh0NlZWWaP3++fvnLX3p0W81ms0aNGqUbbrhBW7du1dKlS/XNN9+wEwEAALwIoQAAAAAA/AgbN25Uv379NGbMGI8rHfR9YmNjlZmZqa5du+q9995Tfn6+Ghsb2ZkAAABewJcuAAAA+D8xMTGuvw2G2na7HSe2/cRtAiCVl5ef1/UNGTKkXdbo9/X1VXJysvr27av8/HwtWbJEKSkp6tu3LwcJAADAZYyRAgAAAADgxcLCwjRu3DilpKSoqKhI7777rqqrq+kYAACAyxShAAAAwAni4uJcf/v4FbXb7Tix7SduEwB8n759+yozM1NBQUHKycnR+vXr1draSscAAABcZggFAAAAThAaGnrZbdORI0dUV1fHzgVwRiaTSSNGjNCYMWO0Z88eLVmyRPv376djAAAALiPMKQAAAPAtp9OpgwcPKiIiQpWVlfL1L1Jzw73t80ue/7GRAlarVVu3bpXNZpPRaJTValVERISsVqusVquCg4PZ8QBOEhUVpYyMDG3evFl5eXmKjo7WsGHDZDab6RwAAIB2ztDU1OQ8fsff358eAQAAXqWh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3G2eAovFQvkweNT/q0pKSlRcXKzIyEilpaUpJCSEjgEAAF7zHf50GCkAAADOmsPh0MqVK+Xj46Mbb7zRq2qPp6amfu9J97q6Oi1YsEAhISHq1q2bSkpKdPvtt2vVqlUaOHCgbrnlFj355JP605/+5PqCtm3bNu3bt0+LFy+Wv7+//vznP6uiokLPPvusevfurc2bN2vMmDGaPXu21qxZo1WrVunGG2/Utm3bFBMTI0nKy8tTfn6+KioqlJKSoilTpmjQoEEcqDiv/P391aVLF3Xp0sW1rKWlxRUUHDx4UFu3blVNTY3CwsLcSg9ZLBb5+vKTAxef0WjUoEGD1KtXL61bt07Z2dmKj49XfHw8o1wAAADflegCAABwNk4MBNLT071uMtKOHTvquuuuc91vamqSwWCQwWDQFVdcoWXLlum9995TYGCgXnnlFd18881KTU2V2WzWH/7wB7377ruu1959993q3Lmzdu/eLX9/f9XV1enIkSPq2rWrnnzySVksFuXk5KhDhw6aOXOmAgICNG7cOF1//fV68803Xet56KGHZLVaNWjQIPXr10+7du3iQMVF4efnp6ioKA0cOFDXX3+9Jk6cKKPRqCeeeEJWq1U2m02LFy9W//791aFDB4WFhWny5Mn68ssv1dLS8r3rNZlMrs+VyWTSoEGDlJOT4/acN998U/7+/qqsrHRb/vTTT7te6+fnp+7du2vOnDlyOp0KCgpyPXbi7Uwjnex2u6ZOnSqr1arQ0FCNGzdO+/btO+V7nniz2WwcJB7CbDZr1KhRGjlypL744gstXbpU33zzDR0DAAC8GqEAAAA4I4fDoeXLl3ttIHAqAQEBcjqdcjqdqq2t1UcffaRrrrlGklReXq5XX31VJpNJJpNJI0eOPOVJqFtuuUXvvvuufvGLX+j555/XY489JpPJpHvuuUfR0dHq3r272/O7deum/fv3u+6fuB98fHzU2trKwYpL98PCaJSfn5/69++vpKQkPfvss7r77rv1xRdf6O2339bu3bv1m9/8Ri+//LLeeustrVmzRp9//rkqKirchjd/9NFHamlp0eHDhzV37lxNmTJFxcXFrscXLVqkoKAgvfbaaye1YeTIkWppaVFdXZ3eeustPf/881q8eLHq6urkdDpdQcLXX38tp9PpVgLsu5xOp8aOHauDBw+qoKBAn332maKjozV8+HA1NDS4npeenu76t+D4zWq1ckB4mNjYWGVmZqpr16567733lJ+ff9r9DwAAcFl/d6cLAADA6bS1tWn58uXy8/MjEDhLXbp00fTp09XY2Oi6fd+Vw9u3b9cDDzygFStWaNasWXr88ce1fv16xcbGugUAkrRnzx517tyZDobH27t3rw4fPqzf/va36tq1q0aMGKEFCxaoW7duuuOOO3TDDTcoKipKNTU1+vTTT/XKK6/ozTfflMPh0FdffaWDBw/KaDRq3LhxSkxM1Pr16yVJZWVl+uijj5SVlaVFixad9L4Gg0G+vr4KCAhQcnKyRo8e7RYonIuCggJt2bJFb7zxhuLi4tSjRw/9/e9/l9ls1ltvvcVObod8fX2VnJysCRMmqLq6WkuWLNGOHTvoGAAA4HUIBQAAwPdqa2vT+++/TyAgqbq6Wvn5+Wf13MmTJ2vx4sX65JNP1NraquXLl+vpp58+5XPvvfdePfzww2pra1NYWJgCAgLU3NysESNGqLGxUX/729/U0tKilStXas2aNZo0aRIHJjxenz59FB8fr8mTJ+vzzz+XJA0ePFgvvfSSjEajrFarrrzySg0bNkzjx4/XHXfc4fo35ujRoyouLtYrr7yiBx98UBs2bFCHDh20f/9+vfDCCxo5cqQmTZqkvXv3auPGjad8f6fTqU2bNmnVqlW6+uqrf9A2rF+/XklJSQoNDXUtMxgMSktLU1FRETu5HQsLC9O4ceOUkpKioqIivfvuu6qurqZjAACA1yAUAAAAp3Q8EAgICGCEgKSPP/5Y06dPP6vnJiQkaMGCBbrzzjsVGhqqOXPm6Kc//ekpn/vWW29px44dGjp0qP74xz/qnnvu0bBhw2QymbRy5Uq9/fbbslgsmj17tnJyctSrVy8OTng8X19fFRQUaNCgQbrpppvUt29fzZ8/X21tbaf+UWI0Kjw8XAaDQdOmTdNNN92k6dOna9myZfp//+//qV+/fvrss8/073//W9HR0Vq7dq2uueYa/eUvf1FdXZ1rPatXr5bJZFJAQIASExP18MMPa/LkyT9oG44cOaLIyMiTlkdERLidQM7NzXWbT+D7AkB4nr59+yrz/7N35/FNlXn//1/pRpekCV0otAUKFLCMshWEouxKUUBkL4ML6K0y3qIijI6jXxV01Jlxit4u37lVpIoDyo6AQEEtuJR9GygCpYBlLQ2mTbqnye8Pfs3XDKsLpSnv5+ORB8l1kpNzPuekJNfnXNdnzBgsFgsLFixg48aNmoZNRERErgmGiooKd82DoKAgRUREREQ8V7crIVB7cnJy2L17N6NHj1YwxCdlZGTw1ltvnffq/W+//ZbHHnuMjh078v77719wHcHBwWRlZdG9e/dzlmVlZTFo0CByc3MpKSlh6dKlPP/887z++usEBQXx5Zdfsm/fPubMmYPRaKR9+/a89957DB482LOOwsJCoqOjOXHixCWn4vrrX//Kl19+yerVq73aH374YZxOJ++++y6vvvoqWVlZrFq1SieAjzt16hTr1q2jqqqKnj170qxZMwVFREREfNZPa3adj0YKiIiIiBen08myZcuUEKhlirPUJ4sXL+a5557zPL7pppv429/+xvz583/xOmfNmkVpaSmxsbG0bt2aqVOnUlJSgtFoZNiwYcTExABn63SsXLmS3r1788QTT7BhwwZyc3MpKir6We/XvXt3Nm3ahN1u97S53W7Wr19Pt27ddJDrmZiYGEaOHMn111/P2rVrWbVqFSUlJQqMiIiI1EtKCoiIiIiX1atXExISooTAVeB2uxUEqRdatWpFeno6s2fPxuFwcPr0aT766CM6duz4i9Znt9tZuHAhmZmZuN1uz23SpEnMmjULk8lEw4YNMZvN3H777dxzzz38z//8D4WFhWzatInc3FyWLVvGnDlzANi6dSsHDhzAZrNd8HPXq1cv2rdvz7hx49i/fz/5+fk8/vjjOBwO1faorz+O/fzo0KEDY8aMAeDTTz9l586duFwuBUdERETqlQCFQERERGps3ryZiooK7rzzTiUEapnBYFBSQOqN9u3bs2LFCv7P//k/PPTQQ4SEhNCnTx8+/vjjX7S+efPmER8fzy233OLVPmnSJNq1a0d+fv45r2nUqBFTp05lzpw5nsLAP/zwA4899hgBAQHk5eWxadMmKioqiIyMJDo6mqioKKKiorBYLPj5+fHZZ58xefJkUlJSqKqqok+fPnz11VeEhYXpINdjYWFhDBw4kB9++IGvv/6affv20bt3b89oFBERERGf//2pmgIiIiICZzvLvvrqK0aNGkVoaKgCUsv27dvHtm3bfnFRVBFfkZ6e7jW1UI1t27bRpk2bWt+eoUOHsmbNGgDPFeHh4eH87//+rydJEB0dTUREBH5+Gmh9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Ef2fgdVqJS8vj+3bt5OVlcXSpUv56KOP+Pjjj1m/fj2HDx/G5XIRHx9Pjx49GDt2LOPHj2fYsGH069eP5ORkEhMTiYqKUkKgjjly5AiLFy8mLCzMKyFQw2w2k56ezuOPPw6A3W1nYul4ppU/w3HXsSu2Xcddx5hW/gxTyx71JAQef/xx0tPTlRAQERGpQzRSQERExEeUlZUBXPMjBY4dO0bv3r158skn+fTTT7Hb7dx333089thjng7zAQMGsGLFCqqrq9mxYwfDhg2jWbNmTJgwAYBXXnmFP/3pTxd8jw8//BCz2cysWbO48847AVi+fLln+fXXX8+zzz5LWlqa12vat2/P9u3bzzt/ds02Wa1W5s+fz6233sr69esvWYw4NTWVVatW4XQ6Wb9+PYMGDaJbt2785S9/oW3btmzfvp1OnToBMG3aNO6++26uv/56fWBErgEul+u8V/sXFRVRXl5OeHg4ZrMZi8VCTEwMbdu2xWw2ExoaquD5sG3btrFlyxY6dAS3HtUAACAASURBVOjAjTfeiMFguOBzn3jiCeLj45k2bRp2u53lVUtYXrWEsYF380DQw79ZrQG7q5j3Kt9hbtVsT5vJZCI9PV1FhUVEROogJQVERER8RHl5OcA135nzf//v/6VLly48/fTTAERERJCens6tt95KVVUVAAaDgYCAAAICAujWrRu33XYbW7du9SQFLiUjI4M33niDBx98kFOnTl2yuLPNZqOgoICGDRtesKBmzTbFxMTwyCOPkJ+fzwsvvMCiRYsu70tbQAD9+vUjISGB3bt3M3bsWB555BGmTZvGkiVL2LNnDytWrOD777/Xh0WkHnG73djt9vMW+C0tLcVoNBIeHo7FYiEyMpKWLVtiNpsxGo0X7SwW31OTHD58+DD9+vUjMTHxsl43evRoUlJSmDFjBgsWLABgbtVsljuX0DugP8l+Xekd0O9nJwjsrmLWOb9kq2sz65xfeEYGwNnpgiZPnkzTpk114EREROogJQVERER8RFlZGQEBARfsdL5WbNu2jZ49e3q1dejQgYKCgnOe63a72b59O6tWreK11167rPV//fXX/Pjjj4wdO5bZs2cze/Zspk6detHXrFu3joYNG3qmMLocAwcOZObMmZf9fJfLxcqVKzl16hTdunUD4M9//jOJiYls376dV155hUcffZT4+Hh9WER8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bGxs1FugmXNxcWH06NH07t2b9evXk5iYqLJRzURFRQUrV67k4MGDjBgxAl9fXwVFGqXc3Fzz7y2NTfc8Ljz2C89JRERE5FKUFBAREZFGx2Aw0Lt3b3bt2kVtba0C0syvdUBAAOPGjaOyspK4uDiysrIUmCasqKiIZcuWUVNTw0MPPUTr1q0VFBERERGRRkRJAREREWmUunXrhtFoVG3kO0TLli158MEHCQoKYsuWLaxZs4by8nIFponZv38/33zzDW3btmXkyJE4ODgoKNKoeXl5mX8vrWm653HhsV94TiIiIiKXoqSAiIiINEqWlpb07NmTzMxMTCaTAnKH6NmzJ1FRUQDExcWxe/duBaUJMJlMbN26lQ0bNhAQEEBYWBhGo1GBERERERFphJQUEBERkUbLz8+PmpoasrOzFYw7iIODA8OHD+fuu+8mLS2NFStWUFpaqsA0UtXV1axZs4Y9e/YQERFBnz59FBRpUv/PnJd6oukmoC889gvPSURERORSlBQQERGRRsvKyopevXqRkZGh3gJ3oO7duxMVFYWNjQ3x8fHs2LFD7aCROXXqFMuXL+f06dOMHj2aDh06KCjSpDg5OemcRERE5I6jpICIiIg0ar169eL06dMcPHhQwbgD2dnZER4ezj333MPOnTtZvny5eg00EocPH2b58uXY29szevRonJ2dFRRpktq1awc0j54C589FRERE5EqUFBAREbnDREZG8vbbb9ebVllZicFgICsrq970RYsWYWVlxfHjx+tNr62t5S9/+Qvt27fH1taWfv368d13313T/n/++WcsLS35+uuvL5pXXFzMpEmTcHV15a677mLkyJEcPXoUX19fMjMzqaur469//SsdOnTA3t6eAQMGsGHDBvP6NjY2GAyGej/jx48HoLCwkIkTJ+Lu7o6DgwMPPvhgvUTDokWL6NmzJ7a2tnTu3Jn333//iufh4OBw0b4MBgM2NjbmZb755hsCAwOxtbWlS5cu/O///q8a4A3y9vYmKioKBwcHc6+Buro6BeY2SU9PJyEhAT8/P4YPH06LFi0UFGmyIiIiANhWDCVnm15ioOSsiW3F9c9FRERE5EqUFBAREZHLmjdvHg4ODnzxxRf1pr/33nusWLGC7777juLiYl544QVGjx59TbX/FyxYgJOTE/Pmzas3vba2luHDh1NeXk5GRgaHDh2i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eugh2rVrp6BIs3LmzJl6yYDhw4fz4IMPcv/99zN27FgAlh01sS6/8SYG1uWbWHb03PGNHTtWvQRERETkuhiqqqrMn3SsrKwUERERkWYuMjKS0NDQet+IrKysxNbWlr1797J582beffdd9u7dW+/h9b59+/Dz8+PgwYO4uroyc+ZMFi9ezIkTJ+jYsSPPPfccv//97y+5z/379+Pr68vBgwfx8vIyT6+trcXHx4c33niDiRMnkp+fz9NPP823336LtbU1v/nNb4iNjcXb2xuAmpoaZsyYwUcffURVVRWBgYG8+eabhIaGAmBjY1OvxBFAREQEa9euJScnhxdffJHExESqq6vx9/fnrbfeIiQkBIB//vOffPDBB+zfvx93d3eioqJ4++23r/r56MSJE7i7u5OXl2cuVXTe119/zaxZs8jKysLT05NnnnmGp59+Wo3wFqmrqyM9PZ0dO3bg4+NjHrBaLm///v0kJyfj7e1NSEgIRqNRQZFm48yZM2RkZLB3717atGlDnz59Lnrfzs3NJTIykrKyMlq2gC+DLfBzalyJ3D0lJh5OqaO0GhwdHVm7dm29/1tFRERErtZjWkkBERERuanc3NyorKysN23s2LHMnz//pu0jNzeXtWvXMnHixAYf9HT06NGsW7eu3rTWrVtz4MABXewmori4mKSkJMrLywkJCTEnmeT/mUwmtm3bRmZmJkFBQfTp00dBkWbjl8mAfv36mQd+v5S4uDhiYmIA8G15LjHgZNU4EgMlZ008kFzHz/+ptPfJJ58QERGhiywiIiL1KCkgIiIizdKyZcvw8PDg7rvvVjDkqkwmE5mZmWzfvh0vLy8GDx7c4AmlpqK6uprExEQKCwsJCwszD7Yq0tSdPn2azMxM9u7di6enJ3369LliMuBCsbGxzJkzB4ABrrDobstGcU73b6xlb+m535999lmNJSAiIiKXpKSAiIiINEsHDhwgKSmJRx99VJ9h5JqVlpaSlJREcXExAwcOpHv37nd0PE6dOkViYiIA4eHhODs7q5FIk3f69GnS09P56aef8PLyol+/fri4uFz3dp577jm++uor4Fxi4J9Bt6/HQMlZE0+l1bG16NzrsWPHEhsbq4stIiIil6SkgIiIiDRLJpOJJUuW0KVLF/r27auAyHXZvXs327Ztw8PDg5CQEBwcHO64GBw+fJgNGzbQqlUrwsPDadGihRqGNGllZWXs2LHjVycDzispKSEqKoq9e/cC50oJvRtw68cY2FNiYsoP/18yyNfXl7i4OJycnHTRRURE5JKUFBAREZFm66effiIlJYWHH35Yn2PkupWXl7NhwwZOnDhB//796dmz5x1z7unp6aSlpeHv70///v3rDSou0tSUlZWRlpbGgQMH6NixI4GBgb8qGXChkpISXn31VXOPgZYt4J/9LBjoemv+Ztblm3h+x7lBheFcD4H//u//VkJARERErkhJAREREWm2TCYTCxcupGfPngQEBCggckOysrJITU3FxcWFsLCwZt1roKamhuTkZHJycggJCaFz585qANJk/TIZ0Ldv3wYrgXXhGAMAD3kZeKarAU+7hkkOHK0w8X62iaW55tt1jSEgIiIi10xJAREREWnWdu3aRWZmJhMmTMDS0lIBkRtSUVFBUlIS+fn59O/fnx49ejS7b8+Xl5eTkJDAmTNnGDZsGK6urrrw0iSdOnWKjIyMW5IMuFBcXByvvvoqZWVl5mmPdTTwp66GmzbWQMlZE//INjHv0P8nAxwdHYmNjSUiIkIXX0RERK6JkgIiIiLSrNXW1vL555/Tv39//Pz8FBD5VQ4cOMDmzZtxcHDg3nvvpWXLls3ivAoKCli7di3Ozs7cd9992NnZ6WJLk3Pq1Cm2b9/OoUOH8PHxITAw8JYPjp2bm8vs2bPN5YTgXEmh+zwMDHSFe1tff4Kg5KyJ7wpMpBadKxd0vlQQnCsXNG3aNLy8vNQARERE5JopKSAiIiLN3vbt29m3bx/R0dGqjS6/WmVlJcnJyeTm5tK3b1969+6NhYVFkz2fvXv3snnzZrp168agQYOa9LnInemXyYCgoCAcHR1v6zGlpKQQGxvL1q1bL5o3wBXzmAMD3c7962g8N6+s5ty/qSfO3YanFpnYWnTx9gcMGMBzzz1HcHCwGoCIiIhcNyUFRERE5I74wPPll18SHBxMt27dFBC5KXJycvj++++xsbEhLCzspg1ceqvU1dWxefNmsrKyGDx4ML6+vrqo0qQUFxeTkZHRqJIBv5SSksKnn35KSkpKvbJCN8LR0ZHg4GAmT56sZICIiIj86nvkK1FSQERERJqFrVu3cujQIcaPH69gyE39ML1582YOHDhAQEAAgYGBTWLsioqKCtatW8epU6e49957adeunS6mNBnFxcX88MMP5Obm0q1bNwICAhpdMuBSEhISSElJISEhgZ9//vma1mnXrh0RERFEREQoESAiIiI39T7mSpQUEBERkWahoqKCRYsWcc899+Dt7a2AyE119OhRkpOTsbCw4N5778XNza3RHmtRURHffvsttra2RERE4ODgoAsoTcIvkwF9+vTB3t6+yZ5PSUkJe/bsAc6NRQCYxwbw8/PDyclJF11EREQahJICIiIicsfYtGkTRUVFjBo1SsGQm66mpoaUlBSysrLo1asXQUFBGI3GRnWM+/fvJzk5GW9vb0JCQhrd8YlcyvHjx0lPT+fo0aP4+vri7+/fpJMBIiIiIrebkgIiIiJyxygtLWXJkiXcf//9tG3bVgGRBpGfn09SUhJ1dXWEhYXh4eFx24/JZDKxbds2MjMzCQoKok+fPrpQ0ugdP36cH374gby8PHx9fQkMDMTW1laBEREREfmVlBQQERGRO0pSUhJlZWWMGDFCwZAGU1tbyw8//MCuXbvo3r07AwYMuG2fpaurq0lMTKSwsJCwsDA6dOigCySNWn5+Punp6UoGiIiIiDQQJQVERETkjlJaWsrixYsZNWoUrVq1UkCkQZ04cYKkpCQqKysJDQ3F09Pzlu7/1KlTJCYmAhAeHo6zs7MuijRa+fn5bNu2jRMnTtCjRw969+6tZICIiIhIA1BSQERERO44CQkJAERERCgY0uDq6upIT09nx44d+Pj4EBwcjI2NTYPv9/Dhw2zYsIFWrVoRHh5OixYtdDGkUfplMiAgIABra+s75vzLy8txdHQkLy/vkuXGHBwc+P777wkICFBjERERkZviakkBC4VIREREmpvAwEAOHz5MaWmpgiENzsLCgqCgIMaMGcPJkyeJi4sjJyenQfeZnp5OQkICfn5+DB8+XAkBaZSOHTvGypUrWbNmDa1bt2bixIkMGDCgSScEbGxsMBgM9X6mTp2qiy0iIiJNilEhEBERkeamVatWtGnThvT0dEJDQxUQuSVcXFwYPXo0mZmZrF+/Hi8vLwYPHoydnd1N20dNTQ3Jycnk5OQQFhZG586dFXhpdI4cOUJ6ejqnTp2iZ8+ehIeHN6ueASkpKQwcOPC27b+urg4LC32/T0RERG6cPkmIiIhIs9S3b1/27dun3gJySxkMBgICAhg3bhzl5eXExcWRlZV1U7ZdXl7ON998w7Fjxxg5cqQSAtLoHDlyhK+//poNGzbg6enJww8/TFBQUJNOCJw4cYK2bdty+PDhqy771Vdf0a1bN5ycnBg2bNhl10lOTqZHjx64uLgwdepU6urqzPNKSkqYMGECLVu2xNfXl7Vr1wLwr3/9i8DAQIYMGUJgYKAam4iIiPwqSgqIiIhIs9S2bVvc3d3JzMxUMOSWa9myJaNHjyYoKIgtW7awZs0aKioqbnh7BQUFLF26FKPRyJgxY3B1dVWQpdG4XDKgOYxZ5+rqykcffUS7du2uuNz27dt5/PHH+eijj8jPz6dPnz6MHDmy3gN/OJfcGzNmDE8++SQHDx7E19eXM2fOmOf//ve/x8LCgvz8fD788EMefvhhioqKAPjpp5945ZVXSE1NVaMTERGRX0UDDYuIiEizlZOTw/r165kwYcJNLeEicj3Ky8vZsGEDxcXFDBw4kO7du1/X+nv37mXz5s1069aNQYMGqWyINKr32O3bt3P69Gl69epFjx49mv09pY2NDVVVVebX1tbWVFZW8uyzz1JWVsann34KnCv15eHhwZo1a/Dz8zMPNPzDDz/wpz/9iUOHDpm3cX6g4U6dOuHi4sLBgwdp3749AGFhYfzXf/0XJSUlfPHFFyQlJanhiYiIyFVdbaBhjSkgIiIizZa3tzf29vbs2rWLAQMGKCByWzg4OPDggw+SlZVFamoq2dnZhIWF4eDgcMX16urq2Lx5M1lZWQwePBhfX18FUxqFXyYDevXqhdF459xaXmpMgby8PHr37v3/N9pGI56enhw5cgQ/Pz/z9Pz8fDp27HjJ7ebl5VFbW0vXrl3rvQ+MGjVKiW0RERG5qfQ1IxEREWnWAgMD2bt371W/KSHS0Lp3705UVBRGo5G4uDh2796NyWS65LIVFRWsXLmSgwcPMnz4cCUEmoH58+cTFBRkfr1o0SJ69uyJra0tnTt35v3337/qNtLS0ggNDcXR0ZFWrVrx9NNPc/r0adLS0jAYDJf8iYyMBM7Vqp88eTJubm44OTkxcuTIejXv3377bfM6LVq0oGPHjrz66qvmNhoZGWme37FjR8aOHcvzzz9PYGAgDzzwgHleq1at+N3vfkdBQcFVz2fSpEnExMQAkJqaisFgYM2aNfWW+ctf/sL48eMb/fVt3759vXjW1NSQm5uLh4dHveXatGlDYWHhJbfRrl07c+mgyspKKisrOXv2LH/605/0ByQiIiI3lZICIiIi0qx17doVGxsbdu3apWDIbWdnZ8fw4cP5zW9+Q3p6OsuXL79oMOyioiKWLVtGTU0NDz300FVrmUvTk5qaypQpU/jggw84efIkixcvJjY2lgULFlx2nYqKCiIjIxk9ejQFBQWkpaWRnZ3N1KlTCQoKwmQyYTKZiI+Pp1u3bubXa9euxWQyMWLECPLz80lOTiYjIwNPT09CQ0PrjXURHh5OdXU15eXlLF68mI8++oh58+axf/9+8vPziYqKYufOndTU1GAymeo9+H/jjTeorq4mJSUFOzs7Bg8ezOnTp687Nk899RRlZWWN5lqZTCbWrFlDbW3tFZf77W9/y5IlS/j+++85e/Ysr7/+Om5ubgQHB9dbbujQoeTn57Nq1SoAVqxYYb4GDg4OPPDAA0ybNo3Tp09TUFDAM888c02DHIuIiIhcDyUFRETkupWUlJCamkpqairx8fHEx8ebX5eUlChA0qgYDAZ69+7Nrl27rvpQR+RW8fHxISoqCgcHB+Lj49mxYwd1dXXs37+fb775hrZt2zJy5MirlhiSpik9PZ0ePXowdOhQbGxsCAoK4s033+TEiROXXScnJ4fi4mKmTJmCnZ0d7du355133sFgMFx1f8nJyezcuZMvv/wSPz8/OnXqxP/8z/9gb2/P4sWL671fGo1GrK2t6d+/P4MGDSIuLo6UlBQcHBzw9/enV69eWFpaXnxjaWGB0WjEx8eHDz/8EBcXF3N9/WtlbW1N586defHFFxvNtSoqKuKxxx4jNzf3isv16tWLBQsWMGXKFFxdXdm0aROrV6++KFb29vYsW7aMF154AW9vb5YsWYKbm5t5/ty5czl79ize3t707NkTa2trvLy89EcjIiIiN5XGFBARkatKTEwkJSWFhIQEjh49ek3reHp6EhERQXBwMOHh4Qqi3FbdunUjPT2dPXv20KtXLwVEGgUbGxvCw8PJycnh+++/Z9euXZw5c4agoCD69OmjADVjw4cPZ8aMGcyYMYM//OEPtGvXjokTJ15xna5duxIYGMiECROYNWsW/v7+BAQEMHfu3Kvub+vWrfTr1w8nJyfzNIPBQEhICKmpqTz++OPm6SaTiZ9++only5ezfv16/vznP/Pwww+zcOHC6xrkOjIykuTk5OsuffPJJ5/g7+/PhAkTGDJkyG2/Vm5ubvV6RFRWVl522VGjRjFq1KiLpjs4ONQrFRYSEsKePXsuuQ1XV1cWLlx40fQnnniCJ554Qn88IiIiclOop4CIiFxSSkoKTzzxBD179uSJJ57g008/veaEAMDRo0f59NNP620jJSVFgZXbwtLSkp49e5KZmXnZGu4it0u7du1wcnKiqqoKg8FAXV2derU0c97e3mRkZFBYWEjv3r0ZMmSIuZzM5RiNRpKTk/H392fUqFF069aNOXPmXFNbOXnyJK1atbpouru7O6dOnTK/TkxMxNramp49ezJjxgz++te/EhMTY/62+8svv1xvvIILexn8UuvWrSkuLr7u2Pj4+DBr1iyeeOKJKz6AFxEREZEbp6SAiIjUk5KSQlRUFNHR0SQmJtardd0b+J2FBb+zsGCO0cgco5F//efn/Ovz83tfsM3S0lISExOJjo4mKipKyQG5Lfz8/KipqSE7O1vBkEbj1KlTLF++nDNnzjB27FiGDRtGdnY28fHxVywlI02ft7c3n3zyCXl5eUydOpXHH3+cL7744orr2NvbM2vWLA4dOsTcuXP54osvmDJlylX35ezsfMn2dPz4cZydnfnpp5/YuXMnPXv2ZMeOHZSWltK6dWvat29fb/m33nrLPFaByWS64gDAeXl53HXXXTcUm2nTpuHk5MSsWbPUUEREREQagJICIiICQG5uLtOnTyc6OprU1NRzDx+ACAsLXrSwYKXRyD9atOAxS0ses7QkwGAgwGCg839+zr8+P/8fLVqw0mjkRQsLIiwssP/PflJTU4mOjmb69OlXrc8rcjNZWVnRq1cvMjIyFAxpFA4fPszy5cuxt7dn9OjRODs74+npSVRUFO3atWP58uWkpqZSU1OjYDUzr7/+OnFxceb3pujoaJ566ini4+Mvu87y5cuZOXOm+fWgQYP429/+dsV1zhs4cCDbtm2rN4BvbW0tiYmJmEwmMjIyaNOmDW3atMHPzw87OzumT5/Oa6+9dkPnd35w3kGDBt3Q+paWlnz66af84x//ID09XQ1GRERE5CZTUkBERIiLi2PYsGH1Hiw8ZGHBYqORly0tGWZpieM1DGT4S44GA8MsLXnZ0pLFRiMPXVCLOD4+nmHDhpkfiojcCr169eL06dPk5OQoGHJbpaenk5CQgJ+fH8OHD6dFixbmeUajkSFDhjBixAhycnKIi4sjPz9fQWtG3N3diYmJYfPmzVRWVrJv3z5WrVpFQEDAZdfx8fEhNjaWzz//nPLyco4fP85nn312xXXOCwkJoXfv3kycOJGsrCzWr1/P/fffT0lJCX/84x+Jjo7G3d293qDF//Vf/8WhQ4f49ttvr/m8TCYTBw4c4PHHH6e4uJjJkyf/qvfrmJiY69q/iIiIiFwbJQVERO5ws2fPJiYmxlwmKMLCgkVGI0/fYCLgchwNBp62tGSR0UjEf5IDpaWlxMTEMHv2bF0IuSWsrKzw9fXVN0/ltqmpqWHDhg3s2LGDsLAwBgwYUO9B7IU8PDwYN24cnTp1YuXKlWzatImzZ88qiM3AU089xZ///GeefPJJnJycGDp0KKGhocyYMeOy6/Tu3ZvVq1fzySef0KpVK7p3705ZWdlVSw7BuUGFly9fjtFopF+/fjzwwAOcPXuWrVu3EhAQcMk2aG9vz7Rp03j11VfN0345poCNjU29eRYWFgwcOJDq6mo2b95cb2DjG/GXv/yFHj16qMGIiIiI3GSGqqoq04U3yiIicmcoKSnhtddeM/cOsAfeMBoJuImJgCv5vq6Ot2prOf2f1+PGjWPmzJm/+gGCyNVUVFSwcOFCIiMj8fLyUkDklikvLychIYEzZ84wbNgwXF1dr3ndEydOkJSURGVlJaGhoXh6eiqgzdTo0aNZt25dvWmtW7fmwIEDN7S92tpa9uzZw44dO7C2tqZPnz74+PhcNhl1s8XGxtYre3Reeno6Xbt21QUXERERaQBX+zKRkgIiInegkpISoqOj2bNnDwA+wMtGI51v0QOC8/abTMyoqaHgP6/9/PxYsmSJEgPS4JKSkigrK2PEiBEKhtwSBQUFrF27FmdnZ+677z7s7Oyuext1dXWkp6ezY8cOfHx8CA4OrvdNbZEL3e5kgIiIiIjcPkoKiIjIRaZPn27uIdCbcz0EHG/TQ4Ky/yQGdv7n9bhx4/j73/+uiyQNqrS0lCVLljBy5EhatWqlgEiD2rt3L5s3b6Zbt24MGjQIC4tfV8GzuLiYDRs2UFFRQUhICN7e3gqymNXU1LB371527NiBnZ0dffv2pUOHDkoGiIiIiNxBlBQQEZF6YmNjmTNnDnAuIfCPCwa3vJ3+VF1tTgw8++yzPPfcc7pY0qDWr19PTU0NERERCoY0iLq6OjZv3kxWVhaDBw/G19f3pm57586dbN++HS8vL0JCQtRr4A5XU1PDrl272LVrF/b29vTt21cJIxEREZE7lJICIiJiFhcXR0xMDHCuZNCc29hD4JfKTCaeuKCU0HvvvUdUVJQumjSYEydOsGzZMsaMGYObm5sCIjdVRUUF69at49SpU9x77720a9euQfZTWlrKd999R3l5OYMGDcLHx0fBvwNv+H788UclA0RERESk3mfEK1FSQETkDpGbm8uwYcMoLS3FHnj/NowhcDX7TSaeqanhNNCyZUu+/fZbDQQrDWrNmjVYW1tzzz33KBhy0xQVFfHtt99ia2tLREQEDg4ODbo/k8nEjz/+yLZt2/Dw8CA0NPSGxiyQpnejt3PnTnbv3o2zszN9+vShffv2CoyIiIiIXDUpYPnKK6/MMr+wtFTERESaqVdffZWMjAwA3jUa8WuEtYVdDAY6GAxsMJmoqqqitLRUpV2kQdnb27Nt2za6dOmCtbW1AiK/2v79+0lMTMTLy4uIiIhbUtLHYDDQqlUrunTpwoEDB0hLS8PGxkY9YJrxDV5GRgbr16/n7Nmz3H333QwYMAAnJycFR0REREQAqK2tvfI9hHoKiIg0fyn/x969x1VV5/sff3ERuUNeELkIgopgCpqKeEutpJxTjgqiWU7ldGymbMTsN800k5fOnKlpBuzMpZkptdQyATXTGqHR1FSgUFETTERAUAFRuchNLvv3B7oGUjPNCxvez8fDB+y19177+/2u5QbWe3+/n+RkoqKiAAi3tORXrTwE/n1DA4mNjQCsWbOGsLAwHUS5ZT766CM6d+7MqFGjNBhyw0wmE19++SX79+9n8ODBDBo06I615fDhw6SkpNCpUyfGjRt3y2cqyO1RW1vLwYMHNTNARERERK5JyweJiAhTp04lJSUFgNXW1nRvhbMEmjtlMjG9vh6AYcOGERcXp4Mot0xubi5btmxh+vTpWnJFbkhdXR1JSUkUFxczbtw4fHx87nibqqqq2LZtG4WFhQwdOpR+/fph0crf++XKamtrSU9P59ChQ3Tt2pV77rkHDw8PDYyIiIiIXJVCARGRdq75LIEplpbMMZOl4v7c0MBazRaQ22TdunW4u7szfPhwDYZcl9LSUpKSkgAYP348rq6urap92dnZ7Nq1C0dHR+6//36cnZ110MxE8zCgS5cuDB06FHd3dw2MiIiIiFzTtUIBSw2RiEjbtnTpUgAcgCcszedt/wlLSxy+1QeRWyU4OJjMzMxr/uIk0lxeXh7r16/HwcGBSZMmtbpAAMDf35+pU6fi6OhIfHw86enpNF4MXKV1qq6uJjU1lffff5+ioiImTJjAI488okBARERERG4azRQQEWnj7r777qaCvWZQS+DbLtUWcHZ25uuvv9bBlFvGZDKxZs0aevfuzT333KMBkWvau3cvaWlpBAcHM3ToULNYmic3N5edO3dia2vLuHHj6NSpkw5kK1JdXc2+ffvIzMyke/fuDBo0SEGAiIiIiNwQzRQQEWnHEhMTKS8vByDEDNt/qc3l5eUkJibqgMotY2FhwYABAzh48CANDQ0aELmq+vp6tm7dSnp6OuPGjSM0NNRs1ur39fVl6tSpdO7cmXXr1pGWlqbzvRWorq5m9+7dfPDBB5SWlvLII48wYcIEBQIiIiIicssoFBARacOSk5ON70damt9bfvM2N++LyK0QEBCAtbU1GRkZGgy5ovPnz7NhwwZOnjzJxIkT6dWrl9n1wcbGhrFjx/Lggw9y5MgR4uPjKSkp0cG9AyorK40woKKiwggDunbtqsERERERkVtKyweJiLRhw4cPp6CggAHA/3XoYJZ9eL6ujgOAl5cXu3fv1kGVW+rgwYPs37+f6dOnY2Vmy23JrVVUVMTmzZtxdXXlgQcewN7e3uz7VF9fT3JyMocPH6Z///4MHjwYa2vrVtG2srIyMjMzAcjPzwfA29sbgMDAQFxcXMx23CsrK9m7dy/ffPMN3t7eDBkyREs5iYiIiMhNda3lgxQKiIi0YT169ADgJ5aWPGmmFziXNzTw3sWimMePH9dBlVuqoaGBlStXMnToUIKCgjQgAkBmZia7du0iICCAESNGYGnZtibbFhYWsm3bNhobGxk3btwdWbYmKSmJ5ORkEhMTKSgo+F7P8fLyIjw8nLCwMMaPH9/qx7miooL09HSFASIiIiJyyykUEBFpp8rKyujfvz/QdkKBgwcPmvWnQ8U87Nmzh6ysLKKiosxmrXi5NRobG9m1axeHDx9m5MiRBAYGttm+NjQ08NVXX3Hw4EH69u1LaGjoLf/bIDk5maVLl5KSkmLUv7lRzs7ODBs2jFmzZhEWFtaqxraiooK0tDSys7Pp2bMnAwcOVBggIiIiIreUQgERkXYqOTmZqKgoAJZYWxNiphc3000m5tbXA7BmzZpWd7FH2uYvTyuRg5NzAAAgAElEQVRXrmTMmDH4+/trQNqpqqoqPvvsM0pLS7n//vvx9PRsF/0uKSnh3//+N/X19YwZMwYvL68r/h/5IX83JCcnExsbS0pKymX3hfatIaxfLYDx1dm+KRgur2qaoZF8qKPxNfWw7WX7GDZsGNHR0Xf858W3w4B77rkHV1dX/ecSERERkdvyd+13sdYQiYiIiPyHjY0NgYGB7N+/X6FAO3XmzBn+9a9/YWdnx5QpU3B0dGw3fe/SpQuRkZHs27ePzZs34+/vz4gRI1qEAAkJCYwZMwYPD4/r2nd+fj5LliwhPj7e2OZk30j44GqGBdUQPqQaFwfTNfcTFtQUFhAJZZUWJH5lR0qGLYlpdlRUWZKSkkJUVBSRkZHMnTvXqEVwu5SWlrJv3z4jDIiIiFAYICIiIiKtiqWGQESkbbpUmBHAnC9nOV6lTyK3UkhICOfOnSM3N1eD0c4cPXqUDRs24OHhwcSJE9tVIHCJlZUVgwcPZvLkyZw5c4a4uDjj/8KePXs4f/4827Ztu+anj5qLi4vjoYceahEIPPVgBbv/fJKYn59l6piq7xUIfJuLg4mpY6qI+flZdv/5JE89WGHcFx8fz0MPPURcXNxtGbfS0lK2bNlCQkICABEREdx3330KBERERESk1dFMAREREZFvsbe3p0+fPqSnp+Pr66sBaQdMJhNffvkl+/fvZ/DgwQwaNKjdj0mnTp2YPHkyBw4cYMuWLXTr1o2TJ08CGMHA9ynwGxsbS2xsrHE7YnQl0RFleLs13NT2ujiYWPhEKbMmVBCb4ELCDgfKy8uZP38+J06cIDo6+paMU2lpKXv27CEnJwd/f3+ioqJwcnLSfyoRERERabU0U0BEpI1qvlzCeTPux/mr9EnkVgsODub06dPGRVBpu+rq6vj000/JyMggPDxcgUDzPxYsLQkJCSEyMpLi4uIW9+Xm5n7nbJqysjJeeOEFIxBwsm9kzSvFxPz87E0PBFr8/HNrIObnZ3n7hRKcLtYjiI2N5YUXXqCsrOy69vXNN99cdUbE2bNnjZkBlpaWREVFMXbsWAUCIiIiItL6f8/XEIiIiIhcztnZGT8/P9LT0zUYbVhpaSnr16+nsrKSSZMm4ePjo0G5gqysLOovFn1vbtu2bVRUVFy2vaysjKioKGO5oMAeF4h7pfg/9QBug/Ah1cS9Uoxnl6Z2x8fHExUV9b2DgTNnzrB9+3YOHjzYYvvZs2dJTExk3bp12NjYKAwQEREREbOjUEBEpI0KCgoyvt/X2Gi2/Wje9uZ9ErkdgoODKSgooKSkRIPRBuXl5bF+/XocHByYNGmS1n6/ijNnzrBnz54r3nfhwgW2b99+2fbFixeTkZEBQGjfGuIWFNPPt+62t72fbx2bXy8ktG8NABkZGSxevPh79Xnjxo0AHDx4kAsXLrQIA+zt7Zk+fTqjRo1SGCAiIiIiZkehgIhIG+Xi4qI+ifxAXbp0wcfH56oXRMV87d27l8TERIKCgpgwYQIdOnTQoFxFRUUFPj4+Vy26fPLkSdLS0ozbMTExxgyB0L41xC88fUNFhG/azw4HE/ELTxvBQHx8PDExMd/Z340bNxrLBl24cIH169ezfv16nJycjDDAwcFBJ4eIiIiImCUVGhYRacM8PT05ceIE+xobedLKyiz7cGmmgKenpw6o3BEDBw7ko48+ory8HGdnZw2Imauvr2fHjh3k5uYybtw4evXqpUG5Bl9fX6Pg9oULFygpKeHUqVOUlJRw+vRpqqqq2Lt3Lw0NDeTl5bFkyRKgacmgd15sPbNs3nmxhAd/6c6JEmuWLFmCl5cXU6dObfGYCxcukJSUZAQCJpMJCwsLKioqiIyMVDgtIiIiIm2CZgqIiLRh4eHhABwAKkwms2t/hcnEgW/1ReR2c3Nzo3v37uzdu1eDYebOnz/Phg0bOHnyJBMnTlQgcANsbGzw8PDgnnvuITw8nMcee4wnnniCQYMGsXv3bn77298CTUWFY35+9o7OEPg2FwcT78z/T/HhxYsXk5+fb9x/4cIFNm7cyJkzZzBd/JlpYWEBQGNjI0ePHtUJICIiIiJtgkIBEZE2LCwszPh+pxnWFWje5vHjx+uAyh0TEhJCVlYWVVVVGgwzVVRUxNq1a7G2tmby5Ml07txZg3KT2NjYMHjwYA4cOEB1dTUA78wvuSM1BK6ln28dMT87C0B5eTmxsbHGfZs3bzYCgUthQHOXaguIiIiIiJg7LR8kItKGhYeH4+TkREVFBenAQ2bW/vSLX21tbTly5AhFRUVYWVlhaWlpfIrzwoULnDlzxnhOnz59GDNmjA6+3FTe3t507tyZ9PR0hg8frgExM5mZmezatYuAgABGjBiBpaU+F3OzJScns2XLFgAiRlcSFlTben82DqkmYnQlCTscSEhI4L777uPs2bNUVVVhYWFhBALdu3c3ntO5c2c6duxIRUWFAiURERERMXsKBURE2riwsDCSkpLY1dhIhaUlTlf49GNrVGEyseviTIHRo0fzwAMP8Pnnnxuf1L7aJzkvrXstcrMNGjSILVu2EBISgr29vQbEDDQ2NrJr1y4OHz7MyJEjCQwM1KDcIs0/cR8dUdbq2xsdUUbCjqZCwa+99hoLFy4kKCioRRAgIiIiItJW6WNSIiJt3KxZswA4D7xrRksIvdvYyPlmffD09OSxxx4jICAAaFrn+dJsgUtfbWxsFArILePr64uDgwMHDx7UYJiBqqoqNm7cyLFjx5gwYYICgVsoOTmZlJQUAJ56sAJvt4ZW32ZvtwaeerACgLy8POzt7RUIiIiIiEi7oVBARKSNCwsLIzQ0FIC1jY2cMoOCw6dMJtZeDDBCQ0Nb1Ea49957jduXZgpc+moymfjqq68oLS3VgZdbYuDAgWRmZmpd8VbuzJkzrFu3jvr6eqZMmYKnp6cG5RZaunQp0FRcODqyzGzaHR1ZZhQdvtQHEREREZH2QKGAiEg7MG/ePON7c5gt0LyNzdt+Sf/+/bn33nuxsbFpsX3AgAFUVVXx0UcfER8fz969exUQyE3Vp08frK2tycjI0GC0UkePHmXDhg14eHgwceJEHB0dNSi32KVZAuGDq3FxMJlNu10cTIQPrm7RBxERERGR9kChgIhIOxAWFkZERAQAiY2N7GzFwcDOxkYSL7YvIiKixSyB5gICAnj44YeNYMDGxoZ77rmHe++9l5kzZxIaGkpZWRnr1683AoLy8nKdDPKDWFhYEBwczNdff01DQ4MGpBUxmUykpqaydetWQkJCGDduHNbWKp91qyUmJhrvrcOCasyu/ZfaXF5eTmJiog6oiIiIiLQLCgVERNqJ6OhonJycAHitoYGjrXAZoaMmE69dvNDq5OREdHT0dz6+c+fOTJkyhU6dOrWoJWBpaUmPHj0YO3YsM2fOZPDgwZw9e5aEhATWrl1Leno658+f10khNyQoKAhAswVakbq6Oj799FMyMjIIDw9n0KBBGpTbJDk52fg+fEi12bW/eZub90VEREREpC2zqK2tNa4KfXsZBhERaVvi4uKYP38+AP7AEmtrnC6ux3+nVZhMPF1fT+HF22+//Tbh4eHf67kXLlygoqKCzp07f+fj6uvrycvLIzs7m/z8fDp16oS/vz+9evXC3t5eJ4h8b+np6Xz99dfMmDHDqGkhd0ZpaSlJSUkAjB8/HldXVw3KbTR8+HAKCgoI7VtD/MLTZtmHyIVdST1si5eXF7t379ZBFRERERGzd606eJpTLSLSjkydOpWCggKWLFlCNvCbhgbebCXLa8xtFgjMnTv3ewcC0BRqXysQALC2tsbf3x9/f3/q6urIzc0lOzubL7/8kq5duxr32dnZ6WSR7xQUFER6ejpHjhwhICBAA3KH5OXlsXXrVtzc3Bg/fjwdOnTQoNxmBQUFAIT1qzXbPoT1qyX1sK3RFxERERGRtk6hgIhIOzNv3jwKCgpISEhgv8nEL+rr+R8rqzs2Y6DCZOI3DQ1kX7wdERFxxeLCN1uHDh3o3bs3vXv35sKFC8YMgtTUVLp164a/vz9+fn507NhRJ41cxsbGhsDAQPbt26dQ4A7Zu3cvaWlpBAcHM3ToUM3YuAPKysraZJ9cXFx0cEVERESkTVNNARGRdmjBggUEBgYCsN9kYm59/R2pMXD04pJB+y++dmBgIAsWLLjt7bCxsaF37948+OCDPP744/Tu3ZucnBxWrVrFp59+yjfffHPNqXfS/vTv35/Kykpyc3M1GLdRfX09W7duJT09nXHjxhEaGqpA4A5pXlfD3GcKXKlPIiIiIiJtlUIBEZF2yMXFhbi4OCIiIgDIpmn5nvTbGAzsbGxssWRQREQEcXFxd/wTmjY2NgQEBDBhwgQee+wxevbsSVZWFitXrmTz5s1kZWVRV1enk0iwt7cnMDCQvXv3ajBuk/Pnz7NhwwZOnjzJxIkT6dWrlwZFRERERETkOmn5IBGRdsrFxYWYmBi8vLxYsmQJ52kKBh60tOQnlpZ0v0WfvD1lMvFeYyObGxuNbXPnzr0tSwZdr44dOxIYGEhgYCDV1dVkZ2eTkZHBjh078Pb2xt/fnx49emgd83YsJCSE999/n/z8fLy9vTUgt1BRURGbN2/G1dWVyZMnqzh4K5Cfn29872zfaLb9aN72/Px8wsLCdHBFREREpE1TKCAi0s7NmzcPLy8vFi1aREVFBZsvXrCPuBgO3KxaAxUXw4CEZmGAk5MTMTEx11VU+E6xs7Pj7rvv5u6776aqqoqjR49y4MABtm3bZgQEPj4+WFvrR2t7Ym9vT+/evUlPT1cocAtlZmaya9cuAgICGDFiBJaWmuwqIiIiIiJyo3TlQkREmDp1KmFhYcTGxpKQkABAwsVwYKSlJSHAiBsICCpMJnY1NpJO03JB55vdFxERQXR0tFleSLW3t2fAgAEMGDCA8+fPc/ToUdLT09m+fTs9evQwZhBYWVnp5GoHBg0axIcffkhxcTFubm4akJuosbGRXbt2cfjwYUaOHGnUQpHWofn7d3mV+QY1zduucE9ERERE2gOFAiIiAjRdCImJiSEyMpKYmBhSU1M5D00zBwAaGwm2sGDgxWBg4MVP6jpcfH7lxa/7Ls4E2GcyGQWEmwsNDWXevHltZnkGR0dHQkJCCAkJoby8nKNHj5KWlsa2bdvw9fXF398fLy8vfbK5DXN2dsbHx4d9+/aZxawXc1FVVcVnn31GaWkpEyZMwNPTU4MiIiIiIiJyE1jU1tYaV2xsbGw0IiIiAkBycjJLly4lOTmZioqKH7QvJycnwsLCmDVrVrtZq7m0tJRjx46RnZ1NZWUlPXv2xN/fH09PTyxuUb0GuXNKSkpYt24dkydPpkuXLhqQH+jMmTP861//ws7OjvDwcBwdHTUorVBZWRn9+/cHYO6UMuZFlptlP2LinVmytqnI/cGDB+94wXsRERERkR/qwoUL33m/ZgqIiMgVhYWFGRfwExMTSU5OJjExkRMnTnyv53t6ehIeHk54eHi7LNro6urKoEGDGDRoEKWlpWRlZbFr1y5qa2uNgKB79+4KCNqILl264OXlxf79+7nvvvs0ID/A0aNH2bFjB76+vowePVp1OlqxtnjxXIGAiIiIiLQHmikgIiLXraysjIyMDADy8/OB/6zDHBQUpIsq36GkpITs7GyOHTtGfX09fn5++Pv74+7ursExcydPnuSTTz4hKioKZ2dnDch1MplMfPnll+zfv5/BgwczaNAgDYoZCAsL48SJE4T2rSF+4Wmz7EPkwq6kHrbF09OT5ORkHVQRERERMXuaKSAiIjedi4uL8en/9jgL4Ifo0qULXbp0ITQ0lOLiYrKzs9myZQuAERCoWK158vDwoGvXruzfv59Ro0ZpQK5DXV0dSUlJFBcXEx4ejo+PjwbFTISHh7Ns2TJSD9tSVmmBi4PJrNpfVmlB6mFboy8iIiIiIu2Bqh6KiIjcIW5uboSFhTFjxgzuu+8+GhsbSUxM5P333yc5OZni4mINkpkJCQnhyJEjVFVVaTC+p9LSUtavX09lZSWTJk1SIGBmmgfDiV/ZmV37m7dZoYCIiIiItBdaPkhERKQVMZlMnDp1iuzsbHJycujQoYMxg0AFbM3DunXrcHd3Z/jw4RqMa8jLy2Pr1q24ubkxfvx4OnTooEExQ/369aOiooKI0ZXE/PysWbV93t86kbDDAScnJw4dOqSDKSIiIiJtgpYPEhERMSMWFhZ4eHjg4eHByJEjOXHiBNnZ2WzatAk7Ozv8/Pzo3bs3rq6uGqxWKjg4mG3btjF48GB94OI77N27l7S0NIKDgxk6dKiKbpuxsLAwkpKSSEqzM6slhMoqLUhKszP6ICIiIiLSXmj5IBERkeuUkpKCra3tZdtfeuklpk2bdtn2vn37Mnfu3Mu2p6WlMWbMGJycnHBzc2POnDlUVlYa91tYWODl5cW9997LzJkzGTFiBFVVVXz00Uc88MADBAQEUF5eftl+ExISCA4Oxt7enj59+vDXv/61xf0HDhzggQcewNHREU9PT55//nnjdV977TUsLCwu+3f48GEAVq9ezd13342dnR29evXizTffNPZbXFzMjBkz6Nq1K46OjjzyyCMcO3bsquP4xz/+8YqvZWFhwWuvvQY0FbWeNWsWXbp0wcXFhYkTJ5KXl9eqzw8/Pz8cHBw4ePCg/rNcQX19PVu3biU9PZ1x48YRGhqqQMDMzZo1C4DyKkti482n0HxsvAvlVZYt+iAiIiIi0h4oFBARaadWr16NjY0Np0+fbrVtXLx4MSUlJWY9zrt376a4uJj333+/xfS9qqoqHnzwQSZNmkRRURFpaWkcOXKE55577so/sC0tjYAgMjKS1NRUTp48SWxsLGvXriU9PZ3z58+zadMmfvrTn7Jw4ULjdX//+9+zatUqAE6cOMG9997LuHHjOH78OLt37+bQoUP84he/MF4rPDwck8nU4l/fvn1JSUlh9uzZ/PnPf+bcuXN8+OGHxMTE8N577wHwk5/8BAsLCzIyMigoKCAwMJAHH3yQ+vr6K/Zp/vz5xv6fffZZZs+ebdx+6aWXMJlMPPzwwxQWFrJjxw727duHl5cXY8aMadVr9ltYWDBgwAAOHjxIQ0OD3myaOX/+PBs2bODkyZNMnDiRXr16aVDagLCwMEJDQwFYttmJ/GKrVt/m/GIrlm12AiA0NFQzBURERESkXVEoICLSTi1fvhxHR0fjYnFr1BZCgWXLlvHf//3feHh4sGnTJmN7bm4uZ8+eZfbs2djb29OjRw9ef/317/WJ6fXr1xMUFMRTTz3F6dOnGTRoECUlJcTFxfHiiy/y+OOPEx4ejqOjI0OGDOGll15izZo1ALz11lsMHjyYX/3qV3Tq1AkfHx9iYmL4+OOPqaur+87X3bt3L/369WPs2LHY2toyePBg/vd//9c4Rnv27OHxxx+na9euuLq68uqrrxIcHMypU6duaOx27NjBgQMH+OCDDwgKCsLPz4+//OUvODg48OGHH7bq4x4QEIC1tTUZGRl6s7moqKiItWvXYm1tzeTJk+ncubMGpQ2ZN2+e8X1sQuufLdC8jc3bLiIiIiLSHigUEBFpB0aOHElcXJxxOz8/n507dxITE8Py5ctbPLauro65c+fSuXNnunXrxi9/+UtMpqb1oXfv3s0999yDg4MDoaGh7N2713heQkICAQEBuLi48NBDDxlLvPzlL3/hwQcfNB730UcfERISAsA777zD/fffz09/+lPuuusu7r77bg4cOMChQ4dwd3enoaGBUaNG8cEHH5jluFdVVREXF8e0adOIiopi2bJlxn19+vRh4MCBTJ8+nf379wMQEhLS4jFXs3z5cmOfq1evxsPDg/vvv5+ZM2eSl5eHr68vq1evZsOGDXz99dfMmjWLjRs3Ak0X9keNGtVif8HBwRQXF1+zyOuECRM4fPgwL7/8MidOnABgxowZvPDCCwBMnz6d+fPnk5iYSENDAzY2NsTHx+Pt7X1D45eamsqQIUNwcfnPxTsLCwtGjx5NSkpKqz72VlZWBAcHs3//fs0WADIzM9m4cSN+fn48/PDD2Nvb6425jQkLCyMiIqLp58EOBxK/smu1bU38yo6EHQ4AREREaJaAiIiIiLQ7CgVERNqB3//+94wdO9a4/e677zJ+/HimTZtGbm4uaWlpxn2vv/46KSkpHDp0iP379/PZZ5/xzjvvUFZWxiOPPMLcuXMpLS3l6aef5sc//jGNjY3s2bOHp556in/84x8UFhYyaNAgJk6cSGNj4zXbtn37dn70ox9x4sQJhg4dyoIFC+jXrx+FhYVYWVnxxRdf8Oijj7a6Ma2trb1sHfzXX3+9xWMSEhLw9PQkJCSEqKgoEhMTjU/NW1tbs2PHDoKDg/nxj39MQEAAS5YsueYF5Ly8PHbs2MHUqVMJCwvD0dGRjz/+GGi6YF5dXU14eDgzZ84kKCiIgoICPvjgAzZt2kRmZiZlZWV06tTpO18jMTGxRb+eeeYZAHx9fdm3bx/FxcUMGDCAUaNGtZj9sGTJEl544QVeeeUVunfvTnR0NGfOnLnhMT537hxubm6Xbe/atSulpaWt/v9dUFAQ9fX1fPPNN+32vaexsZEvvviCnTt3MmLECEaNGoWlpX79bKuio6NxcmpakueFtzpxKLdDq2vjodwOvPBW03ugk5MT0dHROnAiIiIi0u7orzIRkXZg1KhRdO3aFQCTycS7777LtGnTsLW1ZeLEiS1mC6xevZoXX3wRd3d33N3d+cc//kGPHj1ISkqie/fuPP7443To0IGf/vSnvPDCC1RWVrJy5UoiIyMZM2YMdnZ2LFq0iIKCghZhw9UMHz6cSZMmYW9vz8SJE8nKyjKLMe3YseNl6+7/8pe/bPGYZcuWERUVBYC/vz/BwcGsXLnSuN/BwYGFCxeSk5PDsmXLWLVqFbNnz/7O112+fDkjRozAw8MDCwsLIiMjjeNnZWWFq6srpaWldOjQgd69e/Pggw/y+OOP07t3b3JycqiuriY1NZXMzExqa2uv+Brfrinw97//3bjP19eXt99+m1OnTvHcc8/x1FNPGUtQWVhY8MQTT5CamkpycjLFxcWMHDnymssSXY2rq+sVl486ffo0rq6urf4csbKyon///hw4cMCYbdOeVFVVsXHjRo4dO8aECRMIDAzUm3Eb5+3tzYIFC4CmosMvvNWJssrWU0S6rNKCp//UxSguHBMTc8MzmUREREREzJlCARGRdmb79u0UFhby8MMPAxhL0Fy6QFxcXIynp6fx+CFDhhAeHn7ZdoBf/OIXODk5cerUKfz8/Izt1tbWeHl5cfz48Wu2p/ka+lZWVlctSmtujh07xo4dO1i0aJHxifs9e/YYF/DXr1/PK6+8Yjx+xIgR/OEPfyA+Pv6q+zSZTLz33nts377d2Oef/vQnkpKSjOV8BgwYwO7du1s8b+nSpbz88stMmDCBCRMmkJeXR3Z2NitXruTTTz/l448/pkePHte8eP/qq68ay1DZ2NgQFRXFM888Q3x8PFlZWURERBizQ/z9/Xn33Xc5cuQIX3/99Q2N4bBhw/jyyy+pqKhoMQY7duwwipq2dv3796eyspJjx461q/eZM2fOsG7dOurr65kyZcpl7x3Sdk2dOpW5c+cCkJFnw9N/7NJq2ha12I2C09YAzJ07l/DwcB0wEREREWmXFAqIiLQzy5cvp6qqCkdHRywsLHj44Yc5d+4c69evB5o+6dm8MOzx48c5dOjQZdsBtm3bRnV1NT169DBqCADU19eTn5+Pu7s7HTp0aDMX+q/Hu+++S1hYWItP3BcWFnLs2DGSk5Px9/cnJiaGlStXcv78eU6fPs2KFSuMegtX8vnnn1NcXMy5c+da7Ldfv36sWLECgJdffpnXX3+dzZs3U1VVRVpaGr///e+ZNGkS0BTkHDx4kOzsbB5++GE6dOjAK6+8gq+vL1u2bKGkpOSqyz517dqV+fPns2vXLmpqasjKymLTpk2EhITg7e1NWloaL774IsXFxVRWVvLPf/4Te3t7/P39b2gMR48ezYABA5gxYwZHjhwhPz+fuXPncv78eaZNm2YW54GNjQ2BgYFG3Yj24OjRo2zYsAEPDw8mTpyIo6Oj3njbmXnz5hn1BVIybZm6qOsdnTFQVmnB1EVdycizAZrqCKi4sIiIiIi0ZwoFRETagZ07d1JSUkJFRQVr164lKSmpxUXlOXPmGJ9gnz59Om+88QYlJSWcPXuWGTNmsHXrVsaNG0dRURGrV6+msbGRhIQEZsyYgaWlJTNnzmTNmjXs3LmTCxcu8Oqrr9KlSxfCwsLo27cv+/btIycnh7y8PP72t79973Y7ODiQnZ19w8vP3CmNjY289957PPfccy22d+vWjalTp7Js2TIGDBjAJ598wttvv42bmxt9+/aloqLCWIrnSpYvX85jjz122dI5zz//vHH8xo8fT2xsLHPmzKFTp0489thj/PrXv2bGjBlA04X9xMRE1qxZg4+PD08++SRjx45l/fr1eHl5UVxczGeffXZZvYSUlBSeeeYZfv3rX/Pf//3fuLi4MHbsWMaMGcPLL7+Mra0t27Zto6CggICAALp168YHH3zAJ598grOz8w2No4WFBR9//LFxLvXr14+cnBw+//xzHBwczOZ8CAkJ4dy5c+Tm5rbp9xmTyURqaipbt24lJCSEcePGYW1trTfgdmrBggXGklEpmbZELXa7IzUGDuV24KGX3EnJtAUgMDDQWOJIRERERKS9sqitrTUWubWxsdGIiIi0QSNGjGDOnDlUVlbyxhtvkJmZ2WLZnqysLIKCgjh27Bju7u785je/YdWqVdTW1vKLX/yC3/72twDs2bOH5557jkOHDhEYGMjKlSvp06cPAB999BEvv/wyx48fZ8iQIfzzn/+kV69eAPzsZz/j/fffp2vXrtx33318+eWXpAb3j6IAABtWSURBVKen884777Bq1Sq2bdsGwKZNm5g/fz6HDx8Gmj71vmTJEv7yl7/w5JNPtpvjFRMT02JpoUv27t1rjPetVFVVxdGjR8nOzubcuXP06NEDf39/evTogZWV1XXv76uvvmpR6PqSN998k1mzZrWLY/rFF19w5swZfvzjH7fJ/tXV1ZGUlERxcTHjxo3Dx8dHb7xCWVkZixYtIiEhAQBn+0benl9CWFDtbXn9xK/seOGtTkYNgYiICBYsWICLi4sOjoiIiIi0aRcuXPjO+xUKiIiIyFWdP3/eCAjKy8vx9fXF398fLy8vLC014fD7Ki8vZ82aNfzoRz/Cw8OjTfWttLSUpKQkoGmmijkUgZbbKyYmhiVLlhi3I++tZO6UMrzdGm7J6+UXW7FkrQvx2/8zo2ju3LlaMkhERERE2g2FAiIiInJTlJeXGwFBZWUlPXv2xN/fHw8PDwUE38OWLVuora1lwoQJbaZPeXl5bN26FTc3N8aPH0+HDh10oOWK4uLiWLRoUYvC4bMeqmBuRBkuDqab8hpllRYsSXBh6b+cjG1OTk7ExMSoqLCIiIiItCsKBUREROSmKy0tJSsri2PHjlFdXW0EBJ6eni2WppL/KCkpYd26dUyePJkuXbqYfX/27t1LWloawcHBDB06VMddrik/P5/Y2FhjOSFoWlIofEg1w4JqGD+4+roDgrJKC5LS7EjJsCXxKztjqSBoWi4oOjoab29vDb6IiIiItCsKBUREROSWKikpITs7m2PHjlFXV2cEBN27d9eF4m9JTEwEMOtPLdfX17Njxw5yc3MZPXq0UTtE5PtKTk4mJiaG1NTUy+4bFlhDWL+mmgOXvjrZNQJQUd10wT/5UEfj66UCws2FhoYyb948wsLCNNgiIiIi0i4pFBAREZHbpri42AgIGhsb8fPzw9/fH3d3dw3OxfH56KOPmDZtGs7OzmbX/vPnz5OYmEh1dTUPPfQQnTt31kGVG5acnMzSpUtJTk5usazQjXByciIsLIxZs2YpDBARERGRdk+hgIiIiNwRzQMCwAgI3Nzc2vW4bNy4EScnJ8aMGWNW7S4qKmLz5s24urrywAMPYG9vr5NcbprExESSk5NJTEzkxIkT3+s5np6ehIeHEx4eriBARERERKQZhQIiIiJyxxUWFhoBgbW1tREQtIW19a9Xfn4+mzdvZsaMGd95Yb2srIzMzEzjOYCxNnpgYCAuLi63rc2ZmZns2rWLgIAARowYocLScsuVlZWRkZFxxfM/KCjotp7/IiIiIiLmRqGAiIiItBomk4lTp06RnZ1NTk4OHTt2xM/Pj969e+Pq6tpuxmHdunW4u7szfPhwY1tSUpLxSemCgoLvtR8vLy/jU9Ljx4//we3avXs3Hh4e+Pr6AtDY2MiuXbs4fPgwI0eOJDAwUCexiIiIiIhIK6dQQERERFolk8nEiRMnjIDAwcEBf39//Pz82nxAkJuby5YtW/Dz8+P9998nJSWF8vLyH7RPZ2dnhg0bdsNrqldUVLB69WpsbGx4+OGHsbOz47PPPqO0tJT7778fT09PnbQiIiIiIiJmQKGAiIiItHqNjY0UFBSQnZ1Nbm4ujo6O+Pv706tXL7MsyHstycnJ/OpXvzLqLTTn4+2Dr09PAHr6Nn21tbUFoKamBoCc3BwAcvNyyMvPu2wfw4YNIzo6+rrCgY0bN3Lq1CkAXFxcqKurw87OjvDwcBwdHXWSioiIiIiImAmFAiIiImJWGhoaOH78ONnZ2Rw/fhwXFxcjIDD3i9P5+fksWbKE+Ph4Y1vHjh3p2ycQXx9f+gYEYmdrd137rK6p5vA3meTm5XL4SCa1tbXGfZGRkcydO9dYi/1qTp48yaZNm1psc3BwICoqCmtra52UIiIiIiIiZkShgIiIiJit+vp68vLyyM7OJj8/n06dOhkBwXcV6W2N4uLiWLx4cYtlgkKHDGPM6LHXHQRcTXVNNdt2fE7qVynGNmdnZ1555RWmTp161eclJCRw9uzZy7bffffdLeoeiIiIiIiISOunUEBERETahLq6OnJzc8nOzqagoICuXbvi7++Pv78/dnZ2rbrtsbGxxMbGGreD+4cwZvRY7nK965a83rnSc2zb8Tn7D6Yb26Kjo4mOjr7ssd988w3bt2+/6r7uvfdeAgICdALKdUtLS2P+/Pns2bMHOzs7oqKieO2113BwcOD48ePExcUxf/58DZSIiIiIyE2mUEBERETa5C84l2YQnDhxgm7duhlFijt27Nhq2llWVsbixYuN5YI6duzI9MhHjZoBt9rhI5ms/3idsaRQZGQkr7zyCi4uLsY4xsXFUVVVddlzHR0d8fX1JSAggM6dO+ukk+tSVVVFjx49+O1vf8vTTz9NSUkJTz/9NB4eHixfvpydO3cSERFBYWHhde23sbERS0tLDbCIiIiIyDX+Zv4uCgVERETE7H/ZycnJITs7m1OnTtG9e3f8/f3p2bPnHf3dpqysjKioKDIyMgDo5taNSY9Mxr1b99vajsKiU6yO+4Cy8jIAgoKCWLNmDS4uLmzZsoXs7GzjsZ06dSIgIABfX1+cnJx0csl1KSkpYcCAASQnJ1NZWcndd99NVVWVUSg7PT2d//u//yMsLIyXXnqJ0tJSunbtSkpKCi4uLjz77LNs2rSJLl26EB0dzZw5cwB47LHHKC8v59ChQ0yYMIHY2Fj+3//7f6xYsQJLS0tmz57Nq6++CkBtbS3PP/88cXFxeHl5MXr0aPLy8ti0aRMmk4nFixfzl7/8hY4dO/Lcc8/x0ksv6cCJiIiISJv8O/m7qHKciIiImDUbGxsCAgIICAigtraWY8eOkZWVxc6dO/H09MTf3x9fX186dOhwW9u1ePFiIxDw8fZh2tRHb1rtgOvh3q07zzz9cz6M+4C8/DwyMjJYvHgx06ZNIzs7GxcXF4KDg/H09FQQID9I586d+cc//oGnpycAAwcOZPr06SxcuJDg4GBCQkJYtmwZAIGBgS1mCvz4xz/G1taWEydOcPz4cR566CE8PDyYMmUKAEeOHOGTTz7B39+fRYsWsX//fvLy8jh9+jQDBw5k7NixjBs3jtjYWHbu3MmePXuAptkx3bs3BXFvvfUWCQkJpKen09DQwJgxYxg0aBDjx4/XwRMRERGRdkUzBURERKRNqq6uJjs7m+zsbEpKSvD29sbf358ePXrc8oAgJiaGJUuWAE2BwJMzZ7WKMVm+Yil5+XlAU62Al19+mb59++pkkVuisrKSN954g/feew8bGxt+9rOfMWfOHKysrFosH3T27Fm6dOlCXl4e3t7eACxZsoSkpCQ+/fRTHnvsMXr16sXChQsByMnJwcHBATc3NwDuv/9+Jk2axLPPPktoaCjPPPMMTz75pLGff//732zatIkhQ4YwZ84cZs6cCTQFd0VFRfz1r3/VwRIRERGRNuVaMwW0IKeIiIi0SXZ2dtx9991MnDiR6dOn4+7uzoEDB1ixYgVJSUlkZ2dTX19/0183Li7OCAS6uXVj2tRHW82YTJv6KC7OTfUEtm/fzoEDB3SiyC3j4ODAwoULycnJYdmyZaxatYrZs2df9riioiKsrKyMQADA19eX48ePX3G/zs7OLFiwgIEDBxIcHMyXX36JyWQy9uXj43PF5xUUFPD0009ja2uLra0tv/vd7667poGIiIiISFugUEBERETaPHt7ewYMGMCkSZOIiorCzc2N9PR0VqxYwb///W9ycnJoaGj4wa+Tn5/P4sWLgaaiwpMemXxHlgy6GjtbO6ZPfdQoxrx48WLy8/N1gshNt379el555RXj9ogRI/jDH/5gFN1uztvbm/r6ek6ePGlsy8nJwd3d/Yr7fu6557CxsSE1NZX9+/czfPhw4z4PDw9OnDhxxed5enoSHx9PTU0NNTU11NbWsnbtWh0sEREREWl3FAqIiIhIu+Lo6EhISAhTpkwhIiKCTp06kZaWxooVK/j88885fvw4jY2NN7TvRYsWUV5eDsD0yEdve1Hh78O9W3cmPTIZgPLycmJjY3VSyE1hMpn49NNPaWhowN/fn5iYGFauXMn58+c5ffo0K1asICQkBGiaRVBWVkZxcTH29vZERETw4osvUllZyZEjR3jzzTd54oknrvg6hYWFWFpaUl1dzWeffcaePXuMWT9Tpkzhj3/8I3l5eWRnZ/Pee+8Zz5sxYwaLFi0iPz+fyspK/vSnP7F582YdOBERERFpdxQKiIiISLvl7OzMoEGDiIyMZNKkSbi4uJCamsqKFSvYvn07BQUFxrIk15KcnExSUhIAwf1D8PXp2Wr73bdPIMH9my7OJiQkkJycrJNBfrAzZ87w5JNPkp+fz4ABA/jkk094++23cXNzo2/fvlRUVLBq1SoABgwYwPDhw+nZsydZWVm8/fbbWFpa4uvry5gxY3j22Wd57LHHrvg6b7zxBlu3bsXLy4u//vWvhIeHG2HcnDlzGDZsGCEhIUycOJE+ffoYz3v++ef5r//6L4YPH467uzu7du3innvu0YETERERkXZHhYZFREREvqW0tJSsrCyOHTtGbW0tPXv2xN/fn+7du2NhYXHF50ydOpWUlBQAfvFsNHe53tWq+3iu9Bxv/rVplsCwYcOIi4vTgRezZzKZMJlMWFo2ffbp17/+NadOnWL58uUaHBERERFpN1RoWEREROQ6ubq6MmTIEKKiopgwYQI2NjZs376dVatWsWvXrsuKkyYnJxuBQOiQYa0+EAC4y/UuQocMAyAlJUWzBaRNeP/994mOjqa2tpasrCxWrFhBeHi4BkZEREREpBlrDYGIiIjI1XXp0oUuXboQGhpKcXEx2dnZbNmyBQA/Pz/8/f1ZunQp0FRceMzosWbTtzGjx5J+YB+1tbUsXbqUsLAwHXAxaw899BCbN2/G3d0de3t7Zs+ezbRp0zQwIiIiIiLNaPkgERERkRtQWFhIdnY2x44d43/+53+orq4muH+IUcTXXKz/eB37D6bj7OzM119/rQMrIiIiIiJi5rR8kIiIiMgt4O7uzogRI3Bzc6O6uhoAXx9fs+vHpTaXl5eTmJioAysiIiIiItLGKRQQERER+QGar8XfNyDQ7NrfvM2qKyAiIiIiItL2KRQQERER+QGSkpIA8PH2wc7Wzuzab2drh4+3T4u+iIiIiIiISNulUEBERETkBygoKADA16en2fbhUtsv9UVERERERETaLoUCIiIiIjeorKxMfRIRERERERGzolBARERE5AZlZGQY3/f0Nd+ZAs3b3rxPIiIiIiIi0vYoFBARERERERERERERaScUCoiIiIjcoPz8fON7W1tbs+1H87Y375OIiIiIiIi0PQoFRERERERERERERETaCYUCIiIiIjfI29vb+L6mpsZs+9G87c37JCIiIiIiIm2PQgERERERERERERERkXZCoYCIiIjIDQoKCjK+z8nNMdt+NG978z6JiIiIiIhI26NQQEREROQGubi4qE8iIiIiIiJiVhQKiIiIiPwAnp6eAOTmme9MgUttv9QXERERERERabsUCoiIiIj8AOHh4QDk5edRXVNtdu2vrqkmLz+vRV9ERERERESk7VIoICIiIvIDhIWFGd8f/ibT7NrfvM0KBURERERERNo+hQIiIiIiP0B4eDhOTk4A5Oblml37L7XZycmpRcAhIiIiIiIibZNCAREREZEf6NLF9G+yDpvVEkLVNdV8k3W4RR9ERERERESkbVMoICIiIvIDzZo1C4Camhq27fjcbNq9bcfn1NTUtOiDiIiIiIiItG0KBURERER+oLCwMEJDQwFI/SqFc6XnWn2bz5WeI/WrFABCQ0M1U0BERERERKSdUCggIiIichPMmzfP+N4cZgs0b2PztouIiIiIiEjbplBARERE5CYICwsjIiICgP0H0zl8JLPVtvXwkUz2H0wHICIiQrMERERERERE2hGFAiIiIiI3SXR0NE5OTgB8tHE9hUWnWl0bC4tO8dHG9QA4OTkRHR2tAyciIiIiItKOKBQQERERuUm8vb1ZsGAB0FR0eP3G9VTXVLea9lXXVPNh/GqjuHBMTAze3t46cCIiIiIiIu2IQgERERGRm2jq1KnMnTsXgKKiQj6MX91q2vbuquWUlpUCMHfuXMLDw3XARERERERE2hmFAiIiIiI32bx584z6AnnHc1m+ctkdnTFQXVPN8pXLKCoqBJrqCKi4sIiIiIiISPtkUVtba7p0w8bGRiMiIiIichOUlZUxdepUMjObCg536+bOpIcn4d6t+21tR2HRKT6MX23MEAgMDCQuLg4XFxcdJBERERERkTbowoUL33m/ZgqIiIiI3AIuLi7ExcUZMwaKigp5d9VycvNyblsbDh/JbLFkUEREhAIBERERERGRdk4zBURERERusZiYGJYsWWLcDhkwkHtHjeEu17tuyeudKz3H9i+2kX5gn7Ft7ty5WjJIRERERESkHbjWTAGFAiIiIiK3QVxcHIsWLaKiosLYNmxoGPeOGoOdrd1NeY3qmmq2f7GNlC+TjW1OTk7ExMSoqLCIiIiIiEg7oVBAREREpJXIz88nNjaWhIQEY5utrS19+wTi6+NLQJ++1x0QVNdU882Rw+Tm5XL4SCY1NTXGfREREURHR+Pt7a3BFxERERERaScUCoiIiIi0MsnJycTExJCamnrZfT49fOnp2xMAX5+mr7YdOwJQU1sLYNQlyMnNIe947mX7CA0NZd68eYSFhWmwRURERERE2hmFAiIiIiKtVHJyMkuXLiU5ObnFskI3wsnJibCwMGbNmqUwQEREREREpB1TKCAiIiJiBhITE0lOTiYxMZETJ058r+d4enoSHh5OeHi4ggAREREREREBFAqIiIiImKWysjIyMjKAploEgFEbICgoCBcXFw2SiIiIiIiIXEahgIiIiIiIiIiIiIhIO3GtUMBSQyQiIiIiIiIiIiIi0j4oFBARERERERERERERaScUCoiIiIiIiIiIiIiItBMKBURERERERERERERE2gmFAiIiIiI3ICUlBQsLCywsLLC0tMTd3Z2f/OQnnD17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" - } - }, "cell_type": "markdown", "metadata": {}, "source": [ - "![Screenshot from 2023-08-31 13-12-54.png]()" + "![Screenshot](https://github.com/karmenrabar/pygraphistry_images/blob/main/memgraphlab.png?raw=true)" ] }, { @@ -346,15 +340,10 @@ ] }, { - "attachments": { - "Screenshot from 2023-08-31 18-48-56.png": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABpoAAAIkCAYAAAAOFfh4AAAAinpUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjaVY7bDYVACET/qcIShsfCUo4xmtjBLf+yrol6PmAgZAbaf+dBy4BhZC26pzsKS0tZS3RMFGABj151cnflUlLjPZPKFJ49YM+hGT409e5HWIQ333yTcpdd9aqsoOtqmI1X8jHKFT6jv3sxyDuA/v3ZLRsB5QoSAAAKCGlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iWE1QIENvcmUgNC40LjAtRXhpdjIiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIgogICAgeG1sbnM6ZXhpZj0iaHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8iCiAgICB4bWxuczp0aWZmPSJodHRwOi8vbnMuYWRvYmUuY29tL3RpZmYvMS4wLyIKICAgZXhpZjpQaXhlbFhEaW1lbnNpb249IjE2OTAiCiAgIGV4aWY6UGl4ZWxZRGltZW5zaW9uPSI1NDgiCiAgIHRpZmY6SW1hZ2VXaWR0aD0iMTY5MCIKICAgdGlmZjpJbWFnZUhlaWdodD0iNTQ4IgogICB0aWZmOk9yaWVudGF0aW9uPSIxIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg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wYV8gfn5BbZu3YLneXR0tLNlaJCBgX6SyQQAzWYTx3HIZNrItmcZHRlT1kVERERERK4CDpY5kyKc7aMeaWuhyCz5qRGytRlSVitriIiIiIispSsqNAHklnIcP3aCIAiIRqKEQh5BEDA/t8Dzzx3hq195mEwmTTabufhndPSMMi8iIiIiInIVWHSSJKIRSqktLROT16xgzz9Lj79I1DbUSSIiIiIia8gcOniHXfOTGMODb7uP7du3AnDkhRf51jcfU/ZFREREREQ2ufNuB3Pt+/B33w+YlogpXJqhcfTL7G+cUaFJRERERGSNOetxEmstX/3y1zkzdhaA9vasMi8iIiIiInIVSNsykdIMxrbOfrxufpy4rRKxTXWQiIiIiMga89brREEQ8OUv/SPbt29lampamRcREREREbkKJIMqNCpkZ55noeemDY/H9WvY2RF6/EUMVh0kIiIiIrLGnPU8WRAEnD49SqlUVuZFRERERESuiptKS6efY2FyBFMvbXg80fmTUMnTFui+U0RERERkfe4JRERERERERK6A8RvQKGFzkxsei50+QUdQwNFsJhERERGRdeEpBSIiIiIiIrISVcJMeFmq4QxOxzBBunfDYjFY+s98laXKEl1BTp0jIiIiIrJOVGgSERERERGRy2Ix5Jw4570u2tLtVLfcRhDv2dCYormzTM5MsaM5j0ugThIRERERWScqNImIiIiIiMhlmXHbGPe66RzYwWzvLVizsauyRyvzVEaeZMCfJ221N5OIiIiIyHpSoUlERERERETekADDtJOlGmmjbeggsx17ALOhMUVqi9ROP0pnbYYeX0vmiYiIiIisNxWaRERERERE5HVZDHNOmkIkg7PtTnLZnRseUzh/ntrok3RWp9gSzGGw6igRERERkXWmQpOIiIiIiIi8rpyTIBfKwPBd5DLDGx/Q9DHqZ79DT3OJgWBBRSYRERERkQ3iKAUiIiIiIiLyWup4jHsd2P7rKWS2t0ZQxsUBsraoIpOIiIiIyAZSoUlEREREREReVYDhnNdJNNNLqfdGNnpPpou6dpDtGuS820kTVx0lIiIiIrJBVGgSERERERGRV1UwcZacFNXBQwROC62+bhzy/YcohTMsOEl1lIiIiIjIBlGhSURERERERF6Rj8OUl6Gnbwu1WEfLxdcIJ2kf2M2sl6VhNKtJRERERGQjqNAk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tq8T63szOgaz6kKY2B/K2XW/JkVEREREZJ5QRZOIiIiIyCzb88A9k8crNl2qgMiMMwZc10wmekrjo2DtzPXfvg73gjdgAHN4Czz8XU2KiIiIiMg8oYomERERERGR01jrn30GB0NNJFh+wEDnd/9jRscQvPj3sQYMBu+3X9ekiIiIiIjMI6poEhEREREROY25lCuajKFczjTDgtf9JSZYTnL5d34avKImRURERERkHlGiSURERERkDrj1z94yeXzLZ29TQGRGuLEajONQFQ3wdJ7p0I++OHM3pA2LcJuXYjAweBjbd0CTIiIiIiIyz2jpPBERERERkdNUuLkdx3VxKFc02YnPMyVy019jKfdb/PG/akJEREREROYhVTSJiIiIiIictgwGCAddMAYHw/C2B2ek58hlb8cYgwOUfv01TYWIiIiIyDylRJOIiIiIyBxQKuQ5vPUxAAKhMIvWbVBQ5KRrufFPiQTLt4UGGN396Iz0G2g/g1DHRgzgde2gtP8xTYaIiIiIyDylpfNEREREREROU8ZAPOJiJ74+8P3/mpF+K65+N8aCMZbcz76giRARERERmcdU0SQiIiIiInIaar72FsIBB4ODg2F0zxNYr3TS+61+699P7gmVvu2jmggRERERkXlOiSYREREREZHTUPWa84lHAuU18wzs/dZnTnqfoaVn4cbrMAaKB5/CHx/WRIiIiIiIzHNKNImIiIiIzBF3fvoTk8fX/MUHFRA5yTeDBkz5ptDMQH+hxWcQv/oPMQZKh7aR/vmXNAkiIiIiIqcA7dEkIiIiIiJymlnz15+nIuLiANbAvu+d3H2SomdeQuVFvwvWkt12L+O//Y4mQURERETkFKFEk4iIiIiIyGkkWFmDcQzhwMQCFxYGtz54Uvusvvh38QGMUZJJREREROQUo6XzRERERERETiPRliXUxkLl5fIMbP74H57U/lr+9N8xxmAMDHz+vZoAEREREZFTjBJNIiIiIiJzyPZ77pw8XnvZNQqITLuWV12NccqJn/SRA1jfO2l9NbzhfTiBIMbAyHf/FesVNQEiIiIiIqcYJZpEREREROaQ/s59k8eNS5YrIDLt4gs7MNYAhkJqFKw9Kf00/s5fEW5dhsEw9L1PUejZr+CLiIiIiJyClGgSERERERE5TSy+6Y+pDDvggINlx//79EnrK7JgBcaAMZA/slfBFxERERE5RQUUAhERERERkdND3ZKVgMHB0v3Q3Selj3BTOy2/934Acv2H6f3mPyvwIiIiIiKnMCWaRERERETmkNTQwORxZV2DAiLTKpaoxxjAGga2PzLt7YebF9P25g+AteR6O+n95v9W0EVERERETnFaOk9EREREZA7p2b198rh1xVoFRKbNxg99kYADhvJydmOH9kx7Hwvf8gHKeSzDUSWZREREREROC6poEhEREREROcVVti4hFgpMVDPBtm/957S2H2lqZ9FbP4AFcv2H6L5NSSYRERERkdOFEk0iIiIiIiKnuPq15xIMuBggmxqhf/vD09Z2tGUJi976AbCQ6zlI923/pICLiIiIiJxGtHSeiIiIiIjIKW7RhdeWq5kMpI4cxPr+tLW9+G0fxFBu+7CSTCIiIiIipx0lmkRERERE5phb/+wtk8e3fPY2BUSmpOWCq4iEHIwp78/01Nf/bVrajTYvZs3ffAED5PsOsfuTf6Jgi4iIiIichrR0noiIiIiIyCls4foLcDBYYMu3PjctbVa0LGHp2z8EFjI9Bzj4P59QoEVERERETlOqaBIRERERETmF1S1ZARPVTD1PbZ6WNpe/80PlpfgcOKAkk4iIiIjIaU0VTSIiIiIiIqeoKz/+5fL+ScCT3/zslNuraFnMind8CAtkezvZ+9V/VJBFRERERE5zqmgSERERERE5BTWv3Ug4GARj6N/xGD1PPTjlNmtXb8Q4DgbD2K7HwFoFWkRERETkNKeKJhERERERkVPQxne8FwsYC4997d+m3N6GD/83WIsPHP7RFxne9qCCLCIiIiIiSjSJiIiIiIicapZc+NryHkoWDt7/sym1FWtdwpo/KC+Xt/97/4kFRnc+qiCLiIiIiAigRJOIiIiIiMisM8bgBIK4wRBuJEIgFMYEgziOizWA7+OXSniFHKVcHq+Yxy8Wsb7/ou21rj8fMBhj6dny8AmPq7JtKWtv+Vuw4GMZUYJJRERERESeR4kmERERERGR2eI4RKtridY3EI5X4wQDRKIxsj1HiCXqCVfEcIxDIZclMzpMuLmF3HiKUqlIMZMmNzhIZmQQv1h8TrN1S1ZiLIBhuHP3CQ1t04e/BK6LP7EN0yMf+wPNl4iIiIiIvIASTSIiIiIiIjMsGI0RbWgkWlNLNBqjZWkHdS0LqapvJByLEwyHcVwXMBPPsFjfp1Qskk+PMz4yxOhgH13bnyBbbCc3MkJ6oI98aoyb/vmr5acY+PH7335C41t3y4dw3AAWS6bnIFu/9DFNmoiIiIiIvCglmkRERERERGaIGwoTa2giWltHoqmFRSvW0rb6TELR2Cs802Acl2DYJRiOUJmop3nZSjo2bKL/4D56uvbT17kfnCC58TGiVbUc2foYvucd9xjX3/JB4gs7sAbGuw6y9Uv/oIkTEREREZGXpESTiIiIiIjIDAhX1VDTvpTqllZWrz+flhVrptymGwjS0rGalo7VJAf7GEmOs+Vn36b9rFfxyFc/ddztXfTRWzGmXMl0/z/8IdYraeJEREREROTl70taWxZ8VGEQEREREZk7fM+jcclyqptacdwAA4f2M9bXo8DMU8Z1ibcsoH75KpauXcdZl1xNTUvbtPcTrqikujbB4vXncXjrwxQLBQrjKaz/ylVNCza9hg3v/juMU16q78lb/5H86KAmT0REREREXpEqmkRERERERE4SJxCgqm0RVc2trD33QtrXbTip/Rkg7DqsOus8Ul2HcEIhRjv3U8pmXvI5jWs3suK6t5a/sIYnbv1Hkof3avJEREREROSYKNEkIiIiIiJyklQvWkptSysX3vxWKqoTM9ZvbcsCLv39P+ZXt/4HztIVDO3bhZfPveC8qz7xNbDgY7EW7v3oLfhaLk9ERERERI6DoxCIiIiIiIhML+O41LQvo7aplY2ved2MJpmeForGuPwP30N9UwuJJctxAs/9f4YXv/9TGAAD3Zvv5lcffoeSTCIiIiIictyUaBIREREREZlmFfUNVNQmOOvSq0m0Lpy1cYQiUTZe9waqa+upbl+KcV1WXft7XPO//4eKmnow0L/tUXb/5OuaNBEREREROSFaOk9ERERERGQahSrj1C5exroLLqFxacesj6eiqoZzb3gj9//k26y44neoW7YGa8vf+/kH3q4JExERERGRKVFFk4iIiIiIyDQxxlDVtoiGxhYWnblhzoyrqr6Jy972Z2SGe8iNDWMM3PvP79WEiYiIiIjIlKmiSUREREREZJpEEw1UNTSz5sIr59S4jDFEgy51za3suuubHH18syZLRERERESmhSqaREREREREpuPmKhikauEilq3fQLyuYU6OsTYexwkYAtGoJkxERERERKbnXkghEBERERERmbpwVQ3RqlqaF3fM2TE2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" - } - }, "cell_type": "markdown", "metadata": {}, "source": [ - "![Screenshot from 2023-08-31 18-48-56.png]()" + "![Screenshot](https://github.com/karmenrabar/pygraphistry_images/blob/main/allaccess.png?raw=true)" ] }, { @@ -411,15 +400,10 @@ ] }, { - "attachments": { - "Screenshot from 2023-08-31 18-50-30.png": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABogAAAIeCAYAAACIr41mAAAAi3pUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjaVY7LDcQwCETvVJESho8Bl7OKHGk72PKD40jZvAMMCM1A4/c9aJswjKxFendHYd26fEokFgqwgGevuri7cimp8Z5JZQnvGbDn0Awvmnr6ERbhzXffpdxlqF6VFXRdTbP5Sn+MMuEr+r3nAfkPoBP6Ry0hbWvXWQAACghpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSIxNjcyIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iNTQyIgogICB0aWZmOkltYWdlV2lkdGg9IjE2NzIiCiAgIHRpZmY6SW1hZ2VIZWlnaHQ9IjU0MiIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIvPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg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AABQMAIRAABAnXpk20M18769+1KpVCwGAAC4bAIRAAAAAABAwQhEAAAAAAAABSMQAQAA1KnNd9xeM3/n209YCgAAcEUIRAAAAAAAAAUjEAEAANShP/vWN2rmp5542lIAAIArRiACAAAAAAAoGIEIAACgzrS3tyelUs1rg4NDFgMAAFwxAhEAAECd6enpTulDgejgwT6LAQAArhiBCAAAAAAAoGAEIgAAgDqzprenZt67Z5+lAAAAV5RABAAAUGfuf2BrzfzE409ZCgAAcEUJRAAAAAAAAAUjEAEAANSRR7Y9VDPv27svlUrFYgAAgCtKIAIAAAAAACgYgQgAAKCO9PauqZmf/ckOSwEAAK44gQgAAKCOdHR21Mx9ff2WAgAAXHECEQAAAAAAQMEIRAAAAHVi2/aHa+bnd7xgKQAAwKwQiAAAAOrEpk0bauaB/gFLAQAAZoVABAAAAAAAUDACEQAAQB3YuHFDSuXaS7SdO3dbDAAAMCsEIgAAAAAAgIIRiAAAAAAAAApGIAIAAKgDX3lsW838R1/9uqUAAACzRiACAAAAAAAoGIEIAABgjj2y7aGaed/efalUKhYDAADMGoEIAAAAAACgYAQiAACAOdbZ2VEz9x3stxQAAGBWCUQAAABzbE3vmpr52Wd3WAoAADCrBCIAAAAAAICCEYgAAADm0KZNG2vm/r4BSwEAAGadQAQAADCHtm1/qGZ+fscLlgIAAMw6gQgAAAAAAKBgBCIAAIA5snHThpTKtZdlu3btthgAAGDWCUQAAAAAAAAFIxABAADMkfsf2FozP/H4U5YCAAD8/+zdd3Bc15nn/e+9twMa3QiNDBAAQSKQYCaYxCDmIMmWZY3kcfZ6duzdnXf23dnZ2vDuW7W771RtbZjd2Z0Ze4LtsT2eHaexbFmSbYkUcxYp5gQwA0QOjdRAo9O97x+gIEKASDCn36eKVei+5557znMPUH359DnngVCCSERERERE5CEpK5s86vXJE6cUFBEREREReSCUIBIREREREXkIvvyVL456ferkKWzbVmBEREREROSBUIJIRERERERERERERETkKaMEkYiIiIiIyEMwv2beqNff/97fKSgiIiIiIvLAKEEkIiIiIiIiIiIiIiLylFGCSERERERE5AH7k//9x6Ne/58f/FBBERERERGRB0oJIhERERERERERERERkaeM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" - } - }, "cell_type": "markdown", "metadata": {}, "source": [ - "![Screenshot from 2023-08-31 18-50-30.png]()" + "![Screenshot](https://github.com/karmenrabar/pygraphistry_images/blob/main/access3.png?raw=true)" ] }, { @@ -478,15 +462,10 @@ ] }, { - "attachments": { - "Screenshot from 2023-08-31 18-51-35.png": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABo4AAAIjCAYAAAA9VelSAAAAi3pUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjaVY7LDcQwCETvVJESho8Bl7OKHGk72PKD40jZvAMMCM1A4/c9aJswjKxFendHYd26fEokFgqwgGevuri7cimp8Z5JZQnvGbDn0Awvmnr6ERbhzXffpdxlqF6VFXRdTbP5Sn+McsBX9HsvCvkPoBP+QC0dO+foBQAACghpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSIxNjc4IgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iNTQ3IgogICB0aWZmOkltYWdlV2lkdGg9IjE2NzgiCiAgIHRpZmY6SW1hZ2VIZWlnaHQ9IjU0NyIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIvPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+71K/HQAAAARzQklUCAgICHwIZIgAACAASURBVHja7N1nkCTnfef5b2aWt13tq81Mu/EOY+AdQYAESRAgCXJPS3FXuqDEVdy9uLuQxBf34hQMbsRt3FGnuFVcrJZBinGUdNwVJUiisKRIAgNHeMxgvDftvSvvM/Ne9LDJIWaA8V3T8/tETExXVZqn/k9WZ3b98/88xu6d97mIiIiIiIiIiIiIiIjIHc9UCERERERERERERERERASUOBIREREREREREREREZELlDgSERERERERERERERERQIkjERERERERERERERERuUCJIxEREREREREREREREQGUOBIREREREREREREREZELlDgSERERERERERERERERQIkjERERERERERERERERuUCJIxEREREREREREREREQGUOBIREREREREREREREZELlDgSERERERERERERERERQIkjERERERERERERERERuUCJIxEREREREREREREREQGUOBIREREREREREREREZELlDgSERERERERERERERERQIkjERERERERERERERERuUCJIxEREREREREREREREQHAc70bME2TtvZWYrEotm2zML/IwsKiIisiIiIiIiIiIiIiInKbuebEkWEYbN+xhbvu2o4/4L/otfn5Bd55ex9jo+OKsIiIiIiIiIiIiIiIyG3C2L3zPvdqVzJNkyc/9Tjda7ouu4zrurz95rscOXJcURYREREREREREREREbkNXFPF0b337VlOGo2PT7DvvQPMzszh8XhYs7aLe+/dQzgS5r4H7mExlVblkYiIiIiIiIiIiIiIyG3gqhNHsViUrds2AzB4fogXX3gF13XZtn0Lpmly6OARJiem+PyzTxMOh7j3vj1KHImIiIiIiIiIiIiIiNwGzKtdoX+gD8MwsG2b13/xFq67NNJdLB6joSEOQD5f4J233wOgqamRxsaEIi0iIiIiIiIiIiIiIlLnrrriqLm5EYDpqRmKxRLrNwxgWRaNiQZM02TT5g04tsP584O/WqeliYWFRUVbRERERERERERERESkjl114sjj9QJQrlQAWL9+AI/XQzQSAcNgw8Z12LbN6dNncRwH0zTxXlhHRERERERERERERERE6tdVJ44KhQLA8rB0/+35nwLw4MP347EsXn3ldQCi0SimaV5Yp6hIi4iIiIiIiIiIiIiI1LmrnuNofGwCgESigc7Ojl+94LrL8x0BbNu+GQDHcZianFKkRURERERERERERERE6txVJ44Gzw+Ry+UB+PgTj9DUtDTn0fv7D/Lee+8DsGnzBrZs3QTA6VNnKRZLirSIiIiIiIiIiIiIiEidu+qh6mzb4dVXXuczT32SYDDIF774NOfODjI3O4fH62HNmm7a2luXlw8GA5imieM4iraIiIiIiIiIiIiIiEgdM3bvvM+9lhXX9qzh448/gtfrveTrtVoNj2cpLzU4OMyLP3/5oqHsREREREREREREREREpL5YHcmub1zLiulUmlMnT+M4DgF/AK/Xg+M4zM8tcOTwMfa++CoNDTESiYblf4ODw4q4iIiIiGFg+nx4A0G84TDeUARvIITH78e4cOONq2ptEREREREREVkB11xxdEUbNwye+ORj9PauBeDY0RO88frbirqIiIjccSyvD08oRCDWgC8awx+JguPgCwSxLiSL7FqNarGIa5qUc5mlf5k01UIep1pVEEVERERERETkpvPczI27rsveF17hE598jLU9a2hsTCjiIiIickcxPR4ibZ2E25J4fT6scpl19z1C+8BGgrE4BsZvXkBRSKeYPHuc02+9htO1FrtcJjc1QXZmErdWU1BFRERERERE5Ka5qRVHv2SaJr29a5mamiafLyjqIiIisupZXi+h1iThllaikTibHvo4Dc1tS8kiw7yibbiuSyG9wMzgWc4dfJdcIUdhdobc1ASOrQSSiIiIiIiIiNx4tyRxJCIiInIn8cfiNHT3Em1uoX/LTtZu34NpWde1TcexOb//bYZOHyU9M0VmdIhyNqNgi4iIiIiIiMgNpcSRiIiIyA0Uam0j3t1Le1sH937hKzd8+45j89YPv89cap708HkKczMKuoiIiIiIiIjcMFZHsusbCoOIiIjI9TEMiHWtobFnHTvuf4xNDz2OYZo3YT8m3Zu3E/D5yZdL2I5NNZdVB4iIiIiIiIjIDeFRCERERESuk2EQ7eiiuXc92x54lOTA5pu7O9OiZ8fd+IJhjni84LrkJsfVDyIiIiIiIiJy3ZQ4EhEREblOwcYmYp1r2Xb/zU8a/bqO9Ztx7BoHq2Xsconiwrw6Q0RERERERESuixJHIiIiItfBF47QsKaPzbsfILlu8y3ff9em7RSyaY6XSlSLRWrFgjpFRERERERERK6ZEkciIiIi18gwTaJda+ns38Da7btWrB39O+9l7PghKt1rWTx7Ctdx1DkiInL7nVctMH0mps/AChhLP5vgAm7Nxa64OGUXp+xgV13Q6U5ERETkplDiSEREROQahZpaCcfi7Hz8KSyPd8XaYXl9PPSvv8re/+87lBJzFOZn1TkiInLbsAIGoQ4fgQ4PnqCJYRj4gl4CYT+Lh8vgQHy7l3KuTLVcw3WgmrEpTFQpTtdwa66CKCIiInIDKXEkIiIicg0M0yTclqR3664VTRr9ki8QpHtgI4XMIsWFOVxXX6KJiEgdMyHQ4iHc5SPQ5CMSjLLm3iSReIRgUwB/2IvpMzFMA1xwHYda2aGcrZCbLZKeTjP89gSVdUVKc1Xyo1WqaVtxFREREbkBlDgSERERuQb+eIJgNErPtt110iKDdXseYGLoLNl4glJqQZ0kIiL1xwBvxCIy4COcCNLU0sS6T/SQ6Il+6DqGaeLzmPjCHqLtIZLbmtjw8V4mD80xcXaC2eF5MoNF8iNV7KLGsBMRERG5HkociYiIiFwtwyDS3sGadVsIRmN10yx/OEqybz2Z2RnK6RSuqy/ORESkvkTW+Iiu89Pa1cZdX9iEL3ztVbuGadCxs4WOnS2kx/Ps+7tDBJM5UoeKlBdVfSQiIiJyrZQ4EhEREblK3kCQQDRG54bNdde25mQX58NhTK8Xu1JWZ4mISF0wPQaxTQESvRE2PbSB5LZmTK95w7Yf7wzz8Nfu4czeYQbNQbKDJXJDFdDIrSIiIiJXTYkjERERkau9gAqF8YfChGINdde2xmQ3gXAEbzCkxJGIiNQFy2/QsCVI29Ymtjy2iYbuyE3Zjy/sYcvT/UTbwpx65zSGJ0vuXBkV4IqIiIhcHSWORERERK6SLxLFKOTxBYJ11zZ/OIJVreKNRimlF9VZIiKyokwL4lsCWOUAuz+3g0Dcd3N3aMCae9sJNfjZ7xzEtSF3XjdSiIiIiFyNG5I48vq8RMJhajWbfD6P4+h2HhEREVnFF1D+AO3rNgNGXbavc/0WFhdm1VEiIrLiohsCtGxIcPezd938pNGvad6QYKeznX3VQ9glh+JEVZ0hIiIicoWuK3GU7Ghn164ddHQmMYylL04qlQrnzg5y4P1D5HJ5RVhERERWFcMw8Pj9NHasqds2Jjq78fiDGKaJqxt6RERkhQSTXmLdITY/uolgInDL99+6sYn+oX5q2dNUMza1nM6JIiIiIlfimhNHd9+zm527tn/geZ/Px6bNGxhY18cLP3+ZsdFxRVlERERWD8PE8voINTTUbROD0TimzwemCUociYjICrACBvGNfvq299I8EF+hczZs+PRacrk0tcoEC+8XNN+RiIiIyBW4psTRtu1blpNG+XyewwePMjs7j8frobu7k81bNuH1evnEJx/jR//4YxYWNL6+iIiIrBKmgWGaeDy+um2i5fVimiYGBq56TEREVkBkrY9wIEbfI10r3paBB/pZmFwkN1yhPFtT54iIiIh8hKtOHAVDQfbcvROA2Zk5fvLjn1EuV5ZfHxsd59zZQZ56+km8Xi/3P3gvP37+p4q0iIiIrA6ui+u6uI5dv010HFzXBaWNRERkBXiiJuHuADuf3YQnaK14e+KdYbo2dJOfLTG/aOPUdH4UERER+TDm1a4w0N+L1+vFdV1e2vvqRUmjX5qZmeXdd/YD0NmZJBqLKtIiIiKyOjguTq1KqVC/czlWCnmcWhVX4/GIiMgKCCW9JDrixNfE6qZNa3d3EGjw4m0w1UEiIiIiH+Gqr5ha21sBmJmeJZ3OkGhsoKWl+aJ/TU2NnD17fnmd9rZWRVpERERWBdd1qFXKpKcn6raNqZkpaqUSrqM7qkVE5BYzwN/koa27DdMy6qZZwcYA8VgjgXav+khERETkI1z1UHV+39J4/oViEYAvPPs0Hs/Fm3Ech7/8zl/hOA6maeIP+BVpERERWTXscpnpc6cZuPuhumzfzPnT1IpFcJU4EhGRW8sXtwg1BOm+J1lX7TJM6HtwDXNTs2Q8ZVwNVyciIiJyWVedOCpeSBjFokvDz33vu399yeXC4TCmaV5Yp6RIi4iIyKpRKeSpeizsWhXLU193Ltu1KkXHppLPqqNEROSWC7R6iLXE8IU9dde21k2NeD0+fHGL8nxNnSUiIiJyGVc9VN3E+BQATc2NtLVffgi6rds2AeC6LlOTU4q0iIiIrBrVfI58epFiNl13bcvMTJFPzVPN59RRIiJySxkm+BIWresS9dk+A+KdUc1zJCIiIvIRrvpq6fz5IQqFAgBPPPExEomGDyyzYeM6tu/YurT8uUHy+YIiLSIiIqtGrVigWiqyOFV/8xzNjJynWi5Tq5TVUSIicksZHgMraOK1AnXbxmh7GG/EAkP9JSIiInI5V107Xq1Wee3VN3nyU48TjoR59kvPcP78EHMzc3i8Hrq6O0km25eXd10wDANXY+yLiIjIKuHUauRmpjj22gt0b9y2dAtznRg7fZTC7AyubaujRETkljJMA8M0CDTU8TzHeQ+W38AwNBWgiIiIyOVcU332yPAoe198hWq1hmVZrFvXz/0P3svd9+xeThrVakvjBQ+s6+PRjz2EYeh2HhEREVk9inMzlB2b0WMH66ZNQ4feI5fLUpjVMMEiInLrGebSvRTegFW3bfRHvBgefT8hIiIi8mGuebbK8+eGmJyYZvuOLaxZ00UsHsN1XRYXUpw7N8iJ46f42GMP0dvXw/oNAwC8+srrqjwSERGRVcGuVinOzzJ86ihdm3dgmOYKt6fC8JkT5GdncGqa8FtERFbAhXyMYdXvHEKmx0T3tYqIiIh8OM/1rFwsFnnn7X288/a+S77+4guv8MQnH6O3dy3rNwxQLpd56813FXURERG5/bkumYlR5hoaOf32q2x44LEVbc7Z/W+zOD1BfmZSfSMiIit0brzwn+3UbROdmqMh6kREREQ+wk29Dch1Xfa+8ArDQyMANLc0K+IiIiKyajjVKpmJEQZPH2NhYnTF2jEzdJZzJw6RHj6PXamoY0REZEW4ztK8QbVi/c6zV8lWcW1ljkREREQ+jOdm78BxHF74+cv09q5lampGERcREZFVpTg/R2pilPdffJ4HPvdlQvHELd1/Pr3IkTf2kpkYpZReVIeIiMiKcR0X13UpZsr128hIDbvkqupIRERE5EPckoGHHcfh3LlB8vm8Ii4iIiKrius4pIfPk56bZd8Lz+Ny676JsmtV3nvhn1mYHCc1dA7XcdQhIiKycufEmotTdKjY9Zs4Sk/lqeUcUOJIRERE5LJMhUBERETk+riOw8K5k8yMnOMXf/d98qmFm77PzNw0r//jD5gbOk9q8Iw6QURE6uB8COUFp27nOHIdl8xYlkqqps4SERER+RAehUBERETk+tmVCovnzuDaDr/4+79ix6NPkly36Ybvx3VdJk8f5+ibL5Gem2Zx8CxOtaoOEBGRFdW0roG19yfpebyDqZNTddnG2VMpqnaFaloVuiIiIiIfRokjERERkRvErpSZP3OCWncP+1/9Ge0nDrPzM89iebw3ZPu1SpkDP/snpqcnyYyPkBkbVtBFRGTFfeW5zxKM+5cfH/3p8fprpAvH/vkcpfkqTk3j1ImIiIh8GCWORERERG4k1yU9OkQ5naKcS5P62+/R2tbJ+vsfJRCOXtMmS7k0J994henJUbILc2THR6hkM4q1iIismIf/eDfJ7c3EOiIfeK2lr5ljL5xm08fXYVpGXbQ3P1+i4stRmtYwdSIiIiIfRYkjERERkRvNdSmlFylnMxTmZknPTjM1MUJzeyfJgU1E4g0EonE8Ph8Gxm+s6lKrlClm0xSyGcZPHWV+dors/CyF2WkKczO4jobYERGRldG0roEv/MXjl3ytmCozdXiWd//8GM27oyQH2mnsjdVFu0cPTFBcLFNZtNWJIiIiIh/B2L3zPtVoi4iIiNzUKy4DfyRKuK2DQLwBwzLxhyK0tXaw/RNPLy/2+t98m9TMFP7WdsqFHK5tU0ynyE9PUMlnQVdtIiKygv7Nc58l8GtD0v26N/78ACf++fzy49jGAJFghE/97w9hec0VbXd2ssAb//VdZt5JU55T4khERETko6jiSERERORmc13K2QzlXBbL48X0+fAGgrjryxiv78WuVcGA8aGz2NUKtekJaqUSdrWMU6uBq4yRiIisnA2f6eXhP9z1gedP/csgY+9NM/LWJHb14mrY/HCF4L0lht8bp++B7hVt/4kXz5CfLFJZUNJIRERE5EoocSQiIiJyq7gudrWCXa1QzecYfOtVPvnV/2n55fU77+ev/+iripOIiNSF3kc6efiPd+MLeS96fvC1MV770/1UC5efL8guOmROlzlhnyXeHqep79YPWec6cPbFYWZGZ8meKeNqpFcRERGRK6LEkYiIiIiIiIhc5MH/eSebnu77wPNv/McDnHj+/BVtozhRxd9Q4NCPD/PIv7sfj9+6pe9h/myKMwfOkjpRpJpV1khERETkSilxJCIiIiIiIiIAJHpifP4/P47luXheor3ffPuSQ9J9lMyZCqY/x1t/vZ/dn99OqDlwS97H1JF5Dv7LEdLnipSmq+pYERERkaugxJGIiIiIiIiI8On/4yE6d7dd9NzwWxMc/9F5xvdNX9M2napL+lgRw1pg3z8c4q6nthDrjNy09+A6LuMHZzn68+NkRgvkByuaKlBERETkKilxJCIiIiIiInIH69jZwme+9cgHnv/uE8/dkO3bFZeF/UWcqsvr8++y++m7aNvaeFPey6kXBzl/6DzpsyVyQxV1roiIiMg1sDqSXd9QGERERERWRj61QM+OuwEwLYvB99+mlMsoMCIicktseKqXJ/7kvouee+1P9/POfz5MOXtjh3grz9vYNYfF1DwLZ7LE2yL4o77r3q7rwPSxeQ7/5DgjR8dZPFSkOF0FVRqJiIiIXJMbWnFkWRbgYtuadFJERETkSgwf2veraymPl0A0BpOKi4iI3FzBBj//3V8/iTfoXX5u7swiw29OcvqnQzdln27NJT9coZqyKfWXWZxZoHtrJx2b2oh3RTC95lVtr1qoMX8uzfD+MWZGZyjOVMkNlrFLyhiJiIiIXI8bljhqamrk2S89g+O4/OBv/pZisaToioiIiIiIiNShr/z9Zz/w3D/9Dy/dkn1X0jbz7xfJNlYoZsoMHhkiEo3Q3ptkzb1JQo3+y67rupAey3H+5VHmU7OUC2VK0zUyZ0pKGImIiIjcIDcscRQI+DEMA8sy8Pn9ShyJiIiIiIiI1JlHvr6b9U/2XPTcj//wVSYPz93ytlQWbObfK+AJm2SbyiyMpDn9zln8YR+BqA9vyIPltXBdl1q5RrVQo5ip4Dg1SgtVynM1Kos2taKjYelEREREbqDrThwFAgHuvX8PfX2/uvD8/Oef4vjxk+zfdxDH0bB1IiIiIiIiIitt1+9uvihpNPzmBMd/dG5Fkka/5Nou1YxNNWOTG6xgBQw8IRMraGL6DAwLcA0c28Upu9gFh1rBwakqUyQiIiJys5jXs3I4HOaL/+pzxKJRDh44vPz8vn0H6Ovv5VOffgLDMBRlERERkcsoZlJMnjm+/PiZr/97BUVERG64B/+Xnez6t5uWH08cmOGFP3mL8f0zddVOu+RSXrApjFfJDVbInq2QPVcmP1ShOFmlkraVNBIRERG5ya6r4ujjTzzCzMwsL/78ZULhEB2dSVzH5fSpM5w7O8izX3qG7Tu2cujgEUVaRERERERE5BZL9Mb4/F88juX51X2je7/5NoOvjSs4IiIiInJJ15Q4amxMEG+I097exs9/uhfTNMnn8vz4+Z8tL+Pz+Th3dpDNWzYwMT5JoVgkn8sr4iIiIiIiIiK3QPO6Bj7/F48vPy6mSvzw3/6MarGm4IiIiIjIZVkdya5vXM0Kra0tPPulZ+jv78UwDAbW9QEuExNTFy33hS8+TU/PGvx+P5s2b6C/r4cjh48p4iIiIiK/4dQbL7Pnmd9afhyIxhg98r4CIyIi1+W3f/jURY+//9kf4dQ0D7GIiIiIfDiPQiAiIiIiIiKyejz+J/ey5oGO5cevfWsfk4fnFBgRERERuSJXnTianZ3j73/4T8Qb4jzxiY/xs5/uZXxs4gPL/fj5n7J12xb6+tfy4s9foVAoKtoiIiIiIiIiN1nvI13LP+///nFO/2xYQRERERGRK3bViSPXdVlYWGRhYZGpqWnWre9ndGSMcCTMo48+iOu6vPjCK9RqNfr6ezh+7CSzs7qzSURE5E5kGAam14cV8OPx+sE0wXWxa1WccplapYxr2woU8Pp/+S4Pffn3Adj62Kd54wffVVBERFYrCzxBE0/YxBMwMf0GGOA64FZdqkUXO2dTK7rguNd+bvm/3+fkfxtUvEVERETkqlzXUHUv732Nz33hs3zmqU8yNjZOV3cnAOvW97N122bSqTSHDx1VlEVERO4QhgHecJRAPIEvGsfj9+MPR3DLJbw+Px6fH8exqeTzWNEYpVyGWqlEtZinlFqgnEnj1DRht4iIrDImeKMWgWYPvoSFN2wRbPDjpC2CTQH8ES+mZeDUXCr5KoX5EsVsCSPiUM3bVBZqlOdtalkb9zJTFHXsauUz/+fDy49f+9Y+VRqJiIiIyDW5rsRRLpfnub/7Effdfzd77t61/Pzd9+zm+LGT7N93ANd1FWUREZFVzrQsfNE44dZ2wokmYs2txOMJWrp7aWjvxB8OYxjmr63hUqtUyM7PMjt0jlwhy+zIIMV8ltzMNMWFOexySYEVEZHb+/zoNfAlLEKdXnwhP429USKBOK1bGkl0RfHFfJiW8YH1XNullKmQGs0yfWyefDnLwmCaSqFCYbxKZcHGqf3qb+3mdQ0XJY32/7/HlDQSERERkWtm7N553w3J7HR2dfDUZ58E4G//63OkUxlFV0RE5A7gDYVJ9Azgi8Xo7Oph00NPEAhHrno7Li7DB/dx/tQRsotzZEdHyM1M3lGx/IPvPLf8cyGT5q//6Ks6wEREblP+hIfoeh/BJh9N8TY2f76fcFPgmrdXXChz+LkzpHKzFObLZE6VqSwuDff6+y9+cXk5DU8nIiIiItfLc6M2VCqWcF0Xx3GolCuKrIiIyCpnejxE2juJJbvo6t9A/113E2lswTCMa9qegUHPXXfTuXErk4NnOP7WywQam0iNnKdWKCjgIiJyW7ACBpF+P/6gj9493fTe302kNcRFhbfXINjo557f20p2Ks/wvnGGPKPgGHz8jx9YXubVb+3jjCqNREREROR6r2k7kl3fuBEbKhaLHDp4hIMHDlOpVBVZERGR1XwB4fOR6Oknkexi+4MfZ/09D+MPha85aXTRtj1e4i1tJHvXY9s2NdfBrpSplYqrPq4ta/tpaO8AwHEcTr6+l1qlrANOROQ24WuwiG8I0Lqpibue2kL/I934I15uwOkRWJpL0B/10bahiXhLHG/MZPTQGJGmMMf/4RzH/vHcDdiHgd/vJxgMYBgmtm2rY0VERETuMDcscQTguq7mNBIREVnlDI+H5g1baelay6Nf/j0a2jpuyn58gSDJvvVUMinKhkGtVKJWXN2VR+Mnj7Ljyc8B4PF6GTn8Prn5WR10IiK3AX+Th8T2IC2t7Tz0B7uItIVu6v4irSHa1rXQtb2LvX/+KmNvzFArOte93VAoxIaN69m9exc+r4+pqSl1roiIiMgdxqMQiIiIyJWy/H6aBjbS0buOux7/DB6P96buzzAMtj/xNOEDb3McFxeX0sK8OkJEROpKoNVDYkuQ/p39rH+iB8M0btm+DVwGdvVzzhxk8UiB0mzt+t5LwE9fXy/33X8vHsvDgQMHV22/RaMRent7sR2bUydPU6vVdDCLiIiIAKZCICIiIlfCMC0aunppSnaz6xNPEwhHb81+DYOBXfexbusuGtf244tEV22Mi5kUk2eOLz9+5uvf1IEnIlLn/E0eGjaF6N3az4ZP9mL5bv2f2Rs/1c/Arj4S20L4Gi11yhVqbWvlkUcfYtu2rXi9XgVERERE5AJVHImIiMgViSQ7iDQ28cDnv4zXH7jFezfY+MBjzAyepbqml9njRwANjysiIivL9BnEN/pp70yy5Zk+uEWFRgEjSMJqwsUlZS9SMgts/+QmWuwOKuvKDL4zweljZ6lWq7S0NNM/0E8mk+H8uUFc1yWZbKezs4NgKEi1WmN6apqRkVEKhcsPCevz+Whrb6Orq5NQKEStVmN+bp7R0VHS6cxSPEyTRKKBNWvW0JBowLIsSqUS01PTjI2NUywuzVfY3NLMQH8f+UKB+bkFWlqbaW5uplgoMDQ0TCaTZe3aNbS1tVKpVBgZGWVsbBzHcTAMg4aGOGt71pJINGCaJtlsjtGRUaampq9o+Hy/309nVye7d++ira0Ny7LYs2cXQ8MjjI6MEggE6OhIkuxIEgwGsG2HdDrN2OgYs7NzGqJfREREVr0bOseRiIiIrE7eUJhE7wB3fexTJG7SnEYfxTAMOtZtZm5shJpdpZxJr8pYn3rjZfY881vLjwPRGKNH3tdBKCJSZwyPQcO2IMmBNnZ9eTOW99ZVGoXMyZU9ogAAIABJREFUCN3eHhqtVopunryTI+5JcPfmu+kO9RJs8DF0YoRSqcTGTRt48slPYBgGIyOj9Pb18MijDzEw0E8sFqWjs4OenrU4jsPc3Bxer5eenrV0dXcxMT7B8eMn8Pl89A/08cgjD7N58yZaW1vo7Oygp3ctHo+XxYVFyuUyyY4kDz54P7t276S7u4uW1hZ6e3vo7Fy6dlhYWKRardLb28MnPvk4HR1JYrEYW7ZsZsuWTfT29RAIBIjGouzes5NNmzYyMNCPP+Bnbm6eXC5HS0sz9913L7t376StrZVEopHe3h4aGhrIZDJkMpmPjF8wGGTjxvXcddcOIpEwlsfC7w+Qy2ZJpdNs3rKJhx9+kA0b1tPSsvRe+/p6icVi5PN50um0PgAiIiKyqqniSERERD6UYZrEutbS2tZB5/otK9oWXzDEwPZd5LKLFGZnqBYLqzLmc6ODNHf3AhBvbddBKCJSh4JtHoJRH5s/sQ5v8Nb+aV1xK5TcEnGrkbAZwcAgaIYIesN4umu0ZVvp3pyksK9Ic1MTXp+XXC5HJBphz57dJDuSHDl8lGPHjtPW1sbdd+9m5867mJ2ZZXFx8QP7a2puYs+e3fT2ruX06bMcPXKMRGOCXbvuYufOHaQWU5w5c4Zt27YsbWd2lrfefJt8Pk9vXy87dmzn7nv2kE5nOH78BKZp4vP5CIVCZDJZjh45ytxcO9t3bGPz5k2EQiHOnD7LxMQkO3Zsp7evh+HhERYXF1m3boDde3YxP7/AW2++RbFYYuu2LWzavJF8Ic/09AyVSuVD41culxkZGaV7ZJSGhjhzs3Ps3/c+ExOTdHZ08MAD95NINHDixElOnzpDOBJm+/Zt7NixnVqtxtzcPPl8Xh8CERERWbU0x5GIiIh8KH8kRijRyNaPPYlhGCvenq7NdxE0PYTbO1ZtzE++vnf55+4tO3UQiojUGU/AJNofYOPD64l3R275/qtumYKTwzIsAkYIvxEgZESouVVKwRxta1pZu72TeHOUpqZGspksMzOzNDc10dOzllKxxOJiinK5QjaTJZ3O0NLSxJq13ZfcX0tLM729PaTTGY4ePcbBg4fY995+XnzhJfa9t59UKkVjUyO9vb24rsOZM+d49919HDp0hAMHDjE1NUVLy1L1kWEYuBeGm61Wq4wMj/DGG29y9MgxMukMXp+XVCrF66+/yaGDh5mamiYUDNEQjxOLRenq7iIYCpLLZUml0hSKRRYXF7E8FslkO4lEw0fHr1plamqaqakpABYXU5w4cZLFxUU6Ojvo6EgyMzPLe+/u49Chw+zf9z7Hj5/A4/XQ0ZGktbVFHwIRERFZ3de7CoGIiIh8mGBLK42NLYTjjXXRHsMw2PXUl3j9n35AbmqC2iqtOhIRkTo+N3Z6SXTG6HlgZW5isLEpuQWqbgW/4SdmNRA2o5TcIgv2HG2JLhqDLSTXtdDU3EQmk2Fudo7Ork6CoSAYsG3bFgYG+vF4PCQaGwgGg8RisQ/syzRNQqEQ4XCY2dlZ0qmlYdqy2SyHDh1eXm7z5k00NMQpFIosLixSq9UAyOfyLCws0t/fRyQaweP51dcQhUKBhYVFbNuhVCqRz+fx+XykUikqlQqlUplcLodlWXi8nqVh7KJRDAySySSPffxjAEvDzVnW0nuIx5ienrmmuAYCAWKxKJZlksvlmJufB5YqlNKpNJVyhWAwSDQW1YdAREREVjUljkREROSyLJ+fQDTGxvsexTDrp1A53tJGrKWNzOQYuVWYODr20r/w0Jd/f/nx7/zZ9/irP/yqDkgRkTpgeiCU9JLs6cDyrdy5seyWyDs5fIafBquRkBli0V4g7SyS8DbRv72XbDlNItHAqZNnmJ9foLOrc2ndUpnp6RlSqdTSxs4DLoyPT3xgP4ZhYBoGvyw6dn/jNQDXdcFYeuy4Lq7r/NpS7mXfg+041Oza8jYcx8F2bGo1+8JzDo7jLG17aQdLFUuuQyaTYWRk5FcbO36CTCZLajF1zTE1DH6tutrFddyL3ofrLj02WFomEonQ0tJMpVplcWGBQqGI3++nsTGB3+8nnc4sVUNZFvF4nESiYSkhNTePbdv6MImIiEjdUuJIRERELiuQaCSaaCbR0V1X7bI8XhoiMebiCXKT4+ooERG5ZfwtXnxmkN6HVnbI1JJTIu9kafUkaTCaMLEoODmKTp68k6Ozr5Pc7ABWxWJ+bp5CoUCpWKJULFEoFDh69Bgnjp/E4/EQjUbwer3kcnkCAf9F+3Ech2KpRLFUWqroiS5V24TDIXr7emlvb2dsdIxyqUwmm6W5qYlYPI5pmjiOQygUoqEhTqVSIZfLL1ciXYvyhQokF5iamuaVl1+jWq0SCi1VSy1VKGWvfIMX8kLGhYRUqVQml83huC7B4FK7l6qgvESjUfx+P7Ozs2SzS/toT7bzyCMPklpM8957+ygUxojFY+zcdRetra0cPXKMffv24/P5GFjXz65dd3Hu3Hle/8WbFItFfZhERESkbilxJCIiIpdkGAaBeIK23oG6bN/aHXczOnwOw+PBvY4voerVX/3R7/E7/9dfAhCMxkmu38Lk6WM6MEVEVvLcaEGo08va+5NYfmtF21J1KxScHB68eE0feSdH3slRdasU3DyW4aEz2cXY0DgzszO4rsvCwgLj4+Mkk0k2bdpIpVwhHA7T199LrVbj0MHD5PP5i/bjui7z8wuMjY7T1dXJps0bKZfLJBIN3H3PHmKxGOVymaNHjjI8NEyyvZ3+/j5mZ2YpFosMrBugPdnO7Nw8Q0NDy1U71yKbzTAxMcnmLZvo7Oxg+45tLC4u0tXZSVd3F+fOnefA+weBj67mcRyH2oWqn4aGOOvWDTA+Ps7k1BSzM7O0trZw1867CAaDxOMxNm3eiG3XmJiYYnpmaSi8aCRMT28Ps+FZjh4LAeD3+2lvb2fNmm7Gx5dubrEsk8bGBH19veRyeSzL0odJRERE6poSRyIiInJppoknECToD9Zl8+JtHZi2jS8UoZxJrbrwFzMpJk4fo2P9FgCe+fo3+fbXvqjjUkRkJU+NPhNf3KSxY+Xn/XNwKLlFKm6ZsBGh7JTIu1lcXEpOkbJbIpqIUDyXZ3ZuDoCZmVne33+QBx4MsHHjBlpbW/B4vPj9Pk6cOEmhcOkqmNmZWfbvfx+/38fAQD8tLc14vV58Ph9HDh/l1MlTpNMZjh45Rjwep7u7i8efeIxyuUwkGiWdyvD+/vcZGhy6vnNjscTZM2dpa2tlYKCfj33sEXK5POFwiFwuR7FQvOKKpmq1ysLCAouLKdra2njwoQfYv28/x0+c5J2332X33bvZvn0ra9Z0EQgECAQCHDt2gkMHD5HPLSXXJqemefmlV8nn88zPLc2HlElnOHDgIOfPn2d0ZAyASqXK2TPnsG2b6akZKpWKPkwiIiJS15Q4EhERkUsyDBPTsoi3JeuyfaZpEm/rYHFqYlUmjkREpP54wya+kJ9YR7gu2pOzswxVzxIwAmSdDGWnBEDeyTJcOYcHP/vfOUiulgGgWCxy4sQJ8vk8nV2dBAMBqtUqcxeqgebn5wkGg5w8cYpcNrc851GxWOTkiVPk83m6ujoJBUPUajXm5+cZGhpmZmYWgLGxcV5+6RV6e3uIN8SxTItCscDkxBTDwyPk80vzEs5Mz/Laq7+gXKkwN7uU1FpcTLHvvf0EAgFGR0aX3kehwLFjx5mbm2dsdCkJMz09w2uvvc742DgNiQZM0ySXyzM2OsbIyOjSnEhXwHEcRoZH2fviSzS3tFCtVplfWCCbyXLw4GEWUymSySTBYAC7ZrOYSjE8NMzU1PTyNqYmp5ianLpou5lM5kLV069UKhXOnj3H2bPn9CESERGR24Kxe+d9rsIgIiIiv8kTCNJx1x4+9q/+e0Kxhrps44G9P+bUO6+RGjy7avvhD77z3PLPx17+Ka//4Ds6OEVEVkik109je4JPfON+TMuo/wa78Iv/Zx9jh6fIDV5c5WJZFl6vB9u2qVavfMjX5fUch1q1dsmh5wzDwOv1Ypom1WoV27Zv+Fv75T4Mw6BarS4njGLxGMn2dkzLvOy6tWqN+YUFFuYXME0Tj8fCcdwPVCstvVcvjuNQrVava5g9ERERkduJqRCIiIjIJS8SPB48/gBef6Bu2xgIR7C8XjCMVdsPr/+X7y7/vOWxT+nAFBFZQZ6IQcvGxtsjaQRgQKInjjdqwW802bZtSqXyVSWNLlqvcvlEiuu6VCoVSqXSTUka/fo+yuXyRVVGBgamZWJZ1of+My9cOziOQ6VSveQQd0vvtUSlUlHSSERERO6s616FQERERC7NwDQtTLN+7zMxLQ+YJgagr3NEROTmnnTACpgk1kRuq2bH2kNYIRPDNHDt1X+2zGQy5HK5j1zuSoe0ExEREblDL31FRERELsXFdWycOr7D1nFq4LirOml07KV/uejx7/zZ93RoioisAMM0sHwmwUTwtmq3ZXkwLQPjDvnr33VdbNv+yH+qIBIRERG5PCWORERE5JIcu0a1UqZWLtdtGyuFAk6tCqv8y5+hQ/uWf7Y8XgLRmA5QEZFbzABMj4EnYN1ef/Sb5tJf/ob6UERERESu8BpSIRAREZFLcWs1auUypVy6btuYSy1QK5VWfV+89v3/tPyzLxgikezWASpSb78zcXEMF9dQFcPq7mgD9zYb4czRISkiIiIiV0lzHImIiMglOY6DY9fIzEzVZaLCdR0yM5PUCrlV3xfFbJqJ08foWL8FgGe+/k2+/bUv6iAVWYnfPQbkA2VyoRL5YIWKp4ZtOjiWi2uA4YLhGFi2gbdmES77iRQChIt+LEclH7d137vg1Byqhcrt1XDDwXAATekjIiIiIldIiSMRERG5JNdxqJVKFMr1WdGTnZ/BNkwq+dwd0R/Pf+tP+IPvPLf8+KHf/hqv/+A7OlBFbpGyt0Y6UmQhlsMOG9hxk1qLRaPZSrnBxQ65OBYYDlhFA38a3EyV2VKWzFwVp7xAQyZEIhsmUPGiwqTb8bzoUis75GeLt9exmy9jV11clR6JiIiIyBVS4khEREQuzXUpZdLMDJ1l0z0P113zho8coJTLYFerd0yXvP6D7/DQb38NgC2PfUqJI5FbwDFdphszTCXShLxBAutbWdzs4HiXvoSfx77UL1Dy3QAW0IDtQOycyeT5GaYWMzSlwnTPNmKqAuk2Oy+CU3RYGMrcVs3OTOSp5uzbbog9EREREVk5muNIRERELqu0OE9mfpbM3Exdtcuxa6RTC5Qz6TuqP0aPHbzo8aO/+z/qIBW5iVKRAifXTGI3ewjc20bxmQbmd9jLSaMr5ZqQXudgPN5M+P4OCv0Gx3onmG3I4ip3dFup5hymjy/AbVK849ou8+fSVLPKGomIiIjIlVPFkYiIiFxWrVSkmE1z+t1fsPvTz2IY9fENZ25xntTsFKXUwh3VH5mZKYYO7aNnxx4A+nbfz7v/8DcUs5e4+90wsDxeTK8Xy+/H4wtgej0YpgUszWHl1qrYlTK1UhmnVsGpVnFdDWUk4pgu04kMqaYSzoYwMzu8uBZcb7bA9UBurYPZGaXxcIzxkWmKU1U65hrw2Lqnr971PtKJt8Gi5OTgNkn45eeK1AJlqqmaOlBERERErpgSRyIiIvKhCnMzzEyNUcykCMUTddGmQy88T2FhnuodMr/Rr3vt+/+Jnj/7HgC+YIhERzfFU8eWXzdMk0A8QaipBV80hun14AuEKIyNEE404QuGMAyolEoUFucJda2hVMjj1GpUclmK87MUUwu4tq2DX+5IrgEjrfPMNxUI35+k3Hnjk6mOx2Vul4u/txXPTyqcCUyzbqwVj22pA+pQ191tfOo/PARAKVfite+9zVLmqP4T7bNnUpRSZWp5VRyJiIiIyJVT4khEREQ+VDmTpphKcert19j55OdWvD3T50+TymXITo3fkf1RzKaZOH2MjvVbAHj6j7/Jt7/2RbzBEOGWNgKJRkzbZs3WXTS2dRBvbccfjuL1+zEtz3LVmOu6OI5NrVymXMiSmZthcXqKsROHqdg1iqkF8rMzVAs5UBWS3EGG2ufIdtbw358k33hzj/1ywmX+aR/xl6Kcd+bonWzGW1PyqB409Tdw11c20PtI10XP+0I+WvuaGDs6QdfWZN2/j9mhOSqLNf0aFxEREZGrYnUku76hMIiIiMhluS61UhHH5yMSjRFtal3R5gweP0i1WCQ7OUatUr4ju+T0my+z55nfwgVKuSxWOIzhD9A+sJG+zXex+1PP0rl+M/HWJIFwFI/Ph2laFw01aBgGpmni8frwhyLEmtto6+mnd/tuwpEodqUCPj+eQAC7XMapaZgjWd0cw2W8NUWmo4p1XxPl5lvzTbvth1K/RWASMuRoyIUw0MRHK6H3kU4Sa2N86S8/yaan+0isjX1gmWKqzPypFGdfHaZjSzu+sLdu3085U+XkK2dInSliF1RxJCIiIiJXTokjERER+Uh2tYLp9ZDPpOndvnvF5jpygePvvMbDX/wddn36WfY//8M7tk9KuQyhRDPv/fxHNHV0s277Ljbe9yiNHd1Y3mv/ItO0PDS0ddCzbTdta3oplYoYwSB2tUKtWNSHQVatxViBsZZFfA+2UWq7teUZrgVOp4/GEyZZX4FoIaAOucV+/8Uv0vdoF32Pdl12me8+8RxH/u4ME/vn8CctnKxBcltz3b6no39/hpmhOXLnyupgEREREbkqShyJiIjIFalksxjBIJmFOdp7+rE8t3bE25FjB9j/0k/Y/MBjxBJNAOx55rdo7FzD/OgQ5Xz2jukLw7IwfAHS6QUe/sJXSPYMEEk03/AqhWA0TrJvHT6Ph4phgAGVfA7X0Z3rsrqUfFWGOudp2Jwk0++yEgU/jg8qzSaZuQUiRT/+qkYVv9ke/5N7efx/u49dv7P5ssvs/ebbHP+nc7z2p/svet51wYradG7pwOOvv+EFc9NFTr53mtTJItWsfmeLiIiIyNVR4khERESuiOs6S0kDj4dauURTsgvLc2uG6Bk7eYQjb73CzMmjZMaHGbj34eXXEh3dbP34Z2jpHaBSLJCenlzV/WB6PDSs6SXW3sEnvvIHeG5yH1geL02da4nHE2TmZ8Hno5LN4Dq2PhSyaoy1LmK0B0g95GElR4mzowZdE3Hm7EUS2TCGqyHrbrSWDQk2f66fz3zrkUsORQcw+NoY5/aO8pOv/4LUcJbcTOEDyzhFFzPqUpqqktzegmHWT185tsuJn55l5uwcufMVXOWNREREROQqGbt33qdpMkVEROSKWX4/LZu209G7jns+88Wbvr/z+9/kxMH3WBw6S25qYvn5z/+v/4G2vvWXXOfbX/viqoy96fGQ6B3A7/Xz6L/5d8Rb2m/p/kv5DG889zek5maZP3MCp1bVB0Jue2VfjRO9k/gfTVJI1sE37K5B+LkULWNBmtIRddAN9LvPfw5v8PKVXN994rmr2p4vYdG0O8Tuz+wkub1+hqwb2zfDwRcOM/dunmpWSX4RERERuXqqOBIREZGr4to25VSKilNjeugsscYmQrGGG76fSiHP/p/8A4Onj7EweJb87NRFr598fS+Lk6P073ngA+v+cgi7hbEhSrnVMYSdYZrEu3sJR6I8/K+/SkNr8pa3wePz07qml/nRQVyPl3ImrWHr5Pb+fWa4jLQt4FkbJbfVXNFqo1992CGU85LLZmlW4uiG+OWQdJbX/MBre7/5NoOvjvHSv3/nqrfrlF0wDSq1Ik1dTfgj3hV/r4W5Igf++Rip83mK00rui4iIiMi1UeJIRERErppTq1JKLWI7DvNz01QLeSKJJrw+//Vv264xeeYEh177GdNjQyyeP0tpcf6Syy5OjLH/+R9iejz4gqGLEliragg7wyDa3knbuo3c+9SXSLR3rlhTfMEQ7T0DTA+fw3Fdypm0PhBy2yr7aoy2LeLdkaASr5+BGNywhTVYIpzz4bUtddQ1aNmQYPPnLz0kXWGhxNh7Uzz3ey+QGs6SGr72GwyqKQfXW+P/Z+/O4+S46wPvf6qq72N67vvSSKNrRpd1y5ds2WAggINz2EBiEmxIgJzLs9lk90myPLu8Ngu7yRLCBgyEhA04yRqDDTY+ZMuSLMmybo2O0Uhzz/TMdM/0fVZX1fPHSGONJRtZZ4/0ffv18ktdfdTvqumq/tbv+4v0JalfXo3NceP6KxPN8cY/HCYyGiXekwOJ6wshhBBCiMv9GUJS1QkhhBDiSrjKygk0z8Pl9TF/8XLmr7sDTbu8Rd0nh/vZ+5Mn0e12UmOjRIcHUKxLP1XxllXwyf/+rXd8fq6msHN4/VQu6WDFpntpWba6KMo00d/DgW0/Z/zEUTJTk3IgiDkpHEgyMC+C9kAthrO4LotKt+ax9+RpHi+XjnqPHv3pR7G7Lv49tP0r+zj1wsBV3Z/mUilb4SbgKePOP7oNu8d23etcyBrs+c4hwmNhpg6mMbJymS+EEEIIIS6fBI6EEEIIccU0uwNXaTnemlrcPj+Vja2UllVQ07YQX1klqnbxO7CzyQRTw/2M9pwgkUoQmxgjG4+SCo2jZ9JgXd5pStuajdz/2S9e9Lne/bsBeOnvvzpn2rZi4VLalq1i+T0fQFWLZ/bBiZ0vc/LgXsInuzDyOTkQxJzTWx/C01bOyJbiWwfG12OR2xtiaV89NkOVzroEW/58PfPuarzoc1u/tIeB10cxjWtz+WvzqpR1uqmoq2T1wx14Kl3Xrd6ZSI4D/7eLib4wkaMZjIxMNRJCCCGEEFdGAkdCCCGEuGpUVcMRCOAuq8Du8eJwezFSCUoqa3D5S7A5nNNrJKVTJCNhTFVFz+coZLNkoxGy0SkKuexVK8/aBx+hZcUaKhpbL/r8S9/8KqMnu4p6HSRfTT3lza3c8/CncV+DtaSuRC6VZNczTxLsPkZ8dBDkrFLMIYZqcqItiLalmnRt8Q1eV1ghu3WUpX31uHN26bB3ULWojNY7GljxyKILnktPZRnvCrP1S29cl7JoHpXSJS78AT+rH+6krDWAcg1jfpYJk31R9v+gi2QiQexkDiMtQSMhhBBCCHHlJHAkhBBCiGtG1WzY3R5sHg+a3YGiaViWhanrFHIZCuk0hVyOax1x8JaW88mvPPGurynGNHaqzUbV0hV0rL+DhevuKso+Hjtzkt3P/AuTfacpZNIy6MWckXHqDDdHSfxaAMtWfOWzZRQKPwuyoL+KQMotHXYRn/rZR7E5r19Kuku6wLYplCx04Gty0tI+j6UfnY+iXJt9dT11mqH+QRIDWeKnclgFubQXQgghhBBX6XpEmkAIIYQQ14ppFMgl4+SS8RtajlR0im8+/tC7prD77BNPMdLdRS6ZKJo0dp6KakrKypl/28ai7ePq1gVUzWsnE48RHx6QQS/mjLy9gO6hKINGAIbDAq9G3lGAlPTX+X5RSrr+10exjBsTRLEKFvHuPPlJk0LqDOP9E8y7rYXmdTVoritPNVrIGgzuGWPw+CCRwTip4TzZCR1LJhoJIYQQQoirSAJHQgghhLhl9O7bzTf3Tc8suv93vkjb6tkBmYZFncB0EAmmU9kZeZ2BI/uue1kVVcVdUUVlTT2arXjTVKmajZq6JkKDfSRGh7BM+fVSzA26ZmA6ineGhqXCgkg9KS0mnUVxpaT7hX1nWGTGdfIxg3STTjKW4MyuAebd2UBVazmeSg8256XnsDNyBslQhsnhKGdeHSSTT5IO6qSH8xhZmWUkhBBCCCGuPgkcCSGEEOKWdG5W0Uf/w3+ldv7ii77m7bOTrmc6O83pxOUvoWXZ6qJvy5YVqzm+5zUc/hJysagMLjEnmKqFqRVxARUwXBamIoEBgI/+3b0X3f7aV/bR80JxznY0siaJnhyp3jzupjzJbBy7R8Ouu6hqraJ+ZRWBRh9Ovx1FVWY63jJMckmd2FCC4KEwU+FJsnoaPWWSGsqTGdUxdRkXQgghhBDi2pHAkRBCCCFuaT/5b/8RgLY1G1n70UcorW14x9eem4nUu383uXSS7f/099esXK7SClw2OyXVtUXfhg6Xh4aFS8kkE+TiMbDkB01R/BSg6GMyt/ihtPSj82m7p5HazsoLntv6pT307xzFMou/kUzDItWfJzOio3lUnGVZ4lMJhk4Pgq5g99rQnBqqqmCaFmbOIJ8qgM0inyiQjxrkowZG2pSAkRBCCCGEuC4kcCSEEEIIwXQau959u4HpNHbABanszjm3fcmd9wPTKe0AYuNjTA71XXFZFFXFHSilcfEyVFWbE+0XKK/EWVKCqmmYhYIMKFH0VENBMYq4gBbYMgqaqd5yfeOpcFHTUcGm31s5a3uxpaR7r0zdwowZ6LHpgafYFFSHguZQUGzKTL9bhoWZtzDyFlZBAkVCCCGEEOL6k8CREEIIIcTbnEtjB+ApLec3vvLEu77+7SntALpeeY7Xf/idy9q/arOjud0EqqrnTJuV1jai2R2omk0CR2JOsBsaak4p2vIpJvSUjdIyGbjl+ubj//KhC7a99t/30fPiwE1VT6tgYRQsjLQcj0IIIYQQorhI4EgIIYQQ4l2ko1Mzaxu1rZmeaXSxQNHbdd77QTrv/SAwndrunMGuA3TvfOVd36vabNgcTjyBsjnTTp6SADaHA9Vmh1xWBo4oeg7dhmHoqLqCaS++WR1aVqGQMXDkb41Ltsr2UlY8soh5dzXO2h7uiXD4h930bR+RQSuEEEIIIcR1IoEjIYQQQohLdC6V3Tf3PYTT4+Wu3/xd4J1T2p1z/vNtqzey+dHPz3p+3zNPEgkOzzzOZjLYbDbsDtecaRu72w2GgeZwQErGiih+Dt2GmgVPUCHZXHyBI3sSDMPCUdBu+r6Yd1cDW/58wwXbf/bvXiN4OCyDVQghhBBCiOtMAkdCCCGEEJchl07NSmkH0Lnlg9z+8Kff82et+cjDsx4H+3o4uXc77pK5k6JKs9nRoxFM9BvgAAAgAElEQVQUu5xeirlBtRS8aQfeIyrJZrPoyqcnsth1DftNHDhq3lDH+/7Lpgu2D+4J8uJ/2iWDVAghhBBCiBtEruyFEEIIIa6Srq3P0bX1OQAC1XVUNLcCl5ba7nymYaAo6pyqu6Ko06nq5li5xa0tkPLQmx/Glq6j4CmuWUeBbgVHzIdm3pzH1JY/X39BWjqArV/aI2nphBBCCCGEuMEkcCSEEEIIcQ3EJoLEJoLAdGq7c5o6V7H4ji2zXlu3qAO3r+StDYqCNQfrbJkWliV9L+YOb9aBmgP7hEmhVSmacrknFKKpKEtjtTddm8+7q4HNf7YOzTY7IPbmt7s4/GS3DEohhBBCCCGKgASOhBBCiFv5RMBmw+6wY5kWuq5jGMYFr1EUBbvdjs1mo1AokM/nb7o6Xk9DXQcZ6jr4rq+pXriUksbmOdXOlmmQz2SwTEMOLDFnOHQbFTEfxnGdTKujaMqVG4zjTdpx6DfX5dpjLz900e3fvu8pGYxCCCGEEEIUEcklIoQQQtzCamqq2bRpA6tuW0lZWdlFX+PxuFm8eBGbN9/F/AVtN2Udi000OEw+kyYVnZoz7aznc/haWjF1XQ4sMafUhQMkYjECPcVxaWRPKqiDOWoiJTdNG1cuLOPTL37sgu3P/uE2CRoJIYQQQghRhGTGkRBCCHELq6mtYdOmjUxNTTEZniQcDl/wGrvdTnVNFfMXtJFIJOZcHf1+P62trcTjcUaGp9fNcDgcNDY24HK5GBwcJJlMFVWZzUIBo1BAz2bmTDvnUyly6RSGnpcDS8wpdkOjPOoh2hNBaSvF0m5cvkW1oFCyPY85aack5b4p2vdD//Mu6pZXzdrWv3OEl/9yzy01zlRVmVm7zjRNrKuc11NRFFT12n2+EEIIIYS4tUjgSAghhBDvKp3OcOzYCUaGR5mauvwZMIoyvX7I9f4xKxgMsv21Hei6zuTkdPnLy8vYsGEdTpeTWCxWfIEjXUfPZoiFxiitbZgT4yQVm8I0DMyCzDgSc0/dZIC4d4y67Rqj9xRuWDkCp1Sm4pMsDdeh3AS/+3/quQexObRZ2270DCO73Y7dYUdVFAzDJJfLYZrmzPOqquJ0OtG06SBMNpujULjyMREoLaWiohyjYBAMjpHNZq9qvdxuF7W1tTicDgYHhkin03JgCyGEEEKIyyaBIyGEEOIGKy0tpbGxAcMwGBkZIR5P4C/x09jQgN/vIxQKMzQ0TKFQoKqqkqamJmLxOMNDw5imSWVlBbV1tXg8HgBSqRRjwXFCoRCGYeB2u6mvryMQKGFqKoKiKFRUVDAaDF60PGVlZTQ2NaAqKsFgkHg8gcvlxOf3kUpNB1gqqyppbmokmUwRjUYpLy+nvKIco1BgbGyc0dEg+tmUZX6/n8amBsrLyzFNk4nxCXK5HFVVVaTTaYaHR2Y+991UV1fR1NxENBJlaGiYfD5PRUUFTc2NaKpKcGyc0ZFRNE2jtq6WurpaxsfGyWQyeH1ecrkcNruNurpaVq+5jeaWZvL5PJ2dHdhsNgYHh4rmDm3TKKCnU0wGh2lZtnpOjOPYZIhCJo1ZKMhBLeYczVBpGi+nxzmEs7eKXNv1v0zyjqhku0K0jFXgytvndHsufXA+m76wcta2V7+8lzOvDN3wss2f38bCRe243W6ikSh79+4jEonMPF9SUsK69WtmUpvu3r2HkeHRK14fb+nSJWzefBfJZJJ/+9cfMTo6etmfpSgKfr8fsEil0hiGQVV1NQ984P1UVVXyT//4f+jr65cDWwghhBBCXDYJHAkhhBA3WGlpgHXr1mB3OHht23bi8W7q6+q45967qa6u5vjxE0RjMWLRGPPnt7Hlvns5erSL0ESI2toa1q5dTUPjW7NSLGBocIj9+w5wprcXr8/LypUrWLioneHhETRNo7q6itd37iLztjueA4ESbrttJctXLGNsbJxIJIJhGKxYsZyVK1ewe/cbDA4O0djYwH33byESiRIMjlFXV0ttbQ02m43eM73s3LmLM2d6cbvdLF26hE23byAQCBCLxxkLjqGqKs3NTQwPDZNIJC4pcFRbV8vme+5mdGSUeCLBZHiStvnzuO++e9FsGm++sY+J8QncbjfLl3eyYsVydr2+m1g8zn333Us0GuW1bTsoKfGzbFkngdIA+VyezmWd5HI5hodHrviHwavGsshFI0wM9KLnMtidxZ2yyrIsJgbOkI1GsM67c1+IucSXdtE4Xk5mZx6H4iAx7/qNZUdUIX8gTOmUg4qYd862oafCRU1HxaygUah7iv4do0URNDr3XbJixXJ8fh96Pk9//wCxWGxm1lHb/HncdtsqysunA0fd3acYHQ2CUTzt7HQ5+ZVf/Ri6rvPz518gFApPB8He2IvH45kVCBNCCCGEEOJySOBICCGEuMGSySTJVIr2+jrKK8pRNZWKinICpaWYlklVVSVVlZXTM2wqK3A4HKRSaXw+H2vXrWHR4oX09/Vz/NgJFFVl6dLFdHZ2oADRaHT6C99uw+/3U1tbw/DwCPv2HWBkdJTy8vKZcni8Hjo7O1i5agWpVIpDhw4zMjJKWVkpDocDt8eNwz59F7ymqjhdTurqasnn8/T19hEMBlmyZDELFi5gfGKCgYFBamtrWLFyOeXl5fT09HDiRDclfj8dnUsJBEqYGHfMpLD7RVLJFOlUmoqKcsrLysikM1RXV+FyuTBMg6rqSkpKSnC7XVRXVaHrOvF4Ak3TcLvdZDIZVFVldDRIX28fy1cuJxqN0tV1nL6+/lmpiopBLhYhp+cIDfRSv7CjqMdwPBQkmUqSiUzKAS3mLAWojvgZ1aLkdkXwuEtJ1177WYjOiELu1SDlYQ9N42Vzug0//i8fumDbTz7/alGWNZ/PY7fZaGpumpn5qigKrS0tOBwODMNEPZuu7hyHw4Hb7cJ+9ruwYBhk0hny+TyWZaGqKj6fD5vNRiqVwuFw4HQ6iMcTXOybzuFw4PG4QVFIJVPouo6qqrjd7pl0eaZpks3myGQymKaJ1+uhfWE7lZUVxGJxSktLSSaTZDIZenrOoGnqzM0YLpcLl8tFwSig53WcTicOpwPLNEmnM7PS9NntNjweD06nE9M0yWSyWJZ1ti0M0ul08dxcIYQQQgghrjkJHAkhhBA3WDyeIByeZNmyTkpLA5T4/VRUVpBOpUgmU1RUlFNZVUkikaC8rIxYLEY4HKaurpa2tnnEY3EOHTzCwYOHUBSFgq5TV1dHfUM9DY0NDA8NY1kWNptGNBrl9dd3MzgwCDATOHI6nSxc2E5dXS2mafLGnjfpOXX6HX8kss7+z7RM+vr62fX6bvx+Pw6Hgw0b1hEoCeByOamoqKCurpZIJMLhQ0c5fPgIfr8fj9dLY2MD7+Un2ejZei9YMJ+yslJSqRRVVVWMj0+QyWQoLS2lqqoSRVUoKy8jGo0SCoepqal+q9yWRTA4Rn//IJ3LOonF4hw/dpzh4ZGiGxemYZBLJBjtPVXkgSOLU7t3kJoKU8jKmhpi7qudDBAyEwy/PoZ3YSXpThvWNVpwqPSkSuZ4mKoJLw2hMhSUOdlmm/9sLbUdlbO2bf3SHvp3jhZtmUMTIcrLy5g3r4WjR46SSqXw+bzU1tUQi8VQVQX32RSwMB3kWb5iGWvWrKaishxNVUkkkrz55n4OHzpMIpHE7XbzsYc+SmNjI6++so0F7QuYP7+N73//n+FtN0nY7TZWrlzO+g3ryOXyvPDCS4wMj1BdXcXGTRtYsGA+TqeTQqHAyZPd7Hp9NxMTITbdvpHVq2+bPl8oKeFXfvVjvPrKNiKRKHdvvovKygr++Z+fZKB/gJWrVrB+/TrGx8cZ6B9gydIltM5roaAXeHPvPnbv3kMsFkdRFNrb27nr7jtpaKgnm8ty/NgJdF1nwYL5jI2N8/JLWwmH5eYAIYQQQohbhSpNIIQQQtxY+XyeSCRCNpulpKSEhsYGqqoqiUQi9Pb2USgUqKgop6Ki4mxAJMbU5BQ+nxefz0c8HmdyagrLsjBNk1g8TiwWx+v1UOL3z9rX5OQUYxdZ2ygQCLB48SLq6+uJxxOEwuFLWgw8Hk8QDk2/NpVOkYgnANBsGg6HA5fbidvtIplMEo3FgOkZVtFo5D2vJ5SIJ5icnMTldlFaNr3IeHl5GcFgkIGBATweD1VVlZT4SygJlBCJRJmanJqz48KyLFLhcSaG+knHijftUD6dJhqbJDsZBkuOZ3ETXCBZCtWREuYNlqMfniLwSg5nRLmq49ueUPDuzZM8MkbFqJvm8Qo0c25emq19vJMF9zbjq3kryPLTP36Nvu0jWGbx/lEIhyeJxxPU19dTUlKCoii0tLTg8/kYHQmSyWZnhfHa2xdwxx2bqKyq4Mjho+zbdxC3280DD9zPvHnzsNlsWJaFx+OhpMTP2nVrKCsrZXJyklwuP2vfmqbS3t7OnXfdgd/vZ8+eNwiOBvF6vXzggw+watVKRkeDvPrKNoaGhlm5cgV33Hk7gUCA0ESYWCyOYZik0ikGBgaJRKIoioLP5yUQKMFm0wCw2Wz4fF6amhrZsHH99DqEwXHsDjt33X0HrfNasdlslJWVcs+9d9M6r4WJiQmOHD5KaWkpHR1Lqa6uxu12o6jy04EQQgghxK1EZhwJIYQQRSAajREOT1JS4mfevFbKyso4daqHgYEB5s1roaysjEJzgUAgwPDQCJFIBEVtAwVM05q1roxlWdNBGUWZlQbOAnRdJ5/XL9i/qqpMTk6STCapra1h/vw2QhMTF/zY9XaGUZgJMFmmhXG2HNP7Vabvnj9XhvMDRdZ7/w02n88TmYqQy+UoLy9HURRsNhsT4xPE4nEKhQJ19XWk09OzXqampshkMu9pHz6fj7KyUvJ5nVgsRjabxel0UloawG63E48niMfjaJpGSUkJJSV+kskUkUjkmqS6y0UjpKJTHHjuR9z+8G+hKMX3w92pvTtITk2Sicqd6OLmoVhQHvfiyzjpz0+Sm5yktr6RiQ0mpu3KgiEVh1Um+4KoCZX20Wo8WcecbafHXn5o1uOB3aO89P/unhNlt0yT4eERFi9eRENjA6Ojo7S0tuB2uxkcGqK2rpaKt7K5Ypom3SdPkUqnOX7sOMlkipqaahYuaqe+oY6+vj50vTDzVWdZFj9++hmGhoYxTZOmpsbpsaWqNDY1smbNanw+L88//yKnunvI5/OsXr2KmppqEokEu3ftYWRklL7+AcrLy+ns7ODggUMcPnyEqupKqqorGR+b4OWXtjIxEaKtbd6FdTxbmPKKcn7y42c5ePAQToeTT3zyYVpbW6mvq+PM6V4WLlpIIBAgm8nw8+dfnF6j0OPmE594hLLyssv70hZCCCGEEHOaBI6EEEKIIhCLRgmHw7TNb8PhcKBpGpPhSUKhMKFQmPb2Bfh8PgAmp6ZIpdJk0hmy2Sxerweff/o5RVHwerz4/F6ymSzJs+sczHiHH36i0Rj79x3Abrdz5113sHTpYkaGRzh9+sxl18myLLK53NkyeikJlADTwZnSslJU5b2nZIpGY4RDk9TW1pxNV5cmFA6TTmeIRCI0NjaQy+WIx+KEQ+8WyLBm2uv84FpjYwPr1q9lanKK/QcOEhwNUlpaym2rpxdKP3qkiyNHjuJ0Oli0qJ3OZR2cOnWavW+8STabvSZjIzk2SrSsgtBAL9WtC4pq3EaCQ5zpOkg8OIx5CTPUhJhrHLqNBSPVxD0ZxtMh3EGNTLOCeXslGPrsgPg7UAzwjKo4+nWy4QTppEHLVCllCS+aMTdncVQuLOOjX79n1rZjPz7N7q8fnlP16O8foKmpkXnzWjhz+gwNDXVkczkmxifI52ffOHHmTC9TU1NUVVXRvrAdm82G3+8DCzxuD5pmQ9ff+jvY3X2KqampC24q8Ho8bNlyD16vl8OHj3D69JmZ74/qmmpcLifpdIYVK5azoH0+iqLg8bjxeNyUlpVis733S/hYNMbIyCi5bI5cNkcsFqdgFHB73Gg2jdLSAA6Hg/HxcRKJBKZpkkqmmBifoLGxQf4QCCGEEELcgiRwJIQQQhSBRCLJZHiS5cs7KfH7GRsbmw6IpNLTaelWTc+ECYcnCYfCWJbFxESIkeFR6uvrWLJ4MblsDs2msWjRQnxeH6dO9RAcDV7S/rPZDKFQmHg8Tn1DPYsWLaRzWQehUOiK6hWZijAxEaK2pobOzg4URSEQCNDc3PyeU9XBuZlZYZpbmrBMk5MnTxEOhdF1nVBokvnz52NZJmfO9BIOh9/xcwzTxDAM/H4fTU2NpNJpotEogUCA9vYFjHpHOXHyJABuj4umpgbq6upm1kLSNBtV1VUsXNhOPJ5A07RrNjbyyTiJ8RFO7N5GoLoWp8dXNOM2OHCafDpFtohT6QlxpVRToTTpoSTlJu7NMBlLEZ0MQqkd2nzY4wVMm4XXcmOpYCgGOXRscQtXSME3ojBhi6BlHNQkPJQmPXM2YATwS399N7XL3lrPKD2Z4clP/ByzYM65ugwPDROLxamvr2fR4oX4/X76+/pJJmffdKEqKp3LOrj33s34/D6SiSSZTJbS0lJUVeFiS1OlUmkKhQvXCfT7/aTTKQzToLW1hYrycmLRGIZhYLfbUVUVn8/HwkXtFIzpQFTBKBA6+52mXkbKuEwmg3FeWXRdxzKt6ZsnAKfDiaqq6LqOed53c17PX5PZtEIIIYQQovhJ4EgIIYQoArquE4lEyWZzlJWVEo1ECYcmMQyDSDRKKjWdEudc4ARgbGyMA/sP4nA4aF+0gOqaKlRNo8TvZ2BgkEOHDjMxEaK0NHDJ5QiFwhw/foKGhnra2xcwMjLK4ODQZddrbGycriNdeDd4aF+4gOrqKiYmQuh6HvMy1r5IJpNMTk6hKiqWqhCJRIjHp++OnpqcxLIsbDY7kUiUSCT6jp8Ti023Y3VNNevWr0PVNN58cx9jY2Ps2rWbeCxOLDq9JlM8luDokS4GBgZnAkf5fJ7e3j4AhoaG0XX9mo0NyzSJDw3g9Ac4+PzTbHjoN4pizFpqgdH+00QHzmAZhhzE4qanWtMBpNKkh/1/HIBwGoaSaMcyqMk8KS2KhYWCgmYqOHQ77owDb8pNZ6YB1VLmfBv81vMPotlnB8p/8OvPzdn6ZHM5hoeGaWiop6OjA5/Px0D/AKm3zdZ1u92sX7+WquoqXt+5m9de204ykeSRj/86S5cuece/3Re7QSKZTPL8cy+wtGMxixYt4rbVq4hEIkxOTpHNZjEMk9HRID/58TOMjY2jKAp2ux1FUaYDO+cCOdZbmWCvVDqdpmAU8Hg82M/OaNI0DZ/Pd1kznIQQQgghxNwnZ4FCCCFEkRgNBtm5cxelpQEG+geIx+MAjI+Ns3vXHioqKxgaGp4JiGQyWY4fP04ymaS1tQWvz4tlWXTHT9HX18fQ4DCFQoFMJsup7h4y6cxMsOOcifEJ3tizl3Q6TSQSxTAM+nr72bZtO9XVVWQy0+nw3v7+t78Pptd/GBocYseO1xkdGSWXy5JOZzhypIt0Ok1lVSX5vM7IyAhNTU20zW/DPDvz51Lpuk5fbx/bt+/AsizOnO6dWWNpYGCQ7a/twG63c+ZML7lc7h3LGhwN8vrru2lubkJRFJKJBKZhMjAwyMDA4Kx9Tk1NsXv3G7O25XI5jnUd51jX8esyNizDIDbQi6ZqHN/+IkvuuO+GLlReKOTY+fQPWbJhI5sffpj+g/t5+Rt/KwexuHVoKtR4ocZL3TEfpX15LNXCYnryiWIpKKaCcpNUt+OX59O2uWlW0OjVL+/lzCtDc75uvX39dC7roKGhjlg8TjA4NvP9cY7H68Fms6MwnVpWVVRaWpspLy9D01QcDgcOh/2C911MIpEgGBwjEolQXl7OsmWd9Pb2kUgkGR0Nkk6nqaqupLy8nHg8gdvtYkH7AizL4uSJbuLx+MyNFw6nE4/Hg8PhmJV29b2aCIXIZXPU1NTQ0NhAOp0hUBqgtrYGu90ux7sQQgghxC1IAkdCCCFEkQhNhAhNXJgabnJyil279lz0Pel0hpMnu+npOY3dbseyrNl3JAOpVIpDhw5z6NCFa08MD4/MzKI5JxaL8ebefbO2vf39F3ufYRj09Jymp+f0zDafz0ddfS12h4OeU6cZGBzE7/PP3KGdTqcBaGpqxOlyvnPjWG+lqRscHLroLKiRkVFGRkYvqY6JRJKDBw5x9GgXCtN3cV9O6rzrKRePERsZ4IzDgcsfoHXFGlRVu+7lGO7u4ujubXg8Lmrnt6EoCm1r1/HBL/4Jx7a+xMDBA3Iwi5ufMvM/FJTp2USGclNWtfXOBjZ+fuXM44mTU/TvGLkpgkYAQ4ODxGJxKqsq6e8buHBtQCARTxCPxygUalizdjU1NdVUVVeRz+dJpzM0NTXSvrCdkydOXvJ+e3v76O4+RWlpKRs3rmd8fIKentMsXbKYhYsWct/999LX109VVSX1DfUMDw3T39dPPD4dfMrn8zQ01HPHnbdz8OAhLPPyv8N6ek6zfHknPp+PBz7wPlauXIHT6cDhcKAXdIr861EIIYQQQlwDEjgSQgghbgKGYbynmTvXi8PhoL19AatWrWRsbIyenjOU+P0sWDCfcChMf/8A2WyOQCCAx+N5x8+xsMhkMle1bJZloef1OdXP6XAIC4UuLLKJGEvvet913X/vgT2cPPwmU72nGRgfAT3PnY9+CoDGjk4aOzr42Vf+GyPHj8tBKW5a+/9Xxay/TmUH8zdtXVs21XHfX2yYte2ZL7w6p+uUSaeZnJwinkhiGgaZTJbe3j48Hjc9p06TOru+USwWIxQOk8vlyGazvL5zN6qqUlVVRWNTE0cOH6G7+xSbbt9IU3MTLS3N9PcPEI1GCYW9ZDLZWTckZDMZpqamSKfSM2sX7X1jH1VVVdTW1lBfX0doIsQLL7xEIpGkbf48OjqWYhgGJ090s2P7TkKh6VS1p7p7aG1ppqW1hYaGegYHBhmfCBGJRFE1bea77WL7BEgmkoTDkyQSSQzDJJVM8fJLr5BKpqlvrMeyTPbsfoOW1hY6fb6z9ZDokRBCCCHErURZvWqDnAEKIYQQ4prQNI3mlmZWrFhGVVUVDocDwzCIx+OcPn2Gkye6SSQSqKr6rml2LMvCsixZpHv69A13RQWljS185At/dl1SYeUzaY6++jzB4X4iA72kQuOcuwV90Z13cfdvP36upwCIhyZ48t9/UbpK3JT2f61y1uPVvx++KevZ8cvzZ800CnVHeOb3Xr2imS1znaqquN1udF2fmamqKAoOh4NCoXBVb+BwOp04nA5y2dxFZ8UqioLb48Y0TPL5/BV9P9psGl6fD5fTRSKRIJPJYLPZ+NhDD7JsWSeHDx/h5ZdeIRKJyB8AIYQQQohb5ZcHCRwJIYQQ4pqebCgKXq+XiopyXC4XhUKBaDRGNBotyllSc8XqBx8hk89RN6+dlsXLsNkdqNrVTV1nWSahgV6O7XyZyFSY+OAA2djURV/7sb/8EpUtrefeCcCBZ37Cvqd/JJ0lbir7/7bqvIMEVv9+6KarY+udDbNmGv30j15j7GhYOv8mVVVVyUc++mHa5s9jx/bX6eo6RmVlBfffv4WSEj8/ffY5Dh0+Qi6bk8YSQgghhLhFSOBICCGEEGKO+fD/8yXqF3ZMpzDau4O+rgN4FI31H/sEvvLKq7KPdDzKoZ//mGgiRmJ8lOhgH9YvCPQ5vV4e/fo3zttiYSnwxKc+JZ0mbhr7v159bngDsPr3Jm6q+j328kOzHv/DB57G0GW25039o4CicPvtG1m9ZjWBQAmKosysmdjVdYwd218nGo1KQwkhhBBC3ErniBI4EkIIIYSYO97/hT+ldcUaYDqF3JP/6fcwTANfTT3u0jLKKmtoaFtI3cKluHwl7+mz8+kUwTMnGRvqYyo4QnIqRHJslHwygXWJaZDqFi1iwcZNLLl7M5YyfZppGgX6Dxxg6999QzpQzHn7v14z6/HqL4zfNHV7+AcfwFf91npzz/7BNsaPTUqn3yKqqippbGzE6/OSzWYZHQkSCoXQdV0aRwghhBDiFmOTJhBCCCGEmBvufvRzM0GjWGiMJ//s8zPPTSW7cfhLSIVDhIJDdO18hYqmFqpb5+P2+HD7A9gcDjRt+vTPNA0K+RzZZIJsJsPEwGmmxkbJ5TLkEnEy4RDZRGxmLaNLFezuJtjdjdPjoXbxItwlJag2G23r1uL0fJFjW7cycOCgdKaYuxTlpquSp8LFwz/4AKqmzmz76R+9JkGjW0woFCYUkpSEQgghhBBCZhwJIYQQQswJ59LTnfPNxx965xM8RcHh8+MsLcflD6C5XCiaBoqCqqigMD2DyLIwTQMjmyOXjJONTpGPx7Gsq5eW6vF//Ie3bbF47r9/lZFjx6VTxZy0/xt1sx6v/lxwztfp7enpvvvA05gFSU8nhBBCCCHErUoCR0IIIYQQRe7t6el++B8/TzYRv7STPVVF1Wwomoaiqijq9IwCy7KwDAPLNDAN4xeuX3QlFt55B3c/9mnOLQqjAIlQiB9+8U+kc8WcErnNTe/jZTOPV39udGato7nq/PR0x54+ze6/OywdLYQQQgghxC1OlSYQQgghhCheJdW1M0EjgN79uy85aATTM4sMPU8hm0FPp8gnE+STCfRUkkI2g5HPX9OgEcCpHTt54tHfItw/wLkkX/6qSj7zj99hzccelE4Wc4uinE1XN7dT1q37zDIee/mhmaBR344RCRoJIYQQQgghpi97ZMaREEIIIUTx+uwTT838+9irP2fnD56Y0/Vxej08+o2/PW/L9Knotx59TDpbFL3Iaje9j1fMPF79u8NzcsbR5j9dy4ItzTOPB3aN8tKf75YOFkIIIYQQQgBgkyYQQgghhCg+D3zhT2k5O9Po3dYzmmtyqTTPfvmvWLBxA0vuuXtm+2Pf/Xt+9Bf/hamhYel8UcTOzTaa284PGkl6OiGEEBbFrpoAACAASURBVEIIIcTbSeBICCGEEKLI3P3o52aCRjejYPcpgt2ncPo81C1aiLukBMWu8dCX/4KRrhM891f/UwaBKGLKTVGL1GSGiWOTEjQSQgghhBBCXEACR0IIIYQQReQj//7/o6596czjrlefv2nr+vLX/x6Ax//prfR7DZ2Lefz73+K5v/prRrpOyIAQxWeOzji658/WMf/eppnHP/z156QvhRBCCCGEEBelShMIIYQQQhSHB77wpzNBo1wmzUvf/Cqv/+DbN329n/jNx3ntW/8wa9sH/+SP2PJ7n5FBIYrM2VR1ytxKWXfPf5wdNHry489LVwohhBBCCCHekVZf1/iX0gxCCCGEEDfW3Y9+jgXr7gAgFhrj/3zxMSKjt856P5ODQ1imicPtxlNWCliUNdSz+mMfRlVV8tks6WhMBoq4oYwGB/paH05FwakoVDwbLfoyr/vMMpb8UhswnZ7u+w8+Sz6pS2cKIYQQQggh3pGyetUGS5pBCCGEEOLGeXt6um8+/tAt3R6Pf/9bwPmnqNP/zqXT/NNn/1gGjLhhsmu8RH+nBoCCAo2P9c4eqkXm7enpvn3fU9KJQgghhBBCiF9IUtUJIYQQQtwgTZ2r+OwTT9GwcCmqopDPpvnHP/qtoiunpmnYbDY0Tbsu+3viNz7Ds//1q2cfvfWrvNPj5pf+0x+z5N47ZfCIG3TxpOBQVByKimcOXEqdHzR69ct7pQOFEEIIIYQQl/Y7gKSqE0IIIYS4zidgdjvzblvPA5//D6gKlLntkIryb3/574hHI8VVVk2jrr6OxqYGnE4HsVj8uuw3GZ7iwNM/payxDpffh93pxAJ8lRW0rFrGig/fT3xsgshIUAaUuH7cKuZdJWgKaArYRvOoo/miK6an0s1v/PjDKGfXYerbMcL+7x2X/hNCCCGEEEJcEklVJ4QQQghxnX32ibfSRdk1hZW1fnq2v8C2bdt5Y09xzQpwOp28/4H76ezsoLv7FE/936dvSDkWb76dOz79yekTWMUC5dwzFk984vMyqMR1k/3uwvPGH7h+61TRlfGxl99Kd9n/+igv/8Vu6TghhBBCCCHEJbNJEwghhBBCXD8P/flXZz1+8Vv/k302C4wCQ4NDRVfeQqHA8eMnCIcmmZycvLIPU7js9WBObnudUzv28OCX/gMVrQ2znvv0P32NgX2Heflr35EBJq45u6LMChwVm4d/8IGZf3c9fZo9f3dYOk0IIYQQQgjx3i7fZcaREEIIIeb22cx5v+Ba1hV+lILT6cTv92G32wHI5fMk4gny+TyKouB2u/H7fei6TiKRJFAawOlwUCgYpFJJksnUzOc5HHYCgQBOp5N5qzey6sFPYFoWDk3FtCye//bf0rNrG+XlZaiqSjKZIp1OY7fbKS8vxzRNIpEIfr8ft9uFZUEmkyYWi2OdrauiKHi9XrxeDzabDdM0yWazxOMJDMO4pHoBb+3TMolFY5SWBlBVlYmJED6fD7fbRTabIxaLoaoq5RXlqIpCPJ7Abrfj83lRVJVcNkssFqdQKMzs2+/34/V60DSNXC5HIpHA5/OhqirxeIJsNvue+qmhYxEd79tMy5rl522dbo+tX/sOwZNnyFynlHri1qP9w2Ks8/7smJ86WRTl8lS6efgHD6Cq02sv9e0YYet/3iMdJoQQQgghhHjPZMaREEIIIeYU1e7A6fVj9/mwudyoNhuKomKZBoauo6dT5JMJ9EwSyzAv+XMVRcHj9XDbqpWsWbsar9eLZVnEYjF27txF19FjAHR0LOV977+PsbExdu96g3vuvZv6+noy2QwH9h9k68uvksvlsNlsLF6ymC1b7mVB5zL8Da1EMgUyBYNal8JrP/oXune+gsfj4Vd/7Vfw+31s27adN/fuo7Kygk8//ltk0hme+clP2bhpA/MXtKEoCqe6T/H8cy8wOTmFoiiUl5ex6faNdHQsRdM0LMtidDTIa9u2098/APAL62UYBlVVlfz2Y58im8ny0otbef8H3odN0/ibv/5b7t5850yquh899WNcbhePPPJruFwuXtu2ndq6WpYvX4bb42ZkeIQXfv4SZ870oigKgUCA+9+3hWXLOrEsi2AwyKFDR1i7bg0up5OfPvscJ068tx/eR451M3KsG4DH//nvZj235fc/DVic3LaLHU/8UA4YcdVpCucFrBWKZYWjjz/5wZl/978uQSMhhBBCCCHE5ZPAkRBCCCGKm6Lg9PlxV1Ti8JXg9PpQdJ2yuiZ85RU4PV5UTaOg62QTcRLhcZLxGPlCnkImQzYaIROZpJDNvPtJkc3G8uXL+OAvfYBIJMprr+1AU1U2btrAhz/yIWKxOMNDQ2iaSkmJH0VVuOPOTYTDk+TzOm1t81i/YR3DQ8McPXqMQCDARx78CB2b7iJZUAgm8vgcGoFCkrFjp3G7XWerp+DzefH7/TgdDmB67kyJ34/H7eGuu++gUCjQc+o0Cxe2s2TJYmKxOM8+8zNcLhdb7ruXVatWMjg4yIH9B6msqmT9+nWUlgb4zre/Rzab/YX16u/rf2ufHg8bN60nk84Qi8VAYXqWVcn0cwAK0zOvyspKuW31KkzToqfnNPPnt9Hc3MT73n8/3/rmt9E0jdVrbmPNmtWkUimOHu0im82yYcM6qqoqKRQMVE29ouHxxCc+T/ud69n8O79xdsv0zKPFmzex8M51/Pj//SqTAyNyHImrdwGlnDdmFYoicCTp6YQQQgghhBBX9bpHmkAIIYQQxUhRVRz+Enw19fgqqiitqqGiooa6RR34yitRlHdeZMTQdaLjIwx1HSSWiJGITZEcC5IKjaOnUxd9j8PpYOOmDRgFg6NHujh44CB2uwOvz8vtt29k7drVDA0OYVoWiqLgsDvYv/8g+/cdoKSkhM/8zmOUlZbSOq+V48dP8su/+4esuOsO8obJyXCCaDrLxInDzNeS1NbWvOtSP5Y5/axlWUxNRfjx08/gdDr55Y99lBUrl9Pa2jo9m6c0wMqVK8jn8+zcsYu+vj5cLjeNjY00NNaztGMJXV3HfmG9+vv6Mc/NzrIgHk/wb//6FLqu43K5Ll7Gs+2gahrf+973MAoGa9eu5sFf/ghVVZVn0/kVWLlyOYZR4MTxkzz9o59gs9mYmAjx67/+KxQKxmWveXS+nh1v0LPjDbb8/m/Ttn7VuRKiahof+/KfMHLsFMdffI3+fUflwBJX/rdJUVApjmWOPJVuHvnBB1DU6dL07RiRoJEQQgghhBDiikngSAghhBBFR7M7KG2dj7u8gtr6Zpbd/X6cXt97eL+disZWKhpbAYiOjdC1exuTE6OkgqMkgsNY5uw0dg67g5rqKgzDpKammrs3342qKlRUVKBpGg0N9bNer+s6x44dByCfzxMcDVJeVkZpRSWf+ea/0lrqAix00yKZN/jO7z6M0+nkgQfeR21tzSXVo1DQOX78BJZlYRgGQ4NDrFy1Yma9oJqaamw2DdMyaV+4gOaWZmyahtPlnCnzqe6e91yvI4ePouv6JZTP4EzPGbKZLKqqMjg0DJxL++clmUhQXl5OoWDQ199/9j0Fes/0XelyVBe19WvfZSvwgT/5HI3LF89sb1jWTsOydgCeeOQP5AATV3gBpYACWhGURdLTCSGEEEIIIa7NdY8QQgghRLFQFNzllZQ0NlNT38TSTfcSqKl/19lFl6K0toGNH/41xvt6OLT1ZzhLAkQH+9BTybO7VbDbbWdn0KjU1tUQKA3MvH90dIxkMjnrM03TJJvJzjzWdZ3aBYvxL13LiXAam6pgASOnjvPUV/8aAAuLvH7pia0syyKdzsz8O382mHOuPRxnU9vZNI3Wea0YhjHz3rHgGPlc/j3Xy7IsEm/b9u7lS8/8W8+fDTYpoCoKmmbDZtMoGIVZgSjDMGaV9VydXC4XNpsNXdfJZqfb1m6343K5sCyLVCo1M9PJ5/NhYZHL5i4Icj3/V9/AX1VB7eL5bP7dT5xfYh7/4d/Qs/1Ntv3vf5bjTVyW9NeH8P9+MwqgAvZ2D3pP+rqXQ9LTCSGEEEIIIa4VCRwJIYQQoigoqoq3toGKtgW0LVpG+9rbUbWrd6qi2ezUty+lsrGFw9tfwu72MHmmm1w8hmVZ5PJ58nkd0zR5+aVX2PfmfmA6cGGz2TCMwjt+9qI77uX2Rx7D63eQSubBgujUFIG8Rnx0kPj49Bo7mqpRVlZ2VepjWRbpVBoLyGSyfP8f/w+hUHg6jZ7Dgaqq6LqO1+d9j/WyMN82G+uyygfoBZ1MJoumaQQC0wErVVUJBEqw2bRZAR+bzcbKlSuoqa1meHiEfW/uR1VVamtrWLtuDblcjhdfeBld13E6nWy57x4Mw+TQwUMMnZ3pdL5EaJJEaBJfRSmta5dR2dY881z7XWtpv2sNW//me5iGKSnsxHui743NzDoCBa3cgc71DRz99gu/PGt9MAkaCSGEEEIIIa4mCRwJIYQQ4sZTFEoaW/AESlm7+YNUtrRds1053F7Wvv9Bjrz4DKgKUz0nycai6Pk8Q0NDNLc00zavlVOnesCC5uYmKqsqGRocYmBg8ILP++wTT2FTZ8+IsrD4v//jv/DJ3/w4Xq+X5cs66evrp6y8jNbWlqtSD9M0GR8fJ51KY7NpLFzYTiabxW6zs2jxQux2OydPnCSVSv3CevX29l2TtjYMg5GREdrmt7FkySJOnDiJqqqsWLn8glR1NpuNhYvaWbRoIS6XayZwVFlZwYYN60gkkmx9+VV0Xcdut7N+/Vp0XWd4aPiigaNzDv74RQ7++EUcbhePfve/zXpuyx9+aqbHnnjkj+U4FJd+EaVMzyhE4bovdtR6R/2soNF33/8j6RAhhBBCCCHE1b3mkSYQQgghxI2kKAq+ugbcvhLWf+hXr2nQ6HydWz6IfbeHbl0n0n+GfCrJnt17qayspHNZJ4qqks1m6ehYiq7rTE1OzXp/w9LlPPrX37vgc0/u3Mr//spX8Hg89PcN0NTUyC99+EMMDQ3jdrtIJlOUlPinp+RcAcuySCQS7N37JuvWreWeLZuprKrE6XTS0bGUcDhM98lu8nn9PdXratLzOm/u3U9tbS3Nzc188pOPEIvFcDicFN4208k0TaamphgdDRKJRGbqmM5kGBkNkk6lZ2ZCmabB6GiQQqFAOpO5pLLkM1meeOQPaVu/ki1/+Oj5LQnA4z/8H/S+cZhDT7/M5MCoHJjiXWln00VaXN+4UcfHFrDxcytmHj/7B9swDUs6RAghhBBCCHFVSeBICCGEEDeUq7yC8tYFrNr8ALXz2q/bflXNxuJN92IqCj0WhE4e5dix42iaxpq1q2lobMAyTcbGxtm/7wBHjhzF6XZT1jyfys615A0LVyoBgK7nGewZpGfwFL2nzwCQzWb5yU+e5e6776SqqhJd1zl27DjlZWXU19eeFwQxmZiYIJVKkUymACgUdEZGg2QzWfT8W2sipVNpRkdGSSSm1yDK5fJse3U7uq6zZMliFiyYj2EYnDzZzc6du5iYCAH8wnopivKO+zRNk0gkSnA0yNTU1DuWeXa5M+h5nUKhwLFjx/F43HR0dqBpKqFQmKNHuvjMZx+b+azpuuR49pmfzeojwzDoPnmK7pOnZm1PpdL87de+cVn93vvGIQYfPcbmz32ceetXzHqubf0K2tavAMXi5b/5J8ZO9pGJJuQgFRcwpwrYKuwAuNt9ZPZEr8t+zw8avfpf9zJ+bFI6QwghhBBCCHHVKatXbZBb1IQQQghxQ2gOJ1VLltG+7DY67n7/DSvH6099n2B/D5OnTmCZJqqq4vV6ME2TdDqDZVl0bvkQtz/82xd9/zcff+jCumkaVVWV2Gw2otEYqVQKr9fLRx/8MB0dS+nqOsYP/vnJq1YHm03D4/GSz+fJZrMXfc3F6nVNTzQVBZ/fh9/nw7IsJiZCWJZFc3MTn/v8Z0kmU/zDd//xXVPNXWvTKey+zPlTwCz1rX+fenUv2//+X+VgFbNU/cE8fBvLzs42Uuh9eP813+fb1zX69n1PSUcIIYQQQgghrgmZcSSEEEKIG0JRFALNrdS0trH0zvtvaFk6br+XRDJOOjRBejKEaZozs3ru/50vUr+oE5fPf8H73vzJkxz46b9d9DN9Ph+PfPzXqagoZ9euPRw50kVdbQ3t7QtIpVN0d5+6qnUoFAzi8fi7vub8el2XE02bjZUrV3DfffeSSCR45pmfUtALbLp9I4WCwcTEBGNj4ze076dT2P0x8zas4L4/+E1Qzg+mWSy8Zy0L7rqNgTe7GD12hhMv7paDV6AqCirK2f+uvQ9/bfNM0Gji5BTP/t6r0glCCCGEEEKIa0arr2v8S2kGIYQQQlxvDp+fstb5LN90L97S8htaFpevBCOTJhaLkIlOomo25t22jl/7z/+LsvombA7nrNf37t/Nv/7FHxI8dfwdP9M0TUzTpLq6msbGRlatWsH8+W2kUmm6jnSx6/XdM2nablaW9f+zd+dBetz3feff3c81933iGNw3CBIESfGQKImUZJ2RJSex7GzsxCVv4t1UbTaJt5I4lSgpH7WJ46SyW7tZx1WRE8e2LEty5EjWSUmkeFkECRLEjcE1M5j7nnnu7t4/BhwSIiWREjAzwLxfVVN4uvuZ7t98f/2gpuszv98vYWFhkZ6ebhqbG7nj0EHuuOMgDQ0NDA1d5etf+yaTN3GNpbdiZnCU5z/3NeIoorSQp3VTz/KxIAxp3dRN3937uOun38Xs1XGmB8f8EK9jzfe3UbO5liBYWu9o4k9v3rpYXfvaOPKL+5e3X/7cOUZPTNkJkiRJkm4ap6qTJEkr/wtIENC+az/bDt7FkQ98fE20qVou8a3PfJr23g0cfs9H3vA9+dlp/us/+uSbPmcYhrS1tdK3pY/amhoWF/OMjo4yPDyyrvo7m82ydesW2tpaAZiemeHSxcuUSqU13e73/5NPsumuPbwyjd3SyJJXf3WuFIt8+hf/hR/odWrfZ+4FAkJg7tkpBn7n/E25zie/8epUmN/6jb+k/1sDFl+SJEnSTeVUdZIkaeV/Aamvp7a1lZ33PLh22pTNce/7/wrHn/o2Ccl1E1B977//MZdffI7JgYtv6ZxxHDMxMcnExPpewL5cLnP27Llbrt1f+a3fo7GzjZ59W3nX//oJXhsaQUKmJssvf+a3uPDMcb757/7QD/Y6EwbB8hpH4Q+asC5YChx/nOXEOve28dH/8K6l2y6Ax379WS58e9DCS5IkSbrpDI4kSdKKq21uo66+kYbW9jXVrpbOXtLpNLMTY7R0dHPh6NN8/T/+th22js2PTzE/PsW5x59nw4EdHPjAg2y99+B179n+wB1se/A3efEL32Hi4hAXn37Zwq0DxXOLNOxuJEkgXZ8i05gi3RCSrg9J1wSE2RBSAQEJcZQQVxKiQkJ1Maa6GBMVY+JKcn0e+dr76uGNECwFUvnxAhcfH7LokiRJklaEU9VJkqQV17HvDg6+7WH2PvTImmvbhZefZ2LoMs//6R/YUfqBfvkzv7X8OgmvH4kE8NXf/C8MvHDGQt3GWu5vo+9v9ZE/NU3h8jzJYoW6ljqikTRt25qo76wlW58BoLxQZWEsz/SlWaqZIlGmSlSMKY5VWByqUJ2/fr2zR37tPra/a/Py9u+993MWXJIkSdKKccSRJElaUalcDXUtrfTdcfeabF9cKjJ4+mWCMCSJYztMb+g//ew/IUyFbLnvAI/+g5973fGf+qe/wMLYNOP9gxz7wneYvDhs0W6b/8SgtjtDNlNh/ptX6Xv7Ftru3UE2ylDTkiNTmyYI3+gbA5I4oVKoUpguMnVhjtH+MUbqxklSEYuDZYpjEe/+x/deFxr98d/4C2suSZIkaUUZHEmSpBWVrasnHaSoa2pZk+2rb2ollc0RptJEcdkO0w8URzEXnz7O7/214zz6D36enr1bqG1tuHY0oaGrhYauFrY9cACAb/7OHwNw8ekTFu8WFISQbUnTsC1LQ3sd7d2d3PE3d5MNU2/yDMnSOerTZOsbaN7UwLaHNzAzsMD5b1xhsnWCxak8rbsaSKKYIBVy8YlBFsbyFl+SJEnSijI4kiRJK/vLR10dDa1ta7Z99a1tEIYEqRRU7C+9Od/8nT9cfv3Jz/76a468Oo3do//gZ5dfV8tlPv03ft3C3UIad+Ro2l5DS0MnR/7mPrINmRty3pbNDdzzt/dTmqswPzXPU3/4PXY9uJ1MtYZv/vqzFl6SJEnSiktt6N30KcsgSZJWSl17F90799C7bffabGCScPn0yyyOjxKVHXGkt+75zz7G1eP9RJUqnTs3vv4NAaQyIUd+9t3sfvdhuvf2MT86RX56weKtQem6kJY7a2nra+HgI/vZ95HtpHOpG3+dXIq6llp2P7SDS88NcPqx85RnYpKqS9JKkiRJWuHnIEsgSZJWTBAQplNka2rXbBPDTJZ0JksQpuwv/dhGTl9m5PRlnvy9P+fOjz1Mx7YNbHtg/9LH4DWjkBq7mmnsamb7g/tJSPjmb38WgItPn7SIa0CmKUXTrhwb7+jmwHv30tC1Mv937XlgFwuXyoS5SWZPlKjMR3aGJEmSpBVjcCRJklZUEISEYbh220dAEAYEQWBn6YZ48QuPX7f9y5/7l697zytR0qP/6K8t76mWKnz653/LAq6SsCag9VANzfUdvO1vHF7Razf21vHQrxzmiX/7PMEd00wfK1DNx3aKJEmSpBXhVHWSJGlF1bS00b5pC91bdqzJ9kWVMheOP8/8yBBRqWSH6YZ7/k++zQt/+jhTV8YozuWvTWf32unIll6H6RR3/+zD7H7kLrr3bmJ+dMbp7FbqIak2pO1wHRt3beDeXzhIKrvyYXcqE7Lpvh5mzi9QzZUojVdJzI4kSZIkrcTziMGRJElaSbnGJpo7e9i4c9+abF9pcYH+40dZGL1KXKnYYbo5koSZwXEGnj/H83/ybaJqRHmhSOvmzte+CYBsfQ1tfR3se9/dHPnZh5keGKd1cyczAxPW8SYIUgFNe3J07Wzn8McPkmvMrN7DWjqkc2cbE6dniHNVSlMRGB5JkiRJusmcqk6SJK2oqFgkPzezZttXnJ8jqlZIqq4popXz4ue/u/Ti3y7988nP/fNXD37frInv+dWPL7+ulir850/8awt4A9VtzNDUW899nzi8qqHRK2pbcxz5xEGe/sxfUpqqUhgy0JYkSZJ0cxkcSZKkFVXOLzI/MUYcVQlTa+9XkbnJMZIoJo6qdpZWze/9zL8iCEO2vm0vvQf7OPCBe64duX5Ku3QuzS9/4Z8CCReeOg3A8IkrnPzyUYv4Y0jVhDRuy7H98LY1ERq9omljAzvv2kW1cIbKTER10WFHkiRJkm7is5FT1UmSpJWURFXqujfQ0bOR+pa2Nde+of7TjF44R2FizM7SKn9YXpnO7jzPf+ZxokqV0uL3T2f3qtbNHbRubqfvyA7u/sTbufvnHmJ6YJLWvg7Ki0UqhbI1/SGCkKUp6rZ2cehjuwlSwdppWwDt21oYPTlJqVyiNGGwLUmSJOnmccSRJElaUXEUUZqf49wzj9O5Zceaa9/Q2ZMUpidJksTO0pry4uefAuCb17aXRhr9cI/+6kd57SilaqXKp//a71jMN5BqCKnrzbD7XdsIM+Gaa18SJOy4fyszUzMsXCwTFR11JEmSJOnmMDiSJEkrrjg9ycLGzZTyC+TqGtZMu0b6z1AsFihOT9pJWvP+08d+kyAM2Hr/HgDe86sfe/XgDxgsk86k+OU/+1UALjx1Zum+PznAif/xwrqvZ31vhvpMM23bmtdsG3vu6KD5qVYWt5SZP1u6fuZCSZIkSbpBgiOH7/dxQ5IkrahUNkfn/kPc88gH2bTv0JpoUxLHfPezn+bqhXNMXzhrJ+mW1XtgM/s/dASAbQ/u4fvXRQrgWrCULO97xfDJweUQ6eKT6+dzEOYCOo80cO/PHKb3zo413dahl0Y5+sWXmHhmgajoo5wkSZKkG88RR5IkacVF5RKLYyO8+I3/wYbd+wlTq/8rSX5+htmZKfLjo3aQbmnDJwYYPjFw3b73/drP0Hfva6eGfH3gkAQJPQc20nNg47XjHwLg67/xRS4/239b1yzXmqaurXbNh0YArRubydWnSTekiIqudSRJkiTpxjM4kiRJqyI/PkJ9ZzeXThxj+6F7VrUtUbXCM5/9ryxOTVBemLNzdNv52m98DoAgDNj2wG4AHv0/PsKbmevsvb/2kWuvEkZPDZOfWlg+NnJyiBN//uItX59ce5r6+sZboq21LTkyC/Xk2vOUJgyOJEmSJN14BkeSJGlVRJUK88NDnD36FN1btlPf3LZqbbly4hjz5SLzw4MkiVM/6faVxAkXnlxa2+jCR5f+7dm/iQMfPgxBQkNnE527e37g93fv6116ESx9Tra9fScP/p2Hl85NQhInPPavv7r8/rFTwyxOLq79h6LGkI4trbdEHwapgC0P9TL7zWkISxB7X0uSJEm6wc9IlkCSJK2WxbFhck3NvPCdr3H/+z9GOptb8TbMjY9y/vhR5q5eoVoo2Clad0ZODjJycvB1+z/5xf+dNzMi6bWCMODRf/x+3mj9JEjo/845vvVvvrGmfv4wHZCpS9G2vfmW6bOeg+289NWAVC4gKhh2S5IkSbqxDI4kSdKqmhu8RLqmlqN/8Xnu+dBfJZXOrNi1i/OzPPulzzI5dIX8mGsbSa/1e3/l3wHQvW8DdW31ABz6mSN07ux+zbveWmix45272PHOXQTXRixd+G7/dec6/vmXGDsztqI/Z6omIMmH1HfV3jJ9U9teS21LjrlcgagQebNKkiRJuqEMjiRJ0qqqFotMXzhLEIa8+I0vced7PrQi4dH85BjPfvFPmBkfYW7wCjhFnfSGRk9dXX598clz1x3rObCBAx+589pWQhAEbH1o+5s+9/Z3vPa9yWu2Xz9iKbkWNh3//HHGTl8fLgXX9O+sZAAAIABJREFUbV0LpZ64/KbaENaEZDIZsnWZW6ZPwlRAtiFNKhd4g0qSJEm64QyOJEnSqqvkF5k6fwaCgKnBS7zrF37lpk5bNz8xxhOf/TSLhUWmLpwjiVxgXvpxjJy4ysiJqz/w+Ht/7f1seeD6IOmV0UY/rjs+fvA1W0vnCl7z+pX1lx7l1bDp+6fMe+3roRMDpHIhYfrWCmFSudQt12ZJkiRJtwaDI0mStCZU8gtMnTtFvHUHT33xM+w5cj/d23bf0GvE1Srnjz7FpXOnmJscZ27oiqGRdBN9/Te+ct12Q2cDXXteneru0X/6njd1noSbNyIwimJSya0XwARhSBJ6j0mSJEm68QyOJEnSmlHJLzJ59iSVfJ6pq1fYfue9bDt4N43tHXz/ZFRvRRJHTAxc4vSzTzA5dpX54SEWx0ednk5aYQvjCyyMLyxvX/hg/3XHD338EJ17Ol/dsfyxf/Wz2rW/i/r2ujfzyX9TbQpSt+ionTiB2HtKkiRJ0o1ncCRJktaUuFpl5tJ5so2NRMDA+dO0N7dx+P0fJdfQ9JbPN3nlIs99+U8pp1LkJ8eZvXSB2FFG0pr00udfumHn+uWv/MKbel82l4Hg1ktgquWIJPKekSRJknTjBUcO3++f2kqSpLX5i0oqRa6pmbqObho6uqhrbKKlrZOm1g5aezdS19RCKpslDALiOKZcLLAwPcnUwGUKlSKTg5fJ5xfIj4+Rnxijks+TJP6JvqRXZZpStGxt4AP/6mFyDZlbos1JDF/77ccZfW6G0pRBuCRJkqQbyxFHkiRpzUqiiOL0FMWZaeYHL5NrbmW8sYlUroZULks2V0s6lyMMAqIooloqUi4WiCoVqoUCpblpSnOzROWyxZT0hqJiTKYL8pPFWyY4KsyUKBcqREWDcEmSJEk3nsGRJEla+5KESiFPpZBnYWRoaV8YkEplCNNpkiCAOCauVomiKoFrF0l6k+JyQiVfZfryHK1bGm+JNk+enSaqRkRF/6+TJEmSdOMZHEmSpFtTnBDFZaLK9aOJAisj6S2qLsTMTc0CG9d8W5MELj01THm2ShIbHEmSJEm68UJLIEmSJGk9K01VmR6ehVsghynNlSnl5im7tpEkSZKkm8TgSJIkSdK6VpqKKMwVmLkyv+bbOn15jsJcifKs6xtJkiRJujkMjiRJkiSta1EhZnG4xLE/Pkscrd1hR0kCA8euUhytEBcNjiRJkiTdHAZHkiRJkta3BApXK+TDmTU96mjy/AyT45MsXC6TuLyRJEmSpJvE4EiSJEnSuleejShOVLj47JU128bLxwdYHC5SnXe0kSRJkqSbx+BIkiRJ0rqXRAmzZ0sMnRhm8LnRNde+4WMTjJweY/Fi2c6SJEmSdFMZHEmSJEkSUJ2PKEyWOf+9i5QXK2umXQtjBZ77o+PMXypSno3sKEmSJEk3lcGRJEmSJF0zf6HE9JUZnvwPLxJVVn9KuCSGl//HGaJUicUBRxtJkiRJuvkMjiRJkiTpmqQKs2dKzFenOPUX/SRxsqrtOfGVM4wNjDN9vEhcSewgSZIkSTdd2hJIkiRJ0qsqcxHTxwtcCi8TpgP2/dQOglSwom2oliLOfvsil48NMH2iQLUQ2zGSJEmSVoTBkSRJkiR9n/JUxNTxPElwkfmhIvf84n5S2ZWZsCGOEo5/8QyD54eYeiFPedp1jSRJkiStnNSG3k2fsgySJEmSdL0oH1OeiajkCowfn6F1YxM1zbmbes3ZwQWe+8xxhs+PMn2sQGXO0EiSJEnSyjI4kiRJkqQfIComlCciokyZicEp4vmAxu46UtnUDb1Oaa7Mxe8OcvK7p5k8P8PMySLVBaenkyRJkrTynKpOkiRJkn6IaiFm6sUCxYkqxWKRc09e4tCH97Dp3u6f+NxxlDB0dIyzT55ncWGR+XNF8sNVSKy7JEmSpNURHDl8v48kkiRJkvQjn54gVRNStyFDXXeGxu4GOjZ20LOvk7ZtzW96DaRqMWL60hxXnhlhdnGaxalFFgZKFIYrRAUfzyRJkiSt8qOPwZEkSZIkvTWpmpCarjQ1XWlyzWnCYpaO3a209jWRSefoumcj4SvDhgIggclLY5TLCee+dIZSpUh5rkphrEJpLCIqOS2dJEmSpLXBqeokSZIk6S2KijGLV8osXikTZgKyrWnmJhfIvhySqgtJP30K4pgYSKVCUtkMuYYa6rpaGDk2SXkqIq5aR0mSJElrj8GRJEmSJP0E4kpCcaxCcaxCEEKQCnjg//qrJElMAox84zR7P76XMJ0iSIXMnS1z4c9PWzhJkiRJa1JoCSRJkiTpxkjipSAprK0hVVdHuraOhcFFUrkMQRhCAq17OiyUJEmSpDXLEUeSJEmSdIPFSbD8OkkgXl7uKMBFZiVJkiStZY44kiRJkqQbbPzZSyQJRAns+rsP8/xvPwlJQJJAz32bCYLAIkmSJElakxxxJEmSJEk3WJIExHFAAMsjjJZHGjnkSJIkSdIaZnAkSZIkSTfYa6ene3WndZEkSZK09jlVnSRJkiTdaMm1UUfXvkaeHSCOl/YlScD7/ujnrJEkSZKkNcngSJIkSZJusPkL4yQJEEMSQ8P2LkgC4qVdDj6SJEmStGYZHEmSJEnSDTb6jZPEyashUdd7D/LVn//DayORlr56HthioSRJkiStOa5xJEmSJElvIAgCMtkMqTBFNapSKVfe0vcncbA8sii59iImWDPtkyRJkqQ3YnAkSZIkaU1KpVI0NjbS0tL8A98TxzELCwtMTU3f8OvX1NSwe88u2tvbuHp1mNOnzryl748TCFgacRQvJ0iv/Bv8xNPV/aTtkyRJkqQ3YnAkSZIkaU3KZDLs3LWDu+8+/APfUy6XOXPmLE8/9cxNuf6mTRvp6+sjjpMfIzh6/eiikacv033/VgAO/r13MPr05VVrnyRJkiS9EYMjSZIkSWtSKpWiva2NnTu3UyyWGBgYII6vH6cTRRFJktyU6xcKBV568TgX+i8yMzPzlr9//Jsv0/HIQZIEavo6IQiW1zdaC+17rSAIblodJUmSJN1aDI4kSZIkrXkzs7N89Stfp1qtXrc/jhPyhTxBENDU3MT27dso5AsMDg6xceMGWlqaqVSrjI9PMDQ4RBzHBEFAbW0tfVs209zcTBzHTE1OMTMzS9+WzRTyBfr7+wmCgHQ6vbSOUCr1lq4BULkyRVeuieZsPenmkIYjh8nMZkgFKSpJRJBAy+4u4pEiGzdtoLGhEQJYXFxk+OoIU1NTBEFAc3MT27Zvo1goMjo6Sl9fH9VqlXPnzl/XPoDa2lp27NxOHMWMjIzS2NhAV9dSaDU9Nc3AwCClUgmAbDbLho0b6OzsIAxDZmdmmZiYoLunmyRJGBwYYm5uzptPkiRJWmcMjiRJkiSteZVymeHhESqVyhseT6VS9HR389GPfoTp6WmOPvc8B+84QFdXF6l0ivPn+vnWY99mcHCIXC7HgYP7ecfDb6e1pYWpqSnGxyeYn5vn8N13MTw8wsDAIJlMhnvvu4cdO7Zz7NhLDA+PvOlrZLNZtm3awj3t22nN1VOKKmx65zuYn5unWtPGpfwYCbD3p++h5XjE3r27CcOQarVKKp3m7LXp92Zn5+juWbrmzMwMJ0+e4r777mVicpLh4ZHr2jc4OERzcxPve997ADh+/AS9vT1s3LiB+vp6rl69yhNPPMnxl14mnU7Tt2Uzjz76CBs3bmBxcZHR0TGmp2fYvXsnpVKJL3/pKwZHkiRJ0jpkcCRJkiRpzautrWX/gX1E1ei6/cVSkYErg1SrFQigrq6WIAzYtn0b/f0XGBq8yqE772DHju1MTEwyNHSV1tZWHnzwAVpbW+g/f4ETJ07S3NTEHYcOUldXS01NjoSEMAzI5bLU1taSzWaB5C1co4X7tu2lt7aFM3OjXF6YoL41Zk9tF40NG5kqzbMYl9nStpEDD/YyNTXNd77zBFE14q7Dhzhy5G7yi3kef/y7BATU1dWSSoX09fVx4sRJxscnAL6vfRCEIdlclob6erZv38bQ4BAjI6Ps27eHTZs3cc89d3P61Bnq6+s5cuRutm7tY3h4hKPPPU8Qhuzfv4/WtlZmpmcIw9AbT5IkSVqHDI4kSZIkrXktLS188IPvf906POPjE/zZF77I9PQ0XDtUrVS5dOky333iSZpbmmlobODQoTvo6ekmTIW0trbQ29vD9PQ0Tz/9LGdOn6G5uYmm5iZ6e3uWTvIGy/0kCW/qGqlUis7OTjb39FKsVhhcnGa6XCTuqWf2xTnaFzfRWdNGVJxix6Y+witVJiYmuTp0lXQ6zaVLl9m7dy+7du/imWeeXf6Zq9WIy5cu89hj3yaKItraWt+4kdfWUZqamuKrX/066XSaYrHAhz/8QZqbm6mvr6OurpYdO7ZTKBR5+eWTPPnk0+RyOSqVCjt3br9WAtc8kiRJktYjgyNJkiRJa16pVOLSpcvL6we9YnZ2lkqlfN2+YrHIwJUBkiShUq4wMTFxbfRQjnQqTWNTI2EYUCqVGRsdvXb+MiPDI2+6PT/sGplMmta2FlJhSDqVYU9TLzuTmFQQkmmMKA8v0LipjnFm6OjqoDg4Rld3F4++5xEA6uvrCMOA+vo6Ghsbl69ZKBS4cPEiURS9qXpdHbpKpVIhjmMmxicBCMOQXE0NmUyGxsYGZmfnGBsdA6BcLjM6MkqcGBhJkiRJ65nBkSRJkqQ1b2Zmhj//4peoVqvX7Y/jmEqlQhAE1+0rlZfCpCRJrk1vFxAEEAS8Zgq2ZHk0T/Ka12/GD7sGBIRhCoDF81cpdmepXAu8ZidGqVzsp+HjO0kICAho3NnJ8NUR8vk8APl8nvHxCebm5q/7eaMoolgovrn2Jd/XvteETUEQLH+9cvy1EoMjSZIkaV0zOJIkSZK05sVxTKlUolKpvOHx1wZHP0y1GrGwsECSJGSyWdrb25mZmSWXzdLV1XVD2hpFEQvz8yQJTF8e5i/DgPHiIqkgpHDyJQonLnPnTzVTn6khH5Vpa23k0qVL/PkXv0QQBGSzWZqaGimWSuQX83R2dr5ahxsR6iQJlUqFxXyeTCZDe3sbAJlMho6ODlKubSRJkiStawZHkiRJkta8uvo67rzr0BtO05ZfzNPff+FNnSeKImamZ5iamqahvp4j99xNNpuluaWZLVv7bkhbK5UK4+MTTE1NUd9Qz86mBgImqE1n6LzrMPMNnTz3j77IXb/zMUaK03Tmmtlz/0FOnTxNsVRi08YNbNy0kUsXL3Hs2Es3pZ6FYpGBK4Ps3r2Tffv3MjU1TSaTZv+BfU5VJ0mSJK1zBkeSJEmS1rzWlhY+/JEPwhtkGkNDV7l06fKbOk+SJExPT/Pc945y39vu5cCB/XR1dbIwv8D8/PzSe37CtiZJwuTkFM8++z3uve8I+9Ob6exqJBemaHhgCy8MTVIplynFFS4XxujINtHS2Mz7P/A+FhYW6OjoYGFhkfPn+2/atHEL8ws8//wLtLW1smHDBt73/vcyOTFBFMXE0bV1pMyPJEmSpHXJ4EiSJEnSmlStVhkYGOSZZ/7yh75vemp6aSTRzAzPPPOXzM3NkV/ML59jaOgqzzzzLJOTUwAsLuZ57rmj5AsFOjs6KJVKjI2P01Bfz759e0nieGk9oVKJs2fPMzMzy5UrAyRJ8hauschz3ztKPp9nT1uW+r4eFstlzpYW+e7j32Vubp7Rpy/C23ZwNO6nb2Mn409OAHDq1Gku9F/k7NlzP/CawOvaB5DPF3jhhWOEQcj42DjA68+RL1Aulzl39hwBAZv7NhHHMaOjYxQLRQ4c3P+6dZEkSZIkrR/BkcP3+3dkkiRJktaNmtoatm/fRl1dHZMTk1y+fIWWlmbe/e53cc+9d3PmzDn+y+//AXEc35DrdfxP76P5zl2U44hqHDP8T34XgJ1/52Ha79tGkEBAwAt/748gCCiXyjfs2j9INptlw4ZeOrs6WVxY5Ny586TTKe46fBc//bG/wtWhq/y3P/gjJiYmvWEkSZKkdcYRR5IkSZLWlVw2x/79+7jj0EEGB4Y4efIUzU1N7Nqzk6mpaU6dOn1Dg5uJP/ga6QNbIVma/a3559/H7B9+jfP/3+O037uNpSslbP6lBzj3/3x7RWqQSqfY3LeZRx55J/PzC3R1dZJKpdi3fy+LC4v0919genrGm0WSJElahwyOJEmSJK0ri4uLHD36AqlUira2No4cOUwURVwdukr/+Qscf+nlG37NJA6Wlwx67ZQP8/3jNGzvJAEyLfUQsCJrC5WKJc6cPkNLSzO9vb0cuvMO4jghn89z5syzHHvhRaeqkyRJktYpp6qTJEmStP4ehIKA+vo6urq7qanJUSlXmJmZZWJigiS58Y9IuQPbafzEe4GlXGjyn/8uJAmtd29h56+8eykrSuDo3/19knjlHtFyuRzd3V3U19eTJAlzc3OMj09QqVS8SSRJkqR1yhFHkiRJktadJElYWFhkYeHCCl0PSALiNxhR9EpOtRp/0VcqlbhyZcAbQpIkSdKy0BJIkiRJ0s1VPnmBKIE4hjiBlk/9HQCmn79MHCfXgiW4+//9RYslSZIkaVU54kiSJEmSVkCULC1hBAHxa/YnCa8cIIgDCyVJkiRpVTniSJIkSZJWwNyn/iNxEiyNOoohfWAnAC/8yu+TJAFJvBQotd6zzWJJkiRJWjUGR5IkSZK0QuIE4iSAJLhuTaPy1CIJS6OP6rZ2WihJkiRJq8bgSJIkSZJWSPV4P1GyNG1d5qfft7x/oX8MEoiB9kcOWChJkiRJq8bgSJIkSZJWSMzSqKI4CZbWNrpm/LETSyOQro1G6nzPHRZLkiRJ0qowOJIkSZKkFVL5wtdIkgBiiJKQ1Mc/AED+4vjSFHZAAtRt67JYkiRJklaFwZEkSZIkraAkhigJrq139Or+y7/7TeJkaURS451bIAwsliRJkqQVZ3AkSZIkSSuo+oWvEF0LiJJdOyF4NSBKuLY/sU6SJEmSVofBkSRJkiStoGR+keDaOkdxAvGenQDMvXiZJEqWwiNg37//JYslSZIkacUZHEmSJEnSSro6QhQvjSqKgORDH1g+dPJ/+88ESbC0DhLQdGS79ZIkSZK0ogyOJEmSJGmlH8R++/8migOIAyIg3rN7+djChdHl6epaHz5osSRJkiSt7POKJZAkSZKkVTA3vzRVXQJRb8/y7sr0IlwLjnJbOq2TJEmSpBVlcCRJkiRJq2FohCQJiJOA8l2Hl3df/f1vEQOvLHbU+7cftVaSJEmSVozBkSRJkiStgsyX/4Iogeja6KLihz68fGz6sePEQEJArq8LgsCCSZIkSVoRBkeSJEmStEqCq8MEcUCUBFTrG5b3Fy6NAQFJAunmegKDI0mSJEkrxOBIkiRJklZJ5ujzVK+NOCp39yyPLFp46RJJlBADMbD93/ySxZIkSZK0IgyOJEmSJGmVZPrPk8QxUQLEARP/y99fPjb8Xx9bXucoBmq2dVswSZIkSTedwZEkSZIkraL0+fMkcUCUQDWBqKERgMUXL1KdXlzKjpKAukPbLZYkSZKkm87gSJIkSZJWUeNXv0ycQJxAGAdUejYsHytcHCFJlqava3jHQYslSZIk6aYzOJIkSZKkVZY7d4Y4CagkMPnoh5b3zz1xguTaGkgk0PjOQxZLkiRJ0k1lcCRJkiRJq6z5G18muhYQVROYeO9HAChdGaN0aZSYpWN1h7ZDEFgwSZIkSTeNwZEkSZIkrQHtX/0i5WvT1c1t2b0cEM195yWWkiPIbuo0OJIkSZJ0UxkcSZIkSdIakB0fIYyhnECUwPC7PwxAaWCcOElIgDgJaP/5Ry2WJEmSpJvG4EiSJEmS1oDU4gK1F85STSCJYXrLbir1jUSzixReukiSBCRA7uA2Ui0Nr/v+pWWQAmJCYgJiAhIcnSRJkiTpLT6bbOjd9CnLIEmSJEmrr+HSWapN7RRbOokImNp/D10vPkXh+EUa33MEkqWAqDI4TnV0imqQYiGsZSpsZCJsYjy19DWZamY6bGAurCNPjpiQMEhIEVtkSZIkST9U2hJIkiRJ0trRdPIo41v3UgHCBEYP3Ev3ie+RP9ZP7uB2ZkbmGejaylS6SDmdozOdUKjrpFzTQjVTT5zKQgJBUiWu5IlLM5Cf5HIFMtU8jXGR5mSBxrjI0hgmSZIkSXqVwZEkSZIkrSF148Nkx64SdWwgAmY276Hz5FGufOZpxs4WKCUZirNpMh/+KOnLVUZq2onDzBueq3zt37m4Sq48Q93cEIWZEeby02RK83TGczQmedKJI5EkSZIkLXGqOkmSJElaYzLlIpNb9kESUI4DCv1DDKc7aGloYabnYajZQpyvo5ppIAlSP/qEQUg1XUehvody+07o2EFrUGauWGQ8aKAuKZElsvCSJEmSHHEkSZIkSWtN85VzBNWIpP8CnLvI7KH3UBvVMlrXBYXwJz5/NV3L8OZ3kO08SHb8DOdHL9BenaInniGdGCBJkiRJ65kjjiRJkiRpjamQpnrqHIVqDo78FMTtVLMNEAQ39DpRupZy80bq65ugOMtUNU1DUiCNU9dJkiRJ61VoCSRJkiRp7aiQ5mK6i+4ayG66FxZu9kQRAfPNW5nd+xEyrRs5k9lEIcjaEZIkSdI6ZXAkSZIkSWtENUhxMd1FfXMH5w78Tcq5lhW7dpSuYWbHI9R2buNCZgOLQY0dIkmSJK1DBkeSJEmStAZUSXEp1UVDczuT299DJdOw4m2IUjXMb307Ycc2rqS7qAQuiytJkiStNwZHkiRJkrQGjKRamK3pYHL7o1SyjavWjoSQ4pYHqW3u4GKqm8jHRkmSJGld8QlAkiRJklbZdNjAXLaNup0PUM42r3p74jDNwpaHWKjpYDzVbAdJkiRJ64jBkSRJkiStooiQkVQbLZ2byNf3rpl2lWraaOo7wFS6lcUgZ0dJkiRJ64TBkSRJkiStorFUCw11dYxueGDNtW22dRfVlj5GUq12lCRJkrROGBxJkiRJ0iopkWEsbGZxw93EqcyabGOmZxfzmWbyjjqSJEmS1gWDI0mSJElaJbNhPbmaWkpNm9ZsG/MNvTS2dDEatpIQ2GmSJEnSbc7gSJIkSZJWQUzIVKqBmu5dVNO1a7ilAZXOXcyk6ikFaTtOkiRJus0ZHEmSJEnSKlgMcgTZWmY69q35tpZqO4hzjSwEtXacJEmSdJszOJIkSZKkVTAf1lKq7SRKZdd8W6vpGjY11TIfGhxJkiRJtzuDI0mSJElaBYtBDR31NXBLrBsUMNO+l3xYRxT4GClJkiTdzvyNX5IkSZJWWAKUgwz5hp5bps2Fum5yqYASGTtQkiRJuo0ZHEmSJEnSCouCFFEqSynXeuu0OZWjkqmnFBgcSZIkSbczgyNJkiRJWmHVJE1vsEg1U39rtTtdQyVI2YGSJEnSbczgSJIkSZJWWDUIqaZyxGH6lmp3ksoSYXAkSZIk3c4MjiRJkiRphcUEJGGKJLi1HsmSIEVMYAdKkiRJtzGDI0mSJElaYQEJJDGQ3GLtjgmSxA6UJEmSbmMGR5IkSZK0wlLEpKMyYVy9pdodVCukiO1ASZIk6TZmcCRJkiRJKyyTRAwn9aSrxVuq3amoSIbIDpQkSZJuYwZHkiRJkrTC0kSkoiLZ0swt0+YgiUhX82STih0oSZIk3cYMjiRJkiRphQUkZOIqtYujt0ybawoTxNUKOQyOJEmSpNuZwZEkSZIkrYK6pMRkvggkt0R7m6b7SUclMolT1UmSJEm3M4MjSZIkSVoFjUmB7OIYwS0QHIVxhbaJEzTGBTtOkiRJus0ZHEmSJEnSKqhPiqRK8zTOXFzzbc0VpzldrqcxMTiSJEmSbncGR5IkSZK0CjJJRH2Up+bqi4Tx2l43KJy6TH2cJ5e4vpEkSZJ0uzM4kiRJkqRV0h7PM7aQpzY/tmbbmC3P0TF2jJ54mhSxnSZJkiTd5gyOJEmSJGmV1CQV2pN5ssPHCZJobT40jp5mshLQ5PpGkiRJ0rpgcCRJkiRJqyQgoTeaYnFmnNbJU2uufbniJMFEP93xLAGJHSZJkiStAwZHkiRJkrSKckmVzmiawtApwri8ZtqViopsO/dn1JdnaIkX7ChJkiRpnTA4kiRJkqRV1hnNkitOkzn3HVJRcdXbExDTePUolwohPdG0HSRJkiStIwZHkiRJkrTKUsRsjCZhdpD2wWcI48oqtiahbvQEi6MX2VIdI5dU7CBJkiRpHTE4kiRJkqQ1oCYps7M6wvToZXoHHofVWlOopUph8GU2VUZpSIp2jCRJkrTOGBxJkiRJ0hpRk5TZFo0yMzZAX/+XSVcLK/h0mEBqHB7/Ium7dtDqukaSJEnSumRwJEmSJElrSGNcYHNllPHJUbL9j1ObH7vJV0zIlOfJFs7CyafIPnSIpgP7KHVvsDMkSZKkdcjgSJIkSZLWmPqkyN7qELmZi6RPfYWO0ecJkuimXKtppp/qia+RPfYEfQ0lWjb1EgUBwx/5OZJUys6QJEmS1hmDI0mSJElaiw9rScyW6jjt5QkWLr9E9tRXaBs/ThiXb9xFRs8wNznMhtIQO6ojdF46RTWOSBKIE7jwt/6+HSFJkiStM6kNvZs+ZRkkSZIkae0JSahLyrQmCyTlAnOzU3RMn6EaR2TDhCRMk4RpIPgRZ0pIRWVypRmy0xeoGT9D+coLkMpC7346KhPU50cBaH3hGeZ3H6SSzUEC1VwtDYMX7QxJkiRpnQiOHL4/sQySJEmStPbFBEyHDYynmskHOZJMLdvqYKGpj0JdF5VsA3GYgwDCqEq6skBNYYKGuQGihXEGKzlq4jLtyRwd0TwvPvTPls+97eznaRs/vrx97pf+ISUgk0Dj5X42PPZndoAkSZK0DqQtgSRJkiTdGkIS2uN5WuJFKkGafCVLvpijPD1JFGZJggyNFIGAEmniJKESl8lToS7Mh0RdAAAgAElEQVQusS8ukaNCihiAhrkrLDT1ATC24W3XBUe93/jvDD36UapJwFTfTnpTKYIoshMkSZKk25wjjiRJkiTpNhATEgUBSRJAAAEJIQlhEv/QieyOPvQvll+3TJ1hx6k/Xt4eePhDzG7fR5xADOz/098luzhnsSVJkqTbWGgJJEmSJOl2eLiLySQRWapkkyqZJCL1I0Ij4LqgaKZtD0mQWt7e/PiXaO4/uTQ+KYHjP/M/W2hJkiTptn+2kCRJkiStWy1TZ9h++k+Wt59/8J8tT18H0H7qBYIEIoAERg7cZ9EkSZKk25jBkSRJkiStc62Tp8iWZpe3pzsOLL+umxhm55f+gHQEJAFDh9/J1NZ9Fk2SJEm6TRkcSZIkSZK447l/v/x6rPc++vd9Ynm7fnKEO//bb0MCJHDxoQ8zuW2/RZMkSZJuQwZHkiRJkiQADn3vdwiSGFha76h/71+/7njD2ODSiwTGd99twSRJkqTbkMGRJEmSJAmATHme1smTy9sz7fso55qXt/d8/Y9oGBsCYLFtA6fe9wskQWDhJEmSpNuIwZEkSZIkadm2M5+jbfyl5e3j9/z9647v+fofEkQxJJBv6+H5v/6rFk2SJEm6jaQ29G76lGWQJEmSJL2idfI0SSrDQlMfABM9R+gafpaABIDeE08z391HuW5pNNJc73Y6Lrxk4SRJkqTbgMGRJEmSJOl1mmYuUKprp1DXTZzKMbz5nXSMHSMVlQDouPgy8z1bKdc2UalpYnbDTjouvkQABEFANpshm80ShiFRFL3p60aEVIMU5SBNKchQDDKUwizlIE1EijgIgWDpOnaTJEmSdMMFRw7fn1gGSZIkSfoRD09BQENDAy0tS6NsKpUqIyMj170nlUrR09tDGATEcczU9DSFfOGW/rkv7v4YU52HlrePPPkvrzv+ws/8Q+IwvbSRwJE//T9JZ9Ls3rWLru4uxsfGOXHi5A+9RkzAQljLTFDPfFBLOcgQB0CQgjAFBJBEEMUEQUw6iainRHO0SHO8SIbIG1SSJEm6QRxxJEmSJElvQjqdZteunbzzXQ+zc+d2ejf0cqH/ItVqFVgKltra2vjgB9/Prt276OvbzNTkFDMzM7f0z906eZrhvnctbxcaemmbeHl5u/fU08x3bXl12rruHWy8epqH3/l2HnrofsIw5Pjxl1933uRaWDQStjKU7iRdU0+hbSdxz16yG/aQ6tlH0LuXsHsPYc9uUt27yHRvJ9u+hXRLD1FNEyEwQBOz1AAB2aRKiH8bKUmSJP1Ezz6WQJIkSZJ+tDAMaW1rYffuncRJQiFf4NgLL3LmzNmlh6t0mu3bt7Fv3x6SJKFQKPLisZVf9ycIApLkxoYne1/8T5y+85NAwEzbHqY6D9IwN0C2NAtA+9AZFjo2QxKw2LaBxfaNP/R8C0EN46lmqul6ck1t1LbvZLR5C1Gq5kc3JgfUA607KWy8h9r8KE0TpxibnWa8OE9nNE1LvEiK2JtWkiRJ+jEYHEmSJEnSWxDHCYuLi2RzWfbs3b0cHGUyGXbv2UUUxxQKBcIgXP6eIAiora1lw8ZeGhsbCYOAQqHI6Ogok5NTy8d37NhOpVrhyuUBNm7cQFNzE+VSiatXh5mbm2fz5k20tLZQrVQZHR1jdHR0+fx19XVs3ryJhvoGKtUKU5NTFIpFenq6KeQL9PdfIJPJsGPHdsIwZGRkhLa2Nurr6+nvv8BifpHmpiZ6enqora0lCCCfLyxNxzdzlSNP/isymTRdhx/h6nt+icGZIg+e/I+0t7cRpmDo6vd4rO8B8uWIcw99giQ4tzz2p7W1lY0bN5DO5bhSyHBxtEBzmDC49b3MZRt/7L5IghT5+g3k6zcQxlXaR5/n8tBFJuJ5+qrj1CZlb1hJkiTpLTI4kiRJkqS3II5jpqenaWlpYdu2reRyOcrlMnV1dfRt2czc3ByFfIGWlpalbwigrq6We++9hzvvOkQmk6FSqZDJZDh37jxPPP4kMzMztLa28PG/+tMU8gW+8+3HOXz3XWzavIlKucyLx45z+vQZ3v6Oh9i8eSMJcPLEKb78pa+wsLBANpvlnnuOcP/999HQ2MDkxCRjY+Pk83nuOHSQwYEh+vsvUFNTw/s/8FPU1OQ4evQF9u/fR119LZ/5o88yPZ3m7e94iD17dhPHMRCQTqd4+fgJHn/8uywsLFBTU8NHHrmfaMMmnrk0Q9fhX+OuyhlaW5qZmpqi5eQJ/qBmLwCDdzzKgdw8tbU1PPjQ/Wzfs4+6jl4WimU6rkR87nyFShzeuH4J04z33ku2bTe5q0c5N1lDb2Wc9nje6eskSZKktyC0BJIkSZL01lQqVQYGBmlubmbjpo2kUik2bOylrraOy5euXDdVXDqVZvv27Tzy6Luora3lu999isce+zajo2Pcc88R7r3vyPJ76+vqaG5pZu++vVy8eInTp85QV1/PPfce4Z57jzA8PMKxYy+RSWfYv38v27ZtJQxDOjs7eOc730FTUyMnXj7JU089w9zcHPv276Wuro5cLgtAEEAul6W5uZl9+/YyNTXJsRdepFAo0Nvby759eymXKzzx+JM8/dTTZDIZ7nvbvezYuZ1XTlCXhrb6DHdubOTyfMKnp7dx5tIQXd1dvH9fHwf7n1z+efJdW2no7SPb0sG3Tgzw4vAiTX17eOfde+lurrsJPRNQzrUwtfXdZLccYSrXxXCqjSjw0VeSJEl6s/ztWZIkSZLeomq1yrlz58lkM+zds3tpmrrdu6hUKpw7d54gCJbfm81muePQQTKZDJOTk1y4cJGJ8QlOnz5DNpvh/2fvvoPkug473//u7RwmRwxmMINMACRBRCaQIMAkUSIpipQoiZSttUTbevaz1/t2/ez36pVXVVtrrddRkoMClSxKokiKFClTzAmRJHIGBmlyjp27b3h/DNjEaAbgIAwwAL6fKlZ1973n3HPPuYXq5m/OOYsWLZTf78+HTbZtq6WlRa+8/JreeOMtWZYlwzRkWZZe+s3LeuP1tzQwMCCPx6tpNdXyej2aUT9DkWhE8XhCr7zymt579329/fY6DQ6M7EH0QY7lSnJOvMlk0nr22ef1mxdfVnd3j+KJhNav36gtW7bq8OEjamlpVW9vn7xer2bMqMvfjyeXUFH3HvW3HdPTOzr1+sE+fbt/rlzHUWFRoZY6fZr17nOSJEPSYHGtvvXafr18KK6fN4WUsU0FvabqS0KTNj6uYSpRsUDWwruVCJarxVPBnCMAAABgggiOAAAAAOAMWZal48ebZFu2Zs2eqWg0qlmzZioWi6m5qXnUuR6PqcrKChmGoaKiIt1++xrdtma1Fi1akN+bqKiocFTdH8xaisfjSqfSsi1Lra2tsm1blmWpv39AhmEoGAjKND0qKSmW67pKJpPq7+uXJOWyObW1tZ22/fFYXNLI8ntDQ0NKp9IqiEZ1662rtPL6FSosLBjZfykYHFM+se0lVbSsl+246hrOqC0yW6bpVUlJiQq7jqj8+E7ZqaQ6m1vVueBj6qpepv5kTrGMJcMwFAl4Jn2c0sFyWXNvkx0qVYunQjY/gQEAAICPxLdmAAAAADhDrutoaHBI7W3tKi8v0/yr5qmwsFDNTS1KJpO/dbYhj8dzopwrx3HkOI5SqbS2bt2u3bv2yLbtk+p2lc1mT7wZCWlcV8pmsic+cmVZ1gdVS9JI/a5O7E30IcdxT9F+V8nEh+30+/1auWK57rv/E1p83bXyB/wyDOPDmVMnzaDKtyGX0/Tjr6m0Z7ckKWd4NVB5jYZrV0qSvMN9MgZ6lCytl7IeucZIH+RsR8aHTZ90qUi1srNWKR0oUq+nkJlHAAAAwEfw0gUAAAAAcOZyuZwOHDyombMadP31K2RZOR08eHDMeSOzeYZVWlqqlpZW/eLJpyVJPp9PRUWFymaziscTqq6uOqt2OI6j4aFhGaahcDisaDSqeDwun8+rysqKU5Ry80vWSVIgENDK61fI4/Vq9649evnlVxUtiKqiolxFRUVjSntMj0pKSyVJsxqfVbYsqrB/gWzHVXNopg4teFgrTZ/m+cPS8MX/e8VktEYFs25W++HNCjlZFbpJHmAAAADgFAiOAAAAAOAsWJalo0ePy7JsVVZWqqenR8eON42ZSZOzcjpy+Ihmz56p2trpmjVrppLJpKqnVWv2rJlqbWvX++9tOet22LattvZ2ZTNZRSIR3XDDSh0/3qTy8rIJh1GGYcjn80mSTI+padOqVVtXq5KSEhmmoWg0qqKiIjnuyIwmj9ejhoYZmj17ljLZrBYUtKkoN6BWu1DHepNKJpPKVayQHS6TFJ8S4xUralBxZZva2hOKWGl55PAQAwAAAOMgOAIAAACAs+A4jgYHBtXe3qHauulqbW1TPBZXQUF01Hm5bE579+7TrNmzVFs7Xfd84uPq7+/XtJppsi1Lbe0dct2zX0DNtm21tbZrz569mjd/rlbdcrNmz5kly7LV29ungsIC6SMWaMvlcjp48JAWLVqoxdddq9LSUvn9Ph3Yf1DXLVmsmpppWrFimd59731JHyy55+qWW1dJkurrZ8jpPqZ9nV51HDkqz1C7FC7NL083VcSrr1PlYJO6E0WqtgdlsHAdAAAAMIanZlrtf6cbAAAAAOD0DMOQ3++X4zhqbmpRS0urXNdVzsoplUxp96496u3tk2maCkci6uvr09GjxzQ0NKxUKq3Ozi45ti0Zkmma6unu0fbtO7Vj+065riuPx6NwJKy2tnYdPnxUqVRKpmkqEo2MzGY6elwDA4MyTUOhUEjxeFxNTc3q7OiUbdvq6OiUZVkaGhpWb0+fDhw4KI/Xo7q6OvX29mn7th0yTVPRSEQ9Pb06duy4+vv6JY2ET/19/TJkKJVKK5PJaO+e/dq6dZtM06NUKqVsNquWllYtX7FMPq9X27fvUH9/vyKRsPr7B3Tw4EHtfOVZ9XQ0KVM6W+HKGYpnbTV2J3Skd2RpOI9hqCDoVcdwRvs6E+qOZS/oGDoev4xASIMDvSp24vIy6wgAAAAY+9tn2ZIb+BMrAAAAALgQP8AMQ4GAX6bpUTqdluOcn+AiEAho/lXz5PV41draqp6eXpWUlujTn75fM2c2aOvW7frlM899ZD2maSoQCCiXy8myrHybvV6vbNtWJBrRH3719xWNRvTKS69q48bN8vv9Mk1TqVRKacOvg8EGBebeqkTB9Ck5Bh47o4r9z8qI96jG7uehBAAAAH4LS9UBAAAAwAXiuq7S6cx5rzcYDOqmm25Ube107dm9R0eOHFV5RbnqZtSpv39A+/bun1A9juMolUqNaXMulxv1mSFJhiHXdZXJjNyPK0PdZpEihaUaKqiZsmNgewJK11wn5/AGWfaQvLJ5MAEAAICTEBwBAAAAwCUumUxow4aNWrZsqSqrKjWtZppylqWjR47p4MFDamw8fF6u49iOuru6FI+FlYgnRh2z5NGAGVWktEEnoqUpa7B0viLBPRq2B1XqxHmAAAAAgJMQHAEAAADAJS6Xs7Rnz161NLeoqqpK/oBf6VRa/f396us7f8uxZTIZrV+/ST6vV93dPaOOpUy/LE9AqWj1JdBjhkLRIg0lIyoVwREAAABwMoIjAAAAALgMuI6rwcEhDQ4OTdo1LMvSkcNHxj0WN4KaFXZ0zBe9JPrLLqxRqqdVrgwZYutfAAAA4AMmXQAAAAAAOBeupIQZ1HDxPLmG55JocypaJcsbUO4SaS8AAABwoRAcAQAAAADOiW14lDEDShTWXDJtzvoLNc2bU1p+BhAAAAA4CcERAAAAAOCcWPIoaEg5f9El02bH9CsTLFba8DGAAAAAwEnY4wgAAAAAcE5smbI8PtneS2v2Ts4bkWnwsxgAAAA4GTOOAAAAAADnxDEMOfJccj8xbY9fNj+LAQAAgFH4hgwAAAAAODeuJMO4BBtuSAbDBwAAAJyM4AgAAAAAcE4MuTJcW3LdS+sHsWvJvMTaDAAAAEz692S6AAAAAABwLjxy5LFzMt3cJdVuby4hn2sxgAAAAMBJCI4AAAAAAOfEK1umm5MvG7tk2my4lgKZIQWUYwABAACAkxAcAQAAAADOidd1ZDuOgomuS6bNvmxCnZZPIZfgCAAAADgZwREAAAAA4JwYchV2MioaOCLp0tgzKJTsljeXlI/gCAAAABiF4AgAAAAAcM6ibkpNCUveXPLS+DEc61TQzcq4RIIuAAAA4IJ9V6YLAAAAAADnKuxmZWZTCqb6Lon2pmP9KnKSMhg6AAAAYBSCIwAAAADAOfO7lgqdhNTfPOXbGom1yUgNqdBJMnAAAADAbyE4AgAAAACcM0OuKt0hBQaOKpDun7rtdC2FOnYobMUVEPsbAQAAAL+N4AgAAAAAcF4UOCllcllVdG6VpujeQaFkr/qHB1XuxBgwAAAAYBwERwAAAACA86bKHtTgQI8C6cGp9wPYtRRu26oSe1ghN8NgAQAAAON9b6YLAAAAAADnS5GTVCAzpOjx9fLYUyucCfUcVGyoTzXWgMwpOiMKAAAAuNgIjgAAAAAA540hV9Ocfg0OD6ise8eUaZc/MyxP+05VWf3ys7cRAAAAcEoERwAAAACA8yrgWmqwupVob1Rx34Ep0SbPkbfkTw+qzBlmgAAAAIDTIDgCAAAAAJx3xW5Cxdl+pVp2Khxvky7m0nAde5QyQooWlrJEHQAAAPARCI4AAAAAAJOi2hlQabpbyQPrFB1uvuDXN1xbwYMvS/0t0tzVOr7wcwwKAAAA8BEIjgAAAAAAk6baHlCN1S378AZVdG7RhZp5FE50KLjv1woMNasq5JG8fknSrpX/lxzTy8AAAAAAp+CpmVb73+kGAAAAAMBkMCQVuGkZjqXeRErhzICMUJFsb2hSrmfaWYX6Dih8bJ18qX7V2T0qiTUrGypVKlIlx+NXZ92tKu/eKY+dZoAAAACA3/5OTRcAAAAAACZbuTOsudkWOT1HlDnwhsq6t+t8zz4KJbo0d/8Tyh7bqmi6Xw1Wt3yuLUlqOPTsqHNjRQ0MCgAAADAOZhwBAAAAAC4Ir2yVOHEF7JTisQGZA80qtQaV80Vle4Nn96PWzqhw8IgCLe/LaN8tKx1Xvd2rQjc55tyigUb1Vi+TJA2WXSXTdS7K3ksAAADAVGYsW3KDSzcAAAAAAC6knLzqNyMaMAvk+sPyR8tkF9XIjpQp54vI8QRG9iIyjJECrmS4tkwnK28upUB6QFasV/6hFiVTSRW6SZXZMUXc0y8/l4xU68Dix+QaIwtwNDQ+q7LuXQwIAAAAcAI7ggIAAAAALjifLFU5Q6pyhpS0A+rP9GtooE0ZwyfX8Ej+kOYb/bK9Ibky5HGy8uYS2ueUy7IysuQo6qZU4sTV4CZkuhP7m8hwolOGY8n1+CVJx+c+QHAEAAAAnIQZRwAAAACAKcGWKcvwKCevsoZXOXnkyJQMyXAdeeXIJ0t+15LXteWVI+Ms90k6dPXv5vc5CsfbtWDndxkAAAAAQARHAAAAAIAr1Nab/yr/umjgsObse4JOAQAAwBXPpAsAAAAAAFeik4OioZI5sk8sXwcAAABcyQiOAAAAAABXpKKBwzLtbP79jhv+kk4BAADAFY/gCAAAAABwxVqy+a/lzwzm3zfPvodOAQAAwBWN4AgAAAAAcEW7Zss/5V/3VK9Q46JH6BQAAABcsQiOAAAAAABXvOrWdfnXqUi1HNNLpwAAAOCKRHAEAAAAALjiTW96Qx47I0nK+aLafuP/S6cAAADgikRwBAAAAACApOs2f33U+96qJXQKAAAArjgERwAAAAAAnFDevT3/umnOfXQIAAAArjgERwAAAACAy+uHrmmqoqJc5RXl8nrPbK+i+sbnR71vXPQIHQoAAIArCrt9AgAAAAAuCMMwVDejTnPnzFZxSbEMw9Dw8LCOHTuuw41H5LrueblONBrRH/zhY7JtW//+4yfU2tp2RuXn7ntCjQtHAqPh4jmyPX557CwDCAAAgCsCwREAAAAAYNIFAgF98t57tHDRAvl8PnnMkQUwHMfV9TesVHNTi5566hklE8lzvpZhmCosLJBl2fJ4z/xnbyjRKdPJyTF9kqTj8x7Q7P1PMogAAAC4IrBUHQAAAABgUnk8Ht119x1auvQ6RcIR7d2zT7948hn99IkntX79Rnk9Hs2bP1ef+tT4ewoZhjGh60z0vI/iy8a1ZNP/zL8fLL1KvVVLGEgAAABcEZhxBAAAAACYNIZhqLi4SCtWLpfH49X772/Rb158SdlsTpJ05MhRNTc163Of/6y8Xq9CoaDS6YxKSkp06+pVamioVyAYVGx4WDt37tamjZvlOI5M09Ta22/T3LlztHfvPuVylm666Qa9++772r1rz3lpe3n3dvVWjgRGTXPuU3nXdgYUAAAAlz2CIwAAAADApDEMQ7NmzVQwEJBl2Vq/boOSyVT+uGVZamxs1D/8/TeUy+WUTmfk9/v16Bc/r4qKCrW3t+vo0eNavPga3XHHWhmGofXrNpwIpIo1Y0adcjlL02tr5LquQsHgeWt7RceWfHAkSZ21t6i6dR2DCgAAgMsawREAAAAAYFKVlZdLkrLZrHp6escctyxbg4ODkkaCpsKiQvX392t4OKbfvPiSYvG4spmM1qy9TYsXX6P16zZIkhzHkcfjUX39DL344kvat3e/stmM/P7AeWl3ON6u6HCT4oX1kqSB8oWqatsgw3UYVAAAAFy22OMIAAAAADBpDMOQ1+uRKymXy8lxTh+6uK6r/r5+PfvL5/XWW++oqLhIs2bNVCgUkmFIpaWlo8+XFI/HtXXLNg0NDSmVSp/X9s/f/UN57IwkKRmp1rab/j8GFQAAAJc1ZhwBAAAAACaN67pKJlMyJIVCIfl8PuVyudOWCYVCeugzn1Zt7XQZhqF0Oi2/3y9JMj3mb19Ag4ODymazk3YPDY3P6chVD+ffx4pmqmDoGIMLAACAyxIzjgAAAAAAk8Z1XbW2tEqSPB5TVy2YP+acQCCgRx79vG686QZFo1GtuuUmzZs3V7lcTt9//If63ne/n1+ebjy5nDWp91Dcd0CBzED+/UD5QgYWAAAAly2CIwAAAADApHFdV83Nzers7JJpenTHHWtVVVWZPx4KhfSJT35cCxct0OrVt8jr82r69OkyPaY6OjrV3t6hWCyuuhl1Iz9iDeOi3EfB4PH8657q5QwsAAAALlssVQcAAAAAmFSZTFbP/+rXevSLX1BVVaW+8tjvqbOzS5aVU0VFhYpLiiVJ77y9TvF4XIODgzIMQw0z63XLratUWVGhiopyua4rn9+vpcuW6OCBQxf0Hio6t6i3akn+fWftLapuXcfgAgAA4LLDjCMAAAAAwKRyXVfHjx/XD77/I+3Zs0+BYEBz5szW/PnzVVZWqtaWVv38p7/Qli3bZFu2NqzfqM6OLkXCEd1++xqVlZfql888p92798rj8eieez6mwqLCC3oP4Xi7osNN+fcD5QvlGvykBgAAwOXHWLbkBpduAAAAAABMNtM0FQwFFfD7FY1GZZqm4vG4Mpms0um0LMvKnxeNRlRQUCDDMDQ0NKREIqlQKKhoNCrbcTTQP6BAIKBAwK9czlI8Hh91naKiQrmuFI/H8/Weq+GSOWpc+Ej+/XWbvy6PnWFgAQAAcFkhOAIAAAAAXHCmOTJbx3GcU/9gNQwZhuQ47qjPpJFZTBfDjhv+QrYnkH+/bMPXGEwAAABcXt/V6QIAAAAAwIXmOM5pQyNpJBw6OTT64LOLFRpJI7OMTnbyvkcAAADA5YDgCAAAAACAM1DetT3/umnOfXQIAAAALisERwAAAAAAAAAAAJAkeemCi2POnNmqmV6j6TXTVFpWll+n+5mnn1VHR8d5KwMAAAAAOL/qDz8/aom6xkWPau7en9AxAAAAuCwQHF2MTvd69LGP3z3pZQAAAAAAk2PuvifUuPARSdJw8WzZnoA8doaOAQAAwCXvvARH02qq9YVHH8y//9kTz6i9rXPUOddcu1B3fWyNJOlb//Q9ZTIffqE2TVMLF83XvPlzVFlVrmAwKMuyNDQ4rKamFm3fukuxWHxUfSuvX6pbVt846jPHcZRJZ9XX169jx5q1e+depVLpUec8+Jl71TBzxkfe06GDR/TCr1768B6nVWnJsms1raZa0WhEpmkqk8mor7dfhw4e1o7te85qg1bHduS6rjxez6SWAQAAAAAAAAAA+CiTMuPoplUr9NTPn88vpfbb+vr6FI1GJUkFBVHd/+l7VFVVMeocj8evyqpyVVaVa/F1V+s/XnhZR480nfa6pmkqFA6qNlyj2roaLVu+WC/++lU1HW8543uIJ2L51wsWztPHP3FH/n7S6Ywc21Y4ElZt3ci1ikoK9dbrGyZUt+O42rp1m9pa29TR3qlPPXCfqqqrznsZAAAAAMDkKBw4LI+dke0JSJJ23PAXWrbha3QMAAAALnmTEhzV189QTW2FOtp6T3uex+PRAw9+UhWVZZKk3bv26LXX3lB3d5/CoYAWL1mse+65W36/T/d96h597zs/VDyWGlPPd7/zfbW39siyLIUjPq24foXWrr1N4XBI9z/wcf3w+z/V8FB8TLm//POvKRgMjts2n8+nQGDkB8DqNTfLMAwNDw3rG9/4F4UCBZKkWHxAX37sS6qrq9WyZdfptVdel9cT/Mj+cRxHmzZuPqM+PZsyAAAAAAAAAAAAZ8I83xUeOnRYkvTJe+9ROp087bmLrr4qHxpt2bJNL//mLTmWR+WllQqHitR44Lh+8eSzkiSPx9TdH79j3Hoymayi0aiKi4vl90W0c9s+PfXkLyWNBEC33HrDuOUikYii0ei4/30QGvn8PkUi4RP31pgPjSSpIFqi1155R8/+8gV965v/qngszRMFAAAAAFeI6zZ/fdT73qqldAoAAAAueec9OHrt1deVyWRVWFiglTcsO+258+bPyb9+9pnnxl3arnzyZIcAACAASURBVKOtW22tHZKkhoYZ6h/onlA7Wls61dHedeI68xSLxc7qfnLZnJLJkVlO1y6+RtU1ZfJ6P5yo1d83oKOHm5VJOSouLuaJAgAAAIArSCAzkH+djE6jQwAAAHDJO+/BUTye0MYN70qS1qxdLVfWKc8tKy+VJA0ODKq05NT79bS3deZfT6uZ+BfxjvaRcqZpKBTxjTmeSCRk2TkFAoEx/51s4/r3JEl+v1+PPPo5/dGffEWff+RBrVm7SnPmzpLP5+NJAgAAAIArUMHg8fzrnurldAgAAAAueed9jyPDkN7bvFVLll6r4uIi3fWx2/XqS2+Pe67fPxK4JFOp09aZTn+4BFwwGFAmlZxQWzKZbP51IOCXnRt9/K//5q9OWfZfvvm4UqmR6+7csUd9ff1asvRqzZ4zW16vRzXTq1UzvVpLly9WOp3RxvXvavu23TxRAAAAAHAFqT/8vHqrluTfNy56VHP3/oSOAQAAwCXLOymVen1a9/Zm3Xv/3br22qu1eeN7csc5L5PJyO/3KRqNnra+YCiYfz08PDzhZofCoVHlIqGJLyXX1tau0tLS/PvWlna1trQrlUopWhhQQ0ODFiyYr3nz5ykYDGjtHbeqta1VPV0DPFUAAAAAcAWJDjcpXlgvScr5InINU4br0DEAAAC4JHknq+JDBw+ruWmRZtTX6t7779F7m7eNOaenu08FBVEVFhYolhhQQaRk3Lrq6qZLkhzHVX9vTCUlJRNqw4z6WklSNpuTxwiOOf4/vva3CoVC45Y9OTQ6WSgUkp2TjjQ260hjs8LRd/SHX/2KDEO6+pqFerNrA08VAAAAAFxBprWsU+OikeAoFamWY/rksTN0DAAAAC5J3sms/O23NuqRL35G06ZVa+68mWOOH9jfqFmzR75cP/jgp/TSi2/KNEdvuzR33ixVT6uUJO3ds3fCodE1ixeqtHRkhtGO7TsVDAbP6h6uWjBX86+aq9KyEr3w3G/U29s/6ngqkVUul5Pf71NBQQFPFAAAAABcYYLJbpmxTjnJQSkxoEZPlVzTHVl5wzVkGK48riO/m1PYzSjiphV2szLGXZsDAAAAuLgmNTjq7urR7l17tfi6q7Vw0YIxxw/sP6TrllytmunVuubaqxUIBLRzxz4NDQ0rEAhoztxZWnn9Ukkj+xU9/fSzKoyOnQkUCPiViA8rGokqEolowaJ5WrFypFwqldZzzz2vspKqMeUSiYR8fq8i4fGXystkMrIsW3PmjoRe99x7p15+6XUND8aVsywVFkS1ZNni/F5Nx44e44kCAAAAgCuAK0Npw6dBM6JBt0BO1yHJMKVouRKz/pPKenbJ9vglV/I4GfmycfVkpVhqQJ7MsIxsWiVOXMVuQgE3K4MuBQAAwBThnewLbNzwvq66aq4CwcDYL9quq1899xt94t47NWNGrebNn6t58+eOOW94OKbHv/eDcUMjSXrs939v3M+Hh2P67rcfHzc0kqS//pu/Om3b//Hv/lWHG49q29ZdWrrsWlVUlOvRLz487rnt7R3auH7LhGYdlVeU68Ybb8y/Lyn5cO+lVatuUjqTlST1dHdr8+Z3z7oMAAAAAOD8y8qrNm+5+o2IKkNe+WsWK1l2lXRS/NNXce24ZW1JppNTUf8hDXccUHsqpVI3pulWn3yy6VwAAABcdJMeHCUTSW3atEW3rbn5lMef/sXzmj69UouuWagZM+oUiYSVTKbU29un7dt2aMe2vSoqKprw9Xp7+7R9+w5teW+XiouLz7rt8XhCoVBIb76+Tvv3HdCCRfPUUF+vwqJCmaapZCKh9o5O7dyxU40Hmya8VF0oGFR9fd24x6qqq85bGQAAAADA+WPLVJenWL2eYkVLKhWuvka94Qo5pu+M6nFMnwbKF8ksnadAoltux07tH4qq0hpQlTPIEnYAAAC4qIxlS27gG+kFVldXq/s/dd9HntfU1KIXnn/hrMsAAAAAAM6PtOFTi6dC2VCpgrULNVg6eobRuSrp3aNE2yGF0r2qs3sVcHN0OgAAAC4KgiMAAAAAAE4jbfjU6K2RW1wna+4auYY5KdfxWinNOvALNScdzbY6FXSzdD4AAAAuOJMuAAAAAABgfHEjqEbvdBVWz5Qze9WkhUaSZHlDOrzg84pU1uuIt1opw88AAAAA4IIjOAIAAAAAYBxp+dXqq1B55XT1194k2xOY9Gs6Hr8G6m9VtKJeR73TlDZ8DAQAAAAuKIIjAAAAAADG0eStVLpkljoa1soxL1yA4xhe9dffohkRUy2ecrG+PAAAAC4kgiMAAAAAAE7iSmrzlkmRUhn1K+RehJ/OjunT0fkPKR2uVKenlEEBAADABUNwBAAAAADASVJGQF1Giczaa2X5IhetHZY3rHDNfPV4SpRlyToAAABcIARHAAAAAACcpNNTorqSiIaLZ130tgyWLVBhcZlamXUEAACAC4TgCAAAAACAE+JGUClfgTrq10oypkSbYjVLNeQpUNIIMEAAAACYdARHAAAAAACc0GcWyBstVc5fMGXaZIXLFAwXasCMMkAAAACYdARHAAAAAACcEDdD8pY3aKrMNpIkx/DKVz1fQ2ZE7hRqFwAAAC5PBEcAAAAAAEhKGEGF/D7FimZOubYNl8yS/GElWK4OAAAAk4zgCAAAAAAASUNmRHakUrZn6oUzruGVEyxWzAwxUAAAAJhUBEcAAAAAgCueK0MxI6hUtHrKtjEXKVdSAbkMFwAAACYRwREAAAAA4IrnyFDG8MkfmLozekr9Utb0SexzBAAAgElEcAQAAAAAuOI5MmTLlOH1T9k2Wr6obBnMOAIAAMCkIjgCAAAAAFzxXGNkFo9reqdsG22PXy6zjQAAADDJvHTBxTFnzmzVTK/R9JppKi0rk3HiR8ozTz+rjo6O81YGAAAAAHC5IDQCAADA5CM4uhid7vXoYx+/e9LLAAAAAAAm5oNIxnDsKdtG08myUB0AAAAm/3snXXBxObYj27InvQwAAAAA4NQMOTLlymOnp2wbvbmkTBarAwAAwGR/7zxfFU2rqdYXHn1wQuf+6Ps/U29vv1Zev1S3rL5x1DHHcZRJZ9XX169jx5q1e+depVLjf3GPRMJafN3Vqp85Q6UlxfL5fUqn04oNx3Xk8DHt2b1f8XhiVJkHP3OvGmbO+Mg2Hjp4RC/86qUP729alZYsu1bTaqoVjUZkmqYymYz6evt16OBh7di+R647sb/8chxXW7duU1trmzraO/WpB+5TVXXVeS8DAAAAAJgYj+vK7+aUyOSmbBuTmaz8riUx6wgAAACT6KIsVXe8+aii4eJxj5mmqVA4qNpwjWrrarRs+WL9+vmX1NLcPuq82XMa9PFP3KlAwD/q80gkrEgkrOpplVqy9Bo9+8tfq7Oj54zbGE/E8q8XLJynj3/ijvyeQul0Ro5tKxwJq7ZupJ1FJYV66/UNE6rbcRxt2rj5jNpzNmUAAAAAABNjyFVUaSUTXYpr0ZRso50aVsjNMuMIAAAAk2pSgqM333hb77x16pAjEika89l3v/N9tbf2yLIshSM+rbh+hdauvU3hcEgPPPhJfefffqB0auQvvyoqy/XJ+z4mr9cjx3H1+utvaMv72zQ0FFdRUVQrr1+uNWtuUzgS1qc+/Un909//swKB8Jhr/uWff03BYHDcNvp8PgUCAUnS6jU3yzAMDQ8N6xvf+BeFAgWSpFh8QF9+7Euqq6vVsmXX6bVXXpfXE+SpAgAAAIBLUJGdlBlvVcK15RqeKde+QKpPUTfFQAEAAGBSTUpwZFmWotHoGZXJZLKjyuzctk+D/UN66LOfks/n0+13rNZ/vPCaJOmmm1fI6x35Ev/ET36m7s4B+b1hVZSNhEM7tu5TPJbU0qWLtXfffsWTsXGDo0gkolAodNp2+fw+RSIjZQ8dasyHRpJUEC3Ra6+8o+KSArW1tSoeS6u4mOAIAAAAAC5FBW5KTVlX0eFmxYpmTqm2RYeblc1mFHXSDBQAAAAmlTmVG9d0vE2dnd2SpHnz5ykWi8kwDDXMrJck9fT0qqujf9yyhw8d1y9+/ivt3XVIZSVnvxdQLptTMjnyF13XLr5G1TVl8no/zNv6+wZ09HCzMilHxcXFPFEAAAAAcIky5CripBXoPaSptI+Q4ToKdu5UoR2XRw4DBQAAgEk1KTOOvF6v4vG4ysrKxhyzbVuWZU24rvbWDlVXV8o0DYUiXgUCvvxsoyNHjub3HTobiURCPr9XkfDY2VGZTCb/euP693THXavl9/v1yKOfk2XZ6u7qUWdHl1pa2tV0vEW5XI6nCQAAAAAucWXOsNqGB2Q6OTmmf0q0yZ8ZVP9wTHOdOAMEAACASTcpwdGatau1Zu3qcY/t2L5Hr7/69oTrymSy+deBQEDGSXOkUsnRazsbhqE/+bM/GFNHS3Orfvn0r8d8/td/81envO6/fPNxpVIjSwDs3LFHfX39WrL0as2eM1ter0c106tVM71aS5cvVjqd0cb172r7tt08UQAAAABwCStwUvJl4ypuWa+O+jWSjIvepmDXXvnthMIuy9QBAABg8nkv9AWHhwfP6PxQOHRS2WGFQx9Oy49EI6PONQwjPxvpZJnMmX+5bmtrV2lpaf59a0u7WlvalUqlFC0MqKGhQQsWzNe8+fMUDAa09o5b1drWqp6uAZ4qAAAAALhEmXJVZQ/ocHenwmUdSkZrLmp7orFmxXtaNM/qkzmFls8DAADA5WtSgqNXX3ldu3YcOC91zaivlSRlszl5jKAc21A2m5Pf79O8uXP09hubZJoj05Acx9Hf/c0/58s+9PC9qq+fccq6/8fX/lahUGjcYyeHRicLhUKyc9KRxmYdaWxWOPqO/vCrX5FhSFdfs1Bvdm3gqQIAAACAS1iBm1KxE5Natyh51b26mLOO7OadKrGGFFaGgQEAAMAFYU7lxl2zeKFKS4slSTu271QwGJTH49GRw8ckScUlxWqYdeq//gqHI+fchqsWzNX9D9yj//SVR1RePjZMSiWy+f2NCgoKeKIAAAAA4BJnyNUMu0fp4T4Fmt+TLsJMH8O1NOPIizITPZru9DEoAAAAuGAmZcaR1+tVPB5XWVnZuMdt25Jl2aM+CwT86uvpUXl5mSKRiBYsmqcVK5dKklKptJ577nmVlVRJkjZvfF+z58yU3+/TQ595UBs3bNbxY61KxBPyB/yqqqrQNdcuVEXFyPU/CHZ+WyKRkM/vVSQcHfd4JpORZdmaM3emJOmee+/Uyy+9ruHBuHKWpcKCqJYsWyy/3ydJOnb02IT6p7yiXDfeeGP+fUlJcf71qlU3KX1iX6ee7m5t3vzuWZcBAAAAAJzl71rXVp3Vo/au/YoEAxqouEau4bkg1zZcW8VdO9XZ36uZdo+8rs2AAAAA4MJ9F56MStesXa01a1ef8vj7727TO29vGvXZY7//e+OeOzwc03e//Xg+NJKk/v5B/erZF/WJe+9SOBzSzatu0M2rxr/Wzh279cxTL6ioqGjMsb/+m7867X3849/9qw43HtW2rbu0dNm1qqgo16NffHjcc9vbO7Rx/ZYJzToKBYOqr68b91hVddV5KwMAAAAAOHsFbkpVVq+OHt+jMtdVb9WSC3Ldis731ddySLPtTkWdNAMBAACAC8p7MS46ODRw2uPJRFK9vX3avn2Htry3S8XFxWPOaW5q1fe+82PNnTdTVy2Yr6qqSgUCAaWSSfX29auxsVHvbn5foUDBuKHRRMTjCYVCIb35+jrt33dACxbNU0N9vQqLCmWappKJhNo7OrVzx041HmxiqToAAAAAuMwUOUnNUqe6Wiz5s5ZytcvkGpOz6rvHzkhtOxXvOqgGu0cFTooBAAAAwAVnLFtyg0s3AAAAAABwasNmWC2ecoWKKpSuXa5UpEqScd7qD8c7ZLZukzXcrXqrR1GX0AgAAAAXh5cuAAAAAADg9AqdpOY7bWoazCoVi6mmsloddbee875Hpp1VTcvbau3tVak9pHl2twyXv+8EAADAxUNwBAAAAADAhH5A25pldWnYiamrI6FQf7cCxZXKlc1WIlo74SXsDNdSJNYut++Y7KEuDWWHNNcaVIGbkiFCIwAAAFzs770AAAAAAGBCDLkqchIqcJIasiPqywwp09OiyoBHycI6paLTFPT75XgCck7MRjJdS65tKZdNKRTrkC/WoVg6o4ibVrUdU5GbJDACAADAlEFwBAAAAADAGTLlqsSNq8SKyzFMxayQrNSwst1HlZRXjmFI+TDIkClHftdSwE2r2ImrwWF2EQAAAKYmgiMAAAAAAM6B6ToqchMqchJyZMg2TDkyT4qNJI/raPSnAAAAwNREcAQAAAAAwHliypXp2pJsOgMAAACX6HdaAAAAAAAAAAAAQARHAAAAAAAAAAAAOIHgCAAAAAAAAAAAAJIIjgAAAAAAAAAAAHACwREAAAAAAAAAAAAkERwBAAAAAAAAAADgBIIjAAAAAAAAAAAASJK851rBjIZqDccGZVnWhM4PhyMy5FVXRx+9DwAAAAAAAAAAMIWcU3A0o6FaMhzdeeddCoVCEyrT0dGuTZs2qaAwrNhwkhEAAAAAAAAAAACYIs4pOBqODerOO+/SjBl1Ey5TUVGurq5u9XT3EhwBAAAAAAAAAABMIee0x5FlWROeaXSyQCAg13XpfQAAAAAAAAAAgCnEpAsAAAAAAAAAAAAgERwBAAAAAAAAAADgBIIjAAAAAAAAAAAASJK8U7FRlZXl+uKXHtY//f2/ybLs/OfFJUX68mOP6l+++bhSqbRWr7lZBdGofv3Cy/kyH5zvOI56e/q0a+de7d1zIF/H/Q/co9lzZo67x9J3/+1HiscT47bJ4/Ho5lXXa9782fL7/XLlqqOjS2+/sUEDA4P64z99TD6fT5JkmoZc19UHl3j15Te1Z/d+SdLnH31IBQVR/eDxJ5TL5vL1L12+WGvWrtIzT72g48ea85/X19fptttX6Uff/5kk6Y//9DF5vd6R9ruuhoaGdejgEb333jZZOWtUmxtmztAn77tbW9/foU0b3x917I//9DH9+vmXR13rZFXVFbrxphWaXlsjr9erWCyuA/sb9d7mLfk+vv+Be1RbV6MfPv5TJRLJfNmbb7lewUBAr7/2Tn5cHGdsf+/ZvU+vvvzWaZ+B9zZv07p3NuU/N01Tf/Zfv5p/BiRp/lVztHTZYlVUlsl1pf6+Ae3YvnvMuM+a3SDHcSRJyWRKTcdbtHnj+xoejo0ag/Ha+psXX9OBfYc+8jkAAAAAAAAAAOBS5p3KjUsmU/L7/WM+7+zsUlFRkSQpnU2POva/v/4N+f1+DQ0NaUbDND30mU+rqDiqjeu35M958423tX3rnjNqyw03LldNbbW+9c1vKxgIK5PJ6KZVy3Xv/XfrRz/4ub71T9/Nn/vlxx7Rk08+rfhwZlQdFZXlMgxp7559KisvVGd736jjg4NDWr3mJh080KhAIHDKtvzrP39bmbSjdDqtQMijhx56QA8+dK+eevJX+WBEkhZfd7XefH2dVly/RBs3vCfDMEbV09ffO27906dP04OfvU/vbd6in/30FzINn7x+V5/73Gd1/6fv0TO/eCF/biqZ0vKV1+rtNzePqiNnjQ6x/tf//Mdxx/Kjxv/qa67Spk2bZeXccZ+B5SuW6Pobl+mVl9/Qlve3y2N6NG16uT73+c8qGPJr6/u78mXeevMdbd+6R5ZlKZWJ6eMfv1uP/M5n9JMfPalYbCQwbGvr0M+f+OVpn4PptdP0nX/7gUzTm38OPnn/XfrxD54c08cAAAAAAAAAAFxKLtul6oqKijQ0kNSzT7+g629YqayVPKf6yspL1XjoiIKBsCQpEAho+9a9+sH3/33MuR6vT6FQeMzni69bpL27D+idd9bptjWrxxxvbmpVW1u7lq+8dkJtCgaDMlyfnvrFr1RQGFVtXVX+WDQaUVVVhXbt3Kuenl5FC4MTvtfVa27Wrp17tHnTVoWCUQUCAXmMoJ5+6nlVVlaosqokf+6GDZs1e85shSK+8z6G2WxOL774su67/55xZ4j5fF7dvGqlXvz1K2o8eFRFhUWKRqOKDaX1wq9+o1tXr1IsPjCmnNfrVUGkROvfeU9Nx5u1bMXiM3oOjh07LtP05p+DHdv26WdPPM2/JgAAAAAAAACAS95lv8dRf/+QBgcGNWfOrHOq59DBw7p51fVavnKxautq5PV65DiOPKZ/QrNMfH6f5sydpS3vb5XHCMjv840Js0zT1NO/+KVuuXWVLCcz8UE0PDp2tFlz583Of3bN4oXat/egPB6P9u05qNWrb5lQXX6/X9XTqrRu3foxx1xHOn60WXPnz8l/lsvl9Pyv/kOf+/xnlMlkzu/DaRo6dqRVgUBA9TNrxhyvra2RDENHjzSNOdbZ0aNEIqFZs2ee9hpHjzRpwcKrzug5WHn9cl23dGH+ObBtW1bOYbYRAAAAAAAAAOCSN6WXqvujP/mKTNPM/w950zi7nCsWS6iwsFhSpyTptjW36tbVq0ad03S8Rc/98j9OWceB/Y1qa2vXwqvn6fY7V6ukpEjNTW3auOE9dXZ0fWQbFiyYp+PHWuTzjSxBt3v3fq1Zc5s2rHtv1HnFReXavGmLHv7cQ3r6yecnfI/xeELV1RWSJMMwdPU1C/XjH/70xL216q6716p/oFelJeWnrScQDMgwpJ7ubpWWVI05nkgkVVRYlH9vyFBv96D6ege04vrF2rXjwLj1/re/+D8ljQ5WnvzZs6ftO0OGTNPUG6+t0wMPfVL//M3vyDQ/XMIvEAoqmUieMrCJxxMqLS1VT9ep9x6KDccVDH44G6umZpr+9L/8wai22radX4rwwP5G9fb0ac68Bt1+560qKSlWU1OrNm14f0LPAQAAAAAAAAAAU9mUDo7+19f/VnI+bGJxaVT/7c//7IzrKSiMKpFI5N9/sNfNmYoNJ/Tuxu16d+N2DQz16uabb9BnHr5f//rPj8vK2acte+11i1RSUqxZs78s0/wwAHv91XdGBReStH3rLi1YMFd19dWyHXti91gQVTwelyTNmt2gaDSsLz/2OzIMQ4ZhyOPx6JZbb9Le3YdOW08qmZLruiouKRn3eLQgMqovP/Dm6+v0xS89rO3bdo5b7n9//ZtnvMfRB7q6erRv7wHdcddtev2VdfnPk4mkItGIHMcZ1af5tkajSiZTH/lsfNBvktTefvo9jiSpt7dfvb392rxxm4Zi/brxxpX6zMP365v/+G+6AibxAQAAAAAAAAAuY+cUHIXDEXV0tKuiovyMyvX09Oi3Z5+MJxouHhU2FBRGz7iNNTXVKi4u0r69BxQKRs7qPj0ej+bNn60jh48rm81KkkqKyrVvz2GtWbtahnn6cKe6ulKhYFDf+Idvj5odc/tdt2hGwzR1d47eh8d1Xb326tv69EP36tlffvSso0DAr7lzZ+XPvXbxIr38mze0b+/B/DnpTFxf/aPf155dB0+7pJplWWptadfNN9+oTRu2jjoWDAY0c1a9nnpybLASi8XzM6WOHW1SJBo9rw/q5o1b9Dtf+py2l+/UB9sdtba0ybZtlVcWqb83Nur8uhnTFYmEdWD/QUXChaesd9HVC3TwQONZPwdFBaUnnoPb5AuYymX4RwUAAAAAAAAAcOk6p+DIkFebNm1SV1e3AoHAhMr09PQok8mos73/jK/n8XjO6NyGmXW6467btHH9u2cdGkkjS5UtX7FEdTOma/07m5VMpuT1enT1NQtlGKZ6ugZVXFx8yvKLr7taBw40jgls2lo6tWbtbXryp8+OKdPV2aP9+w7pzrvWynGc8fvfMFRRWa7b77hVPT296mjvkddrakZ9rX76xJOj7jkYiEoyFI76lEpYp73ft9/aoM9+7gFlszk1HjqmZCKpiopy3Xb7Lers7FZXZ9+44dMHM6UWXn2Vjh9rPq8Pajab05tvrNenHrhXzolZWI7jat3bm/T5LzysF55/UT1d/XIcR3UzanX7nbdq4/p3TxkaFRYW6IYbl6u8okzff/xHKiosmfBzMKOhVm++vk7ZTO6k58BQb8+gigqL+VcFAAAAAAAAAHDJOqfgqKujTwWFYfV098r9YBrIRzLOKjSaqA/20nFdR/19g3rj9XfUePDoqHPWrF2t29bcOqbsb158TQf2jb+U2zNPPa8ly67WF774kAKBgGzbVmdHt37w+L+fNjQKBPyav2Cuvv+9H485duTIcd39sbVKZ+NyxwmHNq5/V7/7e59XIj56abiv/tHv52fdxGJx7dm9TxvWvSufz6cbblqppqaWcYOyQwcOa/XqW/XSi29Ikr7y2JdGj2dnt376k6fV1dmjH//gZ1q+crG+8OjI/Q4PDWv37n3avHGLvN7xH5sPZkp94dHP6MD+g6OO/d//z38ec/7g4JB+8L0nJjy2Rw4f04KFc1VWVpr/bMf23RoYGNCSpdeqtrZWpmmqr7dfr736lo40Hh9V/oO9rQzDUDqd1tEjx/WP//CtUaHR9OnT9Gf/9f8Yc+09u/fp1Zff0jNPPa/lK6/TF3/3YQWDwZHnoLNbP/nRzwmNAAAAAAAAAACXPGPZkhtcugEAAAAAAAAAAAAmXQAAAAAAAAAAAACJ4AgAAAAAAAAAAAAnEBwBAAAAAAAAAABAEsERAAAAAAAAAAAATiA4AgAAAAAAAAAAgCSCIwAAAAAAAAAAAJxAcAQAAAAAAAAAAABJBEcAAAAAAAAAAAA4geAIAAAAAAAAAAAAkgiOAAAAAAAAAAAAcALBEQAAAAAAAAAAAPT/t3fnT1LXdx7Hn33MzVzMAWSYA3REbh0uRQuDia4Yj9XF8jZJZVNRI5XsVbu1f8BWtlKpTaKpHEqMB6JuICkkGpekVo0cA0IQYWBmmINjmAHm6Lm6h6F7en+Y2TZToICUgR+ej9/m29/P0Z+uzy/zqs/nDQZHkiRJkiRJkiRJGhO+tdARTgAACulJREFU2A4KCnPJnpBOX1/vuQcLh8nLLeBwa4crL0mSJEmSJEmSdJm56OAoGB6hsrKC6upqIPCp78ZiMWprt1FRNdnwSJIkSZIkSZIk6TJzUcFRQeEEMjMzWLp06QW127Tpf1x5SZIkSZIkSZKky8zF1TgKQHp6+gU1ycrKIh6Pu/KSJEmSJEmSJEmXmaBLIEmSJEmSJEmSJDA4kiRJkiRJkiRJ0pjw5TqxsqlTWHrjEoqKCgkGgwzFhtjz4T4+2LEbgJqF87n66mpeefnX49rNnTeLmoXzeeGXawEIBoMsWlLDrFlXkZefR/x0nPb2Dmq37aTtaDsADz92Hw31Teyo3ZXq50u33MS0aRU894uXUs+Kiyfy2Ncf4KfP/JJYbOiMOdcsnM8Xl99IIpEAIJkcobs7wvvvbaO15TAAd99zO9OvqGJkZCTVLhqNsb+uns1/qiWZTAKQk5PN9TcsZvoVlWRnZxOLxWhtOcKW92vp7x8AoLS0mEe/dj/xeCLV10D/AB/u3jtunZbffCMjI8kz5vvWm3/gQF0DT33nm4TD4dTYyWSS3kgf22t3sb+ungWLrmHZTaN1rAIBCAQCqf4G+gd49ucvctPyG8idMIGNb7ydmtfZxtz7UR2b3n4HgDlzZ1KzYD45OdkEAgEikV62btlBS/Mhd6YkSZIkSZIkSZfAZRkcZWSkc+/KO3n7rT9Sf+AggUCA0/Eo3171BO3t7bQdPQ7ASHLkrO3/MgC5654V5Ofn8caGtzh6pJ2BwQEWL7mWe1feyeuvrud4RyctTYeYOnUKO2o/7qOqqpxQOESkr5OCvOLRZ9MqaG8/cdbQ6P8dO9bOq2vWAxCPxymdXMgjjz7EMz/+GSOJAADv/2krO2r/nJrjYKyXbz/1OMePH6exvoWsrEweemQlJ0508vzql4gOniI61MdX7ljBQ4+uZPWzLxI//fF3//73fkx6ejrJZJLTiRhPrXqC9o522o6MrtORI228vva3n7rmq597gf7eKACJRIKc3HS+9fjf09zSxM4du9k5FkSVTi7kS19eztqX149rP5JIMDQ8fl3+8z9++Ik1sCoqp7LspqWsefl1eiP9JJNJikryeOyrD/NfP/gJAULuTkmSJEmSJEmS/souy6vq8vJyCYfDfLSnjkBgNGxJC2fz8ouv09jQct79VFaVU1lZzvOrX6LzZA+ZmZkUFxXTfPAI27Zu59bbbgagufkQlVUVxGIxAAoK8wmFQuzdW8ecubNT/VVNq6Sx/uB5jx8Oh+nu7Kerq5vJU0rP+k4gEGBCdgGHD7VRVjYFgIWLr2V4+DS/WbeR08MjpKWlkZ9bxPvvbqfzZBcLFs7/xL7Sw9kcqGtgxozqz7z+oVCIoWiCSE8vhYUF4z6bVDqFtLQzw6Bg6MKCnqLiifT0ROiN9Kfm3t3Zz7M/e4FEPOnOlCRJkiRJkiTpErgsg6Pu7gidnV08+Mi9XFldRV5+LgDRwRjZ2dnn3U/VtAqOHj1GMHDmwarGhhZKS0vojpyg/VgHp4ZOUVSSB8C0aRUcPtzG/rr9zJ83D4BwWpiyqVPYu3ffhS9yMEh6Wvonfl5SUkR19XSOtXUAo4HXR3v2Egye+fM01DcxZ87sTx0vFAoRCn32w2TBYJDqq6ZTUFhAR/vxz+U3bmk+RFHxRJYtv47pV1SRmZkJwOBgjHA47M6UJEmSJEmSJOkSuCz/Q59IJFj78jqmX1nO1TOrWfGVW4lFY9Ttq6d2285UDaHJkyfxzccfI/QXp13C4TB9faOnWDIzM+iN9J51jIH+QQDy8vIIBAK0tBxi3vx5bNu8k8qqCvbt3U/zwSM89PADxONxpk2rYGjoFLHocOoU1LkEg0FmXF1NQUE+DQ0HSU/LAmDpDUtYtLiGYDBIKBQiFo2xY/su6g80EQgEyMzIoKur++zzHhgkO2c0PIvH42d8PmlyKTNnzWDDhjdTz6ZOLeM7//gtIDBujZ/50bOpv7/6tYdJJkcIBIKkpaVx4sRJ1v16AyQ/+5Vx//Jvq8aNCfDa2t/Q0X6cSE8vz69ewxVXVrJg0Xzu+tsVnDzZya4PPmR/XYM7U5IkSZIkSZKkS+ASBEfndw1ZPB6n4UALDQdaSCQSZGSFeODB+0iMnKZ262h9oJMnTvL0j34xrl3Nwrlcv3QJAIODUUonFZ+1/wm5OQDEYkPk5mTS0nyIpTcs5v13aymvKOO/X19PQUEBvZE+snLSmFrxBZqbWs8ZGk2ZMpknV32DYDBIMpmku6uH19auS4VGAFs216ZqHJVOLmTF7X/Dtq0fkJGRMTrvaIzcvAlnn/eEHKLR0Sv1EiOjAdp3/+kJAoEAgUCAgf4B3nt3C0cPt6faHD167hpHL/xqTarG0Y3LFhMKhTh6+NhF/dLf/97Tn1jjCGBwIMqe3fvZs3s/0WiUiqopPPDgfXR3d3G8o8vdKUmSJEmSJEnSX9lFBUdDsWE6EydpaztGWdkXzqtNY2MjeXn5DPR+cjBQUlJEZlYmRw63AaNXr8WH4aM9+7myeloqOEqMjDBx4sRxbXOyc0kLpwGj16EtWlxDpK+TgrzxAdKsWTPo6uwmN2e0hk9ry2Fuv+MWCosm0BvpIy08GuIcaj3C7NmzqKgs5713Np/z+7W3d/DqmvXnvYYnOno4PRznmprZ7N93cGwuh1iw4Fp279p3xrVtM2fPoLmpJbUuAD/8wU8/NaC5ULt37ePr33iIrVu2MXzq86k3VFlVTn//AN1dPQBkZ2fTeaKXQ61HKJ1UYnAkSZIkSZIkSdIlcNHBUcWkKWzc+AbFxSXnfH94eJihoSFG4p9eWim/IJ/bVtzM+nUbaD92kmQySeHEAubOm8XBxubRl5JnDzSCwSCMnQo61tZBY0MTq1Y9yVtvbaK3Z4D0jHRmzqxmwaJreO3VjwOeU6eGaWvrYMXtt9HQcDD1/FDrEW5YtoSioonUHzh4QTWWzte7/7uZlfffzZ937SYzYwI7d3zI1TOv4o67bmHb1p309w2Sl5fL4utqKCws4KUXXiEjPYtw6PM5MDYwMMj22l3c/8B9PL/6lc+l5tD06ZVMrSjjt+s30t83SCAQoLy8jIrKcrZsrnVnSpIkSZIkSZJ0CVx0InC4tZ3MrHQiPf3nfjkJkZ7Bc752sLGZtwNw/dLFTJo8iUAgQHQwSt2+erZs3j560uY86wy9uXETc+bO4JZbl1NSUkI8HudYWwcv/uoVeiMD495taWpl2ReX8saG36WetbUdo6S4iKam5s8lNBodo53Ghibu/bu7eXPjHxkeHubVNeuYXzObe1feSV5eLtFojOamVn7y9M/JSM+6oP7Ly8v4h39+8oznez+qY9Pb75y1zQc7djNn7kxmzJpOU8Phz/S9/vXfv3vGs0ikl+efW8O772zh2po53H3P7eTn55McSdIT6eX3b/6Brs6IO1OSJEmSJEmSpEsgsODa65IugyRJkiRJkiRJkoIugSRJkiRJkiRJksDgSJIkSZIkSZIkSWMMjiRJkiRJkiRJkgQYHEmSJEmSJEmSJGmMwZEkSZIkSZIkSZIAgyNJkiRJkiRJkiSNMTiSJEmSJEmSJEkSYHAkSZIkSZIkSZKkMQZHkiRJkiRJkiRJAgyOJEmSJEmSJEmSNMbgSJIkSZIkSZIkSYDBkSRJkiRJkiRJksYYHEmSJEmSJEmSJAkwOJIkSZIkSZIkSdIYgyNJkiRJkiRJkiQB8H/i1HqNbSHirQAAAABJRU5ErkJggg==" - } - }, "cell_type": "markdown", "metadata": {}, "source": [ - "![Screenshot from 2023-08-31 18-51-35.png]()" + "![Screenshot](https://github.com/karmenrabar/pygraphistry_images/blob/main/access2.png?raw=true)" ] }, { @@ -545,15 +524,10 @@ ] }, { - "attachments": { - "Screenshot from 2023-08-31 18-52-42.png": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAABpsAAAIbCAYAAAAU+yY+AAAAi3pUWHRSYXcgcHJvZmlsZSB0eXBlIGV4aWYAAHjaVY7dDYRACITfqcIShp+FpZzTaHIdXPmyronn9wADITPQ/vsetAwYRtaie7qjsLSUT4mOiQIs4NGrTu6uXEpqvGdSmcKzB+w5NMOLpt79CIvw5ptvUu6yq16VFXRdDbPxSj5GucJn9HvPK+Q/gE4A3y1JMryusAAACghpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDQuNC4wLUV4aXYyIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgIGV4aWY6UGl4ZWxYRGltZW5zaW9uPSIxNjkxIgogICBleGlmOlBpeGVsWURpbWVuc2lvbj0iNTM5IgogICB0aWZmOkltYWdlV2lkdGg9IjE2OTEiCiAgIHRpZmY6SW1hZ2VIZWlnaHQ9IjUzOSIKICAgdGlmZjpPcmllbnRhdGlvbj0iMSIvPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg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K5lOyMUxXRjLH8YZ7uBMy9vUZHpJ4yLjKZhTfc968vAYkMPERMEmERERERERkRtFwSYRERGR28SoGaDNXYF34d2kihquqaxjWFDcAKZF14W9+NJRTHdgTvXftrxgmOQwcGs5iIiIiIiIiNwwpoZARERE5PZwwaqE+juuOdA0SWEdzNtI0vLjmNac6r9jWjiGgYOhxSAiIiIiIiJyAynYJCIiInIbaLEqcAproLzxw1dW3ACFNaRyzhwbBQfDQaEmERERERERkRtMwSYRERGROS6HScQMQMXS61OhYcKCu7HNuXXisplLYzpZ3dckIiIiIiIicoPpziYRERGROS5u+Mi6A1BQff0qtdzg9s+pcTAzY6QcS8EmERERERERkRv9/+QaAhEREZG5rdtVjFWz6jrXaoBhwBwK3Pjj/XjsNJbjaFGIiIiIiIiI3EAKNomIiIjMcXG85PyF179ifxFkknNkFBxCkXb8ThIDBZtEREREREREbiQFm0RERETmMOe9HUjGLHys8wZhbHROjIMrm6QzniPoJLUoRERERERERG4wBZtERERE5rBZ3cNjANHeOTEO3kQYIx0n4KS1KERERERERERuMAWbREREROb0hzkHcMCZhbuVcjmIDc2JcciN9hF0kridnBaFiIiIiIiIyA2mYJOIiIjIHOdzMrjT0etfcX8zxPrJH7lwS/ffyqUoCJ+m1B7FxNaCEBEREREREbnBFGwSERERmeOqs0Nkuk9d51odjEgfZi5NtucEZu5WPZ7OoXjgKJFUmkI7rsUgIiIiIiIichMo2CQiIiIyx+U7Y1iJYayxwetXaS6DMzZMnR0mExvBF+26Jftu5VJEBjopyUUuHikoIiIiIiIiIjeagk0iIiIic5yFTcBJQ8913N3U+jZ5mQildpTS3AhFHXuxcolbru8lPe/iSgxRake1EERERERERERuEgWbRERERG4DDbk+GGqD8Ie/X8kY7cIIt1KXGwCgPDfKcCKJp/voLdXnvNFWBvq6qM4OYZHTIhARERERERG5SRRsEhEREbkNeJws9dl+jJa38UU6Z17R2BCc30tVbhi/M35Pk4scNdkhPH2nKBw6DbfAcXWeVASn4zDlmTAhJ6EFICIiIiIiInITKdgkIiIicpsotqPUZvtINr8JIx3XXN7IjsHJVyhND1BlD09Ky3fGyM+MEGk5RCDW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0idy3dkkIiIiIiIic5upIRAREREREREREREREZGZUrBJREREREREREREREREZkzBJhEREREREREREREREZkxl4bg5ikpLWHhggXU1tWQn5eHPxAgm80yNDTEmeaznDhxEtu2P3QZERERERERERERERGR2WJsWHeno2G4OR57/BM0NMy7Ynp3dzcvPPcLMtnMhyojIiIiN5eDA45+GUSu9IncxMDQOIiIiIiIiMicpWP0bgF9vX0cPHiIfXubuHD+wsTz6upqNmxcf93KiIiIyM1hXPKnyFSrQ0RERERERGQuu+7H6FVVV/IbX3hqWnm/953vMzg4xKbN67ln212T0mzbJpVMEw4P0dLSzrEjJ0gkklPWEwwGWLN2JfPm11NcVIjb4yaZTBKNxDh/roXjx04Ri8UnlXnqM4/TML/+A9t4pvk8Lzz30vv9q6pg3YbVVFVXEgoFMU2TVCpFeHCIM83nOHzoOI4zvc1iF85fYM/uvQwPD096vmXL3axbvxaAhQsX0LTv7Q9VRkRERG42Yzym4PDeHyIT60K7mkRERERERGSuu6l3NrW2XyAUKJwyzTRN/AEftYFqauuq2XDHGn7+/Et0tHdPyrdwUQOPfOJBvF7PpOfBYIBgMEBlVTnr1q/i2Z/+nN6egWtuYywenfh62fJGHvnEAxjG+A8EkskUdi5HIBigtm68nQVF+by5Y8+06j558tSUz9vb2yYCRz6v90OXERERkZvPAJz3/9CAaEUo0CQiIiIiIiK3jVkNNr3x+lvsfLPpiunBYMFlz771ze/Q3TlANpslEHSzcfNG7r//PgIBP5966jG++Q/fJZkYv4+orLyUx574OC6XhW077NjxOvvfPcjoaIyCghCbNt/B9u33EQgG+OSnH+Nv//ff4/UGLnvnf/uTP8Pn803ZRrfbjfdi8Gbb9i0YhkFkNMJXv/p1/N48AKKxYb70ld+irq6WDRvW8torO3BZvhmPWyiUN/H1aCQya2VERETkRjIwMHAMBZrk/fUgIiIiIiIicjuY1WBTNpslFApdU5lUKj2pzJGDJxkZGuXpX/skbrebjz2wjV+88BoAd2/ZiMtlAfDv//Z9+nuH8bgClJWMB5QOHzhJLDrG+vVrOHHyFLGx6JTBpmAwiN/vv2q73B43weB42TNnzk4EmgDyQkW89spOCovy6OrqJBZNUlg4s2CTZVmsW7d24vvm02dmpYyIiIjcHOMBBgUZREREREREROT2Yc6FRra1dtHb2w9A45JGotEohmHQMH8eAAMDg/T1DE1Z9tyZVn74g+c4cfQMJUUVM25DJp1hbCwBwOo1q6isLsHlej9WNxQe5sK5dlIJm8LCwhm/595776G4pPhivwY4ceLkrJQRERERERERERERERG5HmZ1Z5PL5SIWi1FSUnJZWi6XI5vNTruu7s4eKivLMU0Df9CF1+ue2NV0/vyFiXuUZiIej+P2uAgGLt+FlUqlJr7eu/sdHnhoGx6Ph89/4XNkszn6+wbo7emjo6ObttYOMpnMjNtxxx0bWLFy+XibYnF++YuXsG37upcRERERERERERERERG5XmY12LT9/m1sv3/blGmHDx1nx6tvTbuuVCo98bXX68W4ZE9W4uKOo/cYhsEf/vHvXlZHR3snP/3xzy97/ud/+adXfO/Xv/ZtEokkAEcOHyccHmLd+pUsXLQQl8uiuqaS6ppK1t+xhmQyxd7db3Po4LFrHquVq1Zw512bx/uTSPDccy8QiUavexkREREREREREREREZHryXWzXhyJjFxTfn/Af0nZCAH/+7t3gqHgpLyGYUzserpUKpW85nZ2dXVTXFw88X1nRzedHd0kEglC+V4aGhpYtmwJjUsa8fm83P/AvXR2dTLQNzztdzQuWcy2bfcC40GjZ599jqGhoeteRkRERERERERERERE5Hqb1WDTq6/s4Ojh09elrvp5tQCk0xksw4edM0inM3g8bhoXL+Kt1/dhmuPbnWzb5q//8u8nyj792ceZN6/+inX/jz/7K/x+/5RplwaaLuX3+8ll4PzZds6fbScQ2sl//r0vYxiwctVy3ujbM61+Ncxv4IEHPoZhGCQTCX727PMMhYeuexkREREREREREREREZHZYM6FRq5as5zi4kIADh86gs/nw7Iszp9rAaCwqJCGBdVXLB8IBD90G5YuW8yTn3qU3/7y5yktvTwAlYinJ+5rysvLm1adtbU1fPzjD2GaJslEgmeffZ5wOHzdy4iIiIiIiIiIiIiIiMyWWd3Z5HK5iMVilJSUTJmey2XJZnOTnnm9HsIDA5SWlhAMBlm2opGNm9YDkEgk+dnPnqekqAKApr3vsnDRfDweN09/5in27mmitaWTeCyOx+uhoqKMVauXU1Y2/v73gkG/Kh6P4/a4CAZCU6anUimy2RyLFs8H4NHHH+Tll3YQGYmRyWbJzwuxbsMaPB43AC0XWj5wbEzT5NHHHsXlGp+CZCrF3VvunnKMXvzFL2dcRkREREREREREREREZDbNarBp+/3b2H7/tiumv/v2QXa+tW/Ss6/8zn+aMm8kEuVb//jtiUATwNDQCM89+yKfePwhAgE/W7beyZatU7/ryOFj/ORHL1BQUHBZ2p//5Z9etR9/89ff4NzZCxw8cJT1G1ZTVlbKF7742Snzdnf3sHf3/g/c3WSaBh63e+L7wsJCCgsLL8t3aYBsJmVERERERERERERERERmk+tmvnxkdPiq6WPxMQYHwxw6dJj97xydMrDS3tbJP33zX1jcOJ+ly5ZQUVGO1+slMTbGYHiIs2fP8nbTu/i9eVMGmqYjFovj9/t5Y8cuTp08zbIVjTTMm0d+QT6maTIWj9Pd08uRw0c429w27WP0RERERERERERERERE5jpjw7o7HQ2DiIiIiIiIiIiIiIiIzISpIRAREREREREREREREZGZUrBJREREREREREREREREZkzBJhEREREREREREREREZkxBZtERERERERERERERERkxhRsEhERERERERERERERkRlTsElERERERERERERERERmTMEmERERERERERERERERmTEFm0RERERERERERERERGTGFGwSERERERERERERERGRGVOwSURERERERERERERERGZMwSYRERERERERERERERGZMQWbREREREREREREREREZMYUbBIREREREREREREREZEZc12viuobKolER8hms9PKHwgEMXDR1xPWLIiIiIiIiIiIiIiIiMxR1yXYVN9QCYbNgw8+hN/vn1aZnp5u9u3bR15+gGhkTDMhIiIiIiIiIiIiIiIyB12XYFMkOsKDDz5EfX3dtMuUlZXS19fPQP+ggk0iIiIiIiIiIiIiIiJz1HW5symbzU57R9OlvF4vjuNoFkREREREREREREREROYoU0MgIiIiIiIiIiIiIiIiM6Vgk4iIiIiIiIiIiIiIiMyYgk0iIiIiIiIiIiIiIiIyY65buXHl5aV88bc+y9/+738gm81NPC8sKuBLX/kCX//at0kkkmzbvoW8UIifv/DyRJn38tu2zeBAmKNHTnDi+OmJOp781KMsXDR/yjujvvUP3yMWi0/ZJsuy2LJ1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" - } - }, "cell_type": "markdown", "metadata": {}, "source": [ - "![Screenshot from 2023-08-31 18-52-42.png]()" + "![Screenshot](https://github.com/karmenrabar/pygraphistry_images/blob/main/access.png?raw=true)" ] }, { From a5511d7c2ff693c630f030fdc35385f7a7b28b83 Mon Sep 17 00:00:00 2001 From: lmeyerov Date: Thu, 14 Sep 2023 18:07:52 -0400 Subject: [PATCH 003/104] docs(memgraph demo): update text --- .../memgraph/visualizing_iam_dataset.ipynb | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb b/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb index 12e5062121..ec46361f35 100644 --- a/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb +++ b/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This notebook showcases the utilization of Graphistry to visualize data from Memgraph using a sample dataset related to a company's Identity and Access Management. We'll demonstrate how Graphistry streamlines the visualization of Cypher queries, making it easier to analyze extensive data effectively." + "This notebook showcases using Graphistry to visualize data in Memgraph for a sample dataset of a company's Identity and Access Management records. We'll demonstrate how Graphistry streamlines the visualization of Cypher queries, making it easier and more effective to analyze rich and potentially large data in Memgraph." ] }, { @@ -32,6 +32,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ + "#### About Graphistry\n", + "\n", + "[Graphistry](https://www.graphistry.com) is a visual graph AI platform featuring rich point-and-click visual analytics and end-to-end GPU acceleration for exploring and analyzing many relationships. The OSS [PyGraphistry](https://github.com/graphistry/pygraphistry) library enables quickly visualizing large data from Memgraph, and provides a rich and easy dataframe-centric library for intermediate graph processing steps like data shaping, graph algorithms, graph layouts, autoML, autoAI, and data-driven visualization configuration. If you have a GPU where your PyGraphistry client is running, it supports automatic GPU acceleration for the locally executed steps. PyGraphistry is often used directly within data science notebooks and as a Python toolkit for building custom dashboards and webapps.\n", + "\n", "#### About Memgraph" ] }, @@ -106,7 +110,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Lastly, establish a connection with your Graphistry GPU server account. Make sure to substitute the connection string and password with your personal credentials. You can create your account [here](https://hub.graphistry.com/). For additional configuration options, refer to [GitHub](https://github.com/graphistry/pygraphistry#configure)." + "Lastly, establish a connection with your Graphistry GPU server account. Make sure to substitute the connection string and password with your personal credentials. You can create your account [here](https://www.graphistry.com/get-started). For additional configuration options, refer to [GitHub](https://github.com/graphistry/pygraphistry#configure)." ] }, { @@ -278,7 +282,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "After populating Memgraph instance, it's time to visualize the dataset with graphistry. But first, let's see the graph schema in Memgraph Lab. It defines the structure of your data and its relationships, providing a blueprint for how your data elements are connected and organized within the graph database and offers interactive graph visualizations.\n" + "After populating your Memgraph instance, it's time to visualize the dataset with Graphistry. But first, let's see the graph schema in Memgraph Lab. It defines the structure of your data and its relationships, providing a blueprint for how your data elements are connected and organized within the graph database and offers interactive graph visualizations.\n" ] }, { @@ -542,11 +546,15 @@ "metadata": {}, "source": [ "\n", - "Pygraphistry complements Memgraph by providing a specialized tool for creating rich and interactive visualizations of graph data stored in Memgraph. It allows users to gain deeper insights into their graph data by leveraging the advanced visualization capabilities of the Graphistry platform, especially when dealing with complex and extensive graph data sets. \n", + "PyGraphistry complements Memgraph by providing a specialized tool for creating rich and interactive visualizations of graph data stored in Memgraph. It allows users to gain deeper insights into their graph data by leveraging the advanced visualization capabilities of the Graphistry platform, especially when dealing with complex and extensive graph data sets.\n", + "\n", + "The [PyGraphistry README.md](https://github.com/graphistry/pygraphistry) shares examples for how to take your Memgraph query result and perform on-the-fly steps like filtering, Pandas dataframe analysis, graph algorithm enrichments, autoML & autoAI analysis, new layouts, and configuring data-driven visualizations.\n", + "\n", + "Feel free to get your hands on Graphistry and Memgraph and share your insights or questions with us on the [Memgraph Discord](https://discord.com/invite/memgraph) and [Graphistry community Slack](https://join.slack.com/t/graphistry-community/shared_invite/zt-53ik36w2-fpP0Ibjbk7IJuVFIRSnr6g)!\n", "\n", - "Feel free to get your hands on Graphistry and Memgraph and share your insights or questions with us on [Discord](https://discord.com/invite/memgraph) !\n", + "You can find out more about building and scaling modern IAM systems with Memgraph [here](https://memgraph.com/identity-access-management?utm_source=memgraph&utm_medium=referral&utm_campaign=bfb_blog&utm_content=iam) and on blogposts [What Makes Memgraph Great for Real-Time Performance in IAM Systems](https://memgraph.com/blog/what-makes-memgraph-great-for-real-time-performance-in-iam-systems), [Benefits Graph Databases Bring to Identity and Access Management](https://memgraph.com/blog/benefits-graph-databases-bring-to-identity-and-access-management) and [How Graphs Solve Two Biggest Problems of Traditional IAM Systems](https://memgraph.com/blog/how-graphs-solves-two-biggest-problems-of-traditional-iam-systems).\n", "\n", - "You can find out more about building and scaling modern IAM systems with Memgraph [here](https://memgraph.com/identity-access-management?utm_source=memgraph&utm_medium=referral&utm_campaign=bfb_blog&utm_content=iam) and on blogposts [What Makes Memgraph Great for Real-Time Performance in IAM Systems](https://memgraph.com/blog/what-makes-memgraph-great-for-real-time-performance-in-iam-systems), [Benefits Graph Databases Bring to Identity and Access Management](https://memgraph.com/blog/benefits-graph-databases-bring-to-identity-and-access-management) and [How Graphs Solve Two Biggest Problems of Traditional IAM Systems](https://memgraph.com/blog/how-graphs-solves-two-biggest-problems-of-traditional-iam-systems).\n" + "The [PyGraphistry demos folder](https://github.com/graphistry/pygraphistry/tree/master/demos) has more examples of how security operations and security data science teams are using Graphistry, including a free [GPU graph visualization & AI security analytics training from Nvidia GTC 2022](https://www.nvidia.com/en-us/on-demand/session/gtcspring23-dlit51954/). You may also want to explore how [Louie.AI](https://www.louie.ai) is enabling analyst teams to talk directly to their data silos in natural language and get back analyses and visualizations, including Graphistry graph and AI visualizations.\n" ] } ], From 5490ea29cdf868e3f67312732bc214a113a17081 Mon Sep 17 00:00:00 2001 From: lmeyerov Date: Thu, 14 Sep 2023 18:09:33 -0400 Subject: [PATCH 004/104] docs(memgraph demo): update text 2 --- .../memgraph/visualizing_iam_dataset.ipynb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb b/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb index ec46361f35..6c03d5a4b5 100644 --- a/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb +++ b/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb @@ -548,13 +548,11 @@ "\n", "PyGraphistry complements Memgraph by providing a specialized tool for creating rich and interactive visualizations of graph data stored in Memgraph. It allows users to gain deeper insights into their graph data by leveraging the advanced visualization capabilities of the Graphistry platform, especially when dealing with complex and extensive graph data sets.\n", "\n", - "The [PyGraphistry README.md](https://github.com/graphistry/pygraphistry) shares examples for how to take your Memgraph query result and perform on-the-fly steps like filtering, Pandas dataframe analysis, graph algorithm enrichments, autoML & autoAI analysis, new layouts, and configuring data-driven visualizations.\n", - "\n", "Feel free to get your hands on Graphistry and Memgraph and share your insights or questions with us on the [Memgraph Discord](https://discord.com/invite/memgraph) and [Graphistry community Slack](https://join.slack.com/t/graphistry-community/shared_invite/zt-53ik36w2-fpP0Ibjbk7IJuVFIRSnr6g)!\n", "\n", "You can find out more about building and scaling modern IAM systems with Memgraph [here](https://memgraph.com/identity-access-management?utm_source=memgraph&utm_medium=referral&utm_campaign=bfb_blog&utm_content=iam) and on blogposts [What Makes Memgraph Great for Real-Time Performance in IAM Systems](https://memgraph.com/blog/what-makes-memgraph-great-for-real-time-performance-in-iam-systems), [Benefits Graph Databases Bring to Identity and Access Management](https://memgraph.com/blog/benefits-graph-databases-bring-to-identity-and-access-management) and [How Graphs Solve Two Biggest Problems of Traditional IAM Systems](https://memgraph.com/blog/how-graphs-solves-two-biggest-problems-of-traditional-iam-systems).\n", "\n", - "The [PyGraphistry demos folder](https://github.com/graphistry/pygraphistry/tree/master/demos) has more examples of how security operations and security data science teams are using Graphistry, including a free [GPU graph visualization & AI security analytics training from Nvidia GTC 2022](https://www.nvidia.com/en-us/on-demand/session/gtcspring23-dlit51954/). You may also want to explore how [Louie.AI](https://www.louie.ai) is enabling analyst teams to talk directly to their data silos in natural language and get back analyses and visualizations, including Graphistry graph and AI visualizations.\n" + "The [PyGraphistry README.md](https://github.com/graphistry/pygraphistry) shares examples for how to take your Memgraph query result and perform on-the-fly steps like filtering, Pandas dataframe analysis, graph algorithm enrichments, autoML & autoAI analysis, new layouts, and configuring data-driven visualizations. The [PyGraphistry demos folder](https://github.com/graphistry/pygraphistry/tree/master/demos) has more examples of how security operations and security data science teams are using Graphistry, including a free [GPU graph visualization & AI security analytics training from Nvidia GTC 2022](https://www.nvidia.com/en-us/on-demand/session/gtcspring23-dlit51954/). You may also want to explore how [Louie.AI](https://www.louie.ai) is enabling analyst teams to talk directly to their data silos in natural language and get back analyses and visualizations, including Graphistry graph and AI visualizations.\n" ] } ], From d64b71adf3abf7df7ef395a652555fbac4390d5f Mon Sep 17 00:00:00 2001 From: lmeyerov Date: Thu, 14 Sep 2023 18:15:07 -0400 Subject: [PATCH 005/104] docs(memgraph demo): update text 3 --- .../demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb b/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb index 6c03d5a4b5..9d2658d427 100644 --- a/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb +++ b/demos/demos_databases_apis/memgraph/visualizing_iam_dataset.ipynb @@ -552,7 +552,7 @@ "\n", "You can find out more about building and scaling modern IAM systems with Memgraph [here](https://memgraph.com/identity-access-management?utm_source=memgraph&utm_medium=referral&utm_campaign=bfb_blog&utm_content=iam) and on blogposts [What Makes Memgraph Great for Real-Time Performance in IAM Systems](https://memgraph.com/blog/what-makes-memgraph-great-for-real-time-performance-in-iam-systems), [Benefits Graph Databases Bring to Identity and Access Management](https://memgraph.com/blog/benefits-graph-databases-bring-to-identity-and-access-management) and [How Graphs Solve Two Biggest Problems of Traditional IAM Systems](https://memgraph.com/blog/how-graphs-solves-two-biggest-problems-of-traditional-iam-systems).\n", "\n", - "The [PyGraphistry README.md](https://github.com/graphistry/pygraphistry) shares examples for how to take your Memgraph query result and perform on-the-fly steps like filtering, Pandas dataframe analysis, graph algorithm enrichments, autoML & autoAI analysis, new layouts, and configuring data-driven visualizations. The [PyGraphistry demos folder](https://github.com/graphistry/pygraphistry/tree/master/demos) has more examples of how security operations and security data science teams are using Graphistry, including a free [GPU graph visualization & AI security analytics training from Nvidia GTC 2022](https://www.nvidia.com/en-us/on-demand/session/gtcspring23-dlit51954/). You may also want to explore how [Louie.AI](https://www.louie.ai) is enabling analyst teams to talk directly to their data silos in natural language and get back analyses and visualizations, including Graphistry graph and AI visualizations.\n" + "The [PyGraphistry README.md](https://github.com/graphistry/pygraphistry) shares examples for how to take your Memgraph query result and perform on-the-fly steps like filtering, Pandas dataframe analysis, graph algorithm enrichments, autoML & autoAI analysis, new layouts, and configuring data-driven visualizations. The [PyGraphistry demos folder](https://github.com/graphistry/pygraphistry/tree/master/demos) has more examples of how security operations and security data science teams are using Graphistry, including a free [GPU graph visualization & AI security analytics training from Nvidia GTC 2022](https://www.nvidia.com/en-us/on-demand/session/gtcspring23-dlit51954/). You may also want to explore how [Louie.AI](https://www.louie.ai) is enabling analyst teams to talk directly to their data silos in natural language and get back analyses and visualizations, including Graphistry graph and AI visualizations. Finally, you may consider [graph-app-kit](https://github.com/graphistry/graph-app-kit) as a maintained OSS Streamlit distribution and reference for building PyData dashboards with Graphistry and your Memgraph data.\n" ] } ], From e14b8396a200d7e5fedc83d7b0a8acaac2ddd240 Mon Sep 17 00:00:00 2001 From: lmeyerov Date: Sat, 16 Sep 2023 18:01:58 -0400 Subject: [PATCH 006/104] docs(changelog); add memgraph tutorial --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf30f599cb..5269a61ad9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +### Docs + +* Memgraph: Add tutorial (https://github.com/graphistry/pygraphistry/pull/507 by https://github.com/karmenrabar) + ## [0.29.5 - 2023-08-23] ### Fixed From d4a3136d326888787fc18a7e21d9b02db313fffb Mon Sep 17 00:00:00 2001 From: Vaim Dev Date: Thu, 12 Oct 2023 16:01:31 +0800 Subject: [PATCH 007/104] fix (sso): In databricks, the HTML is not displayable, print out the sign in url to let user manually copy and paste in browser --- graphistry/pygraphistry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/graphistry/pygraphistry.py b/graphistry/pygraphistry.py index 9b4430d9d8..7be979818a 100644 --- a/graphistry/pygraphistry.py +++ b/graphistry/pygraphistry.py @@ -268,6 +268,7 @@ def _handle_auth_url(auth_url, sso_timeout): from IPython.display import display, HTML display(HTML(f'Login SSO')) print("Please click the above link to open browser to login") + print(f"If you cannot see the link, please open browser, browse to this link: {auth_url}") print("Please close browser tab after SSO login to back to notebook") # return HTML(make_iframe(auth_url, 20, extra_html=extra_html, override_html_style=override_html_style)) else: From 38f401a007a91dbee8ea604e3053c6dc548e25e2 Mon Sep 17 00:00:00 2001 From: Vaim Dev Date: Thu, 12 Oct 2023 22:30:53 +0800 Subject: [PATCH 008/104] wip (sso): Add explicit SSO login prompt option, display, browser or None (just print auth url) --- graphistry/pygraphistry.py | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/graphistry/pygraphistry.py b/graphistry/pygraphistry.py index 7be979818a..4ab0460a91 100644 --- a/graphistry/pygraphistry.py +++ b/graphistry/pygraphistry.py @@ -202,7 +202,7 @@ def pkey_login(personal_key_id, personal_key_secret, org_name=None, fail_silent= return PyGraphistry.api_token() @staticmethod - def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SECONDS): + def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SECONDS, opt_into_type=None): """Authenticate with SSO and set token for reuse (api=3). :param org_name: Set login organization's name(slug). Defaults to user's personal organization. @@ -211,8 +211,8 @@ def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SEC :type idp_name: Optional[str] :param sso_timeout: Set sso login getting token timeout in seconds (blocking mode), set to None if non-blocking mode. Default as SSO_GET_TOKEN_ELAPSE_SECONDS. :type sso_timeout: Optional[int] - :returns: None. - :rtype: None + :returns: token or auth_url + :rtype: Optional[str] SSO Login logic. @@ -244,11 +244,12 @@ def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SEC auth_url = arrow_uploader.sso_auth_url # print("auth_url : {}".format(auth_url)) if auth_url and not PyGraphistry.api_token(): - PyGraphistry._handle_auth_url(auth_url, sso_timeout) + PyGraphistry._handle_auth_url(auth_url, sso_timeout, opt_into_type) + return auth_url @staticmethod - def _handle_auth_url(auth_url, sso_timeout): + def _handle_auth_url(auth_url, sso_timeout, opt_into_type): """Internal function to handle what to do with the auth_url based on the client mode python/ipython console or notebook. @@ -256,14 +257,14 @@ def _handle_auth_url(auth_url, sso_timeout): :type auth_url: str :param sso_timeout: Set sso login getting token timeout in seconds (blocking mode), set to None if non-blocking mode. Default as SSO_GET_TOKEN_ELAPSE_SECONDS. :type sso_timeout: Optional[int] - :returns: None. - :rtype: None + :returns: token + :rtype: token: Optional[str] SSO Login logic. """ - if in_ipython() or in_databricks(): # If run in notebook, just display the HTML + if in_ipython() or in_databricks() or opt_into_type == 'display': # If run in notebook, just display the HTML # from IPython.core.display import HTML from IPython.display import display, HTML display(HTML(f'Login SSO')) @@ -271,13 +272,16 @@ def _handle_auth_url(auth_url, sso_timeout): print(f"If you cannot see the link, please open browser, browse to this link: {auth_url}") print("Please close browser tab after SSO login to back to notebook") # return HTML(make_iframe(auth_url, 20, extra_html=extra_html, override_html_style=override_html_style)) - else: + elif opt_into_type == 'browser': print("Please minimize browser after SSO login to back to pygraphistry") import webbrowser input("Press Enter to open browser ...") # open browser to auth_url webbrowser.open(auth_url) + else: + print(f"Please open browser, browse to this link: {auth_url}") + print("Please run graphistry.sso_get_token() to complete the authentication if you get timeout error") if sso_timeout is not None: time.sleep(1) @@ -563,7 +567,8 @@ def register( org_name: Optional[str] = None, idp_name: Optional[str] = None, is_sso_login: Optional[bool] = False, - sso_timeout: Optional[int] = SSO_GET_TOKEN_ELAPSE_SECONDS + sso_timeout: Optional[int] = SSO_GET_TOKEN_ELAPSE_SECONDS, + sso_opt_into_type: Optional[str] = None ): """API key registration and server selection @@ -691,7 +696,7 @@ def register( PyGraphistry.api_token(token or PyGraphistry._config['api_token']) elif not (org_name is None) or is_sso_login: print(MSG_REGISTER_ENTER_SSO_LOGIN) - PyGraphistry.sso_login(org_name, idp_name, sso_timeout=sso_timeout) + PyGraphistry.sso_login(org_name, idp_name, sso_timeout=sso_timeout, sso_opt_into_type=None) @staticmethod def __check_login_type_to_reset_token_creds( From 5d365fec8e36a4a9df21dafb03b97d958bf3f70e Mon Sep 17 00:00:00 2001 From: Vaim Dev Date: Thu, 12 Oct 2023 22:43:59 +0800 Subject: [PATCH 009/104] fix (mypy): fix error of mypy check --- graphistry/arrow_uploader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphistry/arrow_uploader.py b/graphistry/arrow_uploader.py index 765fe3067e..e9e735be8a 100644 --- a/graphistry/arrow_uploader.py +++ b/graphistry/arrow_uploader.py @@ -656,7 +656,7 @@ def post_arrow(self, arr: pa.Table, graph_type: str, opts: str = ''): raise Exception('No success indicator in server response') return out except requests.exceptions.HTTPError as e: - logger.error('Failed to post arrow to %s (%s)', sub_path, e.request.url, exc_info=True) + logger.error('Failed to post arrow to %s (%s)', sub_path, "{}/{}{}".format(self.server_base_path, sub_path, f"?{opts}" if len(opts) > 0 else ""), exc_info=True) logger.error('%s', e) logger.error('%s', e.response.text) raise e From d3e1a2735319998c4de80e5d44fe3552b223f938 Mon Sep 17 00:00:00 2001 From: lmeyerov Date: Thu, 12 Oct 2023 11:33:11 -0400 Subject: [PATCH 010/104] fix(uploader): guard against potential null deref in exn handler --- CHANGELOG.md | 4 ++++ graphistry/arrow_uploader.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5269a61ad9..173231098e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm * Memgraph: Add tutorial (https://github.com/graphistry/pygraphistry/pull/507 by https://github.com/karmenrabar) +### Fixed + +* Guard against potential `requests`` null dereference in uploader error handling + ## [0.29.5 - 2023-08-23] ### Fixed diff --git a/graphistry/arrow_uploader.py b/graphistry/arrow_uploader.py index 765fe3067e..0651f192d8 100644 --- a/graphistry/arrow_uploader.py +++ b/graphistry/arrow_uploader.py @@ -656,7 +656,7 @@ def post_arrow(self, arr: pa.Table, graph_type: str, opts: str = ''): raise Exception('No success indicator in server response') return out except requests.exceptions.HTTPError as e: - logger.error('Failed to post arrow to %s (%s)', sub_path, e.request.url, exc_info=True) + logger.error('Failed to post arrow to %s (%s)', sub_path, e.request.url if e.request is not None else "(No request)", exc_info=True) logger.error('%s', e) logger.error('%s', e.response.text) raise e From 7bfb22e69f4ed9b2732822510b7fa33dfe6250d9 Mon Sep 17 00:00:00 2001 From: lmeyerov Date: Thu, 12 Oct 2023 11:59:20 -0400 Subject: [PATCH 011/104] fix(sso display): correct val passing and add docs --- graphistry/pygraphistry.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/graphistry/pygraphistry.py b/graphistry/pygraphistry.py index 4ab0460a91..d4f3330791 100644 --- a/graphistry/pygraphistry.py +++ b/graphistry/pygraphistry.py @@ -211,6 +211,8 @@ def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SEC :type idp_name: Optional[str] :param sso_timeout: Set sso login getting token timeout in seconds (blocking mode), set to None if non-blocking mode. Default as SSO_GET_TOKEN_ELAPSE_SECONDS. :type sso_timeout: Optional[int] + :param opt_into_type: Show the SSO url with display(), webbrowser.open(), or print() + :type opt_into_type: Optional[Literal["display", "browser"]] :returns: token or auth_url :rtype: Optional[str] @@ -257,6 +259,8 @@ def _handle_auth_url(auth_url, sso_timeout, opt_into_type): :type auth_url: str :param sso_timeout: Set sso login getting token timeout in seconds (blocking mode), set to None if non-blocking mode. Default as SSO_GET_TOKEN_ELAPSE_SECONDS. :type sso_timeout: Optional[int] + :param opt_into_type: Show the SSO url with display(), webbrowser.open(), or print() + :type opt_into_type: Optional[Literal["display", "browser"]] :returns: token :rtype: token: Optional[str] @@ -568,7 +572,7 @@ def register( idp_name: Optional[str] = None, is_sso_login: Optional[bool] = False, sso_timeout: Optional[int] = SSO_GET_TOKEN_ELAPSE_SECONDS, - sso_opt_into_type: Optional[str] = None + sso_opt_into_type: Optional[Literal["display", "browser"]] = None ): """API key registration and server selection @@ -612,6 +616,8 @@ def register( :type idp_name: Optional[str] :param sso_timeout: Set sso login getting token timeout in seconds (blocking mode), set to None if non-blocking mode. Default as SSO_GET_TOKEN_ELAPSE_SECONDS. :type sso_timeout: Optional[int] + :param sso_opt_into_type: Show the SSO url with display(), webbrowser.open(), or print() + :type sso_opt_into_type: Optional[Literal["display", "browser"]] :returns: None. :rtype: None @@ -627,6 +633,14 @@ def register( import graphistry graphistry.register(api=3, protocol='http', server='200.1.1.1', org_name="org-name") + **Example: Override SSO url display method to use `display()`, `webbrowser.open()`, or just `print()`** + :: + + import graphistry + graphistry.register(api=3, protocol='http', server='200.1.1.1', org_name="org-name", sso_opt_into_type="display") + graphistry.register(api=3, protocol='http', server='200.1.1.1', org_name="org-name", sso_opt_into_type="browser") + graphistry.register(api=3, protocol='http', server='200.1.1.1', org_name="org-name", sso_opt_into_type=None) + **Example: Standard (2.0 api by username/password with org_name)** :: @@ -696,7 +710,7 @@ def register( PyGraphistry.api_token(token or PyGraphistry._config['api_token']) elif not (org_name is None) or is_sso_login: print(MSG_REGISTER_ENTER_SSO_LOGIN) - PyGraphistry.sso_login(org_name, idp_name, sso_timeout=sso_timeout, sso_opt_into_type=None) + PyGraphistry.sso_login(org_name, idp_name, sso_timeout=sso_timeout, opt_into_type=sso_opt_into_type) @staticmethod def __check_login_type_to_reset_token_creds( From 20680b23edc24494cf1e705ecc6d32c7e2a841fa Mon Sep 17 00:00:00 2001 From: Vaim Dev Date: Fri, 13 Oct 2023 08:40:46 +0800 Subject: [PATCH 012/104] fix (sso_login): fix missing parameter --- graphistry/pygraphistry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphistry/pygraphistry.py b/graphistry/pygraphistry.py index 4ab0460a91..d0c638a6af 100644 --- a/graphistry/pygraphistry.py +++ b/graphistry/pygraphistry.py @@ -202,7 +202,7 @@ def pkey_login(personal_key_id, personal_key_secret, org_name=None, fail_silent= return PyGraphistry.api_token() @staticmethod - def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SECONDS, opt_into_type=None): + def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SECONDS, sso_opt_into_type=None): """Authenticate with SSO and set token for reuse (api=3). :param org_name: Set login organization's name(slug). Defaults to user's personal organization. From b0b8a03f21638c1227a3c260fe1e370a0b00ce70 Mon Sep 17 00:00:00 2001 From: Vaim Dev Date: Fri, 13 Oct 2023 08:42:56 +0800 Subject: [PATCH 013/104] fix (sso_login): refactor opt_in_type --- graphistry/pygraphistry.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/graphistry/pygraphistry.py b/graphistry/pygraphistry.py index d0c638a6af..f636873d6d 100644 --- a/graphistry/pygraphistry.py +++ b/graphistry/pygraphistry.py @@ -220,7 +220,7 @@ def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SEC if PyGraphistry._config['store_token_creds_in_memory']: PyGraphistry.relogin = lambda: PyGraphistry.sso_login( - org_name, idp_name, sso_timeout + org_name, idp_name, sso_timeout, sso_opt_into_type ) PyGraphistry._is_authenticated = False @@ -244,12 +244,12 @@ def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SEC auth_url = arrow_uploader.sso_auth_url # print("auth_url : {}".format(auth_url)) if auth_url and not PyGraphistry.api_token(): - PyGraphistry._handle_auth_url(auth_url, sso_timeout, opt_into_type) + PyGraphistry._handle_auth_url(auth_url, sso_timeout, sso_opt_into_type) return auth_url @staticmethod - def _handle_auth_url(auth_url, sso_timeout, opt_into_type): + def _handle_auth_url(auth_url, sso_timeout, sso_opt_into_type): """Internal function to handle what to do with the auth_url based on the client mode python/ipython console or notebook. @@ -264,7 +264,7 @@ def _handle_auth_url(auth_url, sso_timeout, opt_into_type): """ - if in_ipython() or in_databricks() or opt_into_type == 'display': # If run in notebook, just display the HTML + if in_ipython() or in_databricks() or sso_opt_into_type == 'display': # If run in notebook, just display the HTML # from IPython.core.display import HTML from IPython.display import display, HTML display(HTML(f'Login SSO')) @@ -272,7 +272,7 @@ def _handle_auth_url(auth_url, sso_timeout, opt_into_type): print(f"If you cannot see the link, please open browser, browse to this link: {auth_url}") print("Please close browser tab after SSO login to back to notebook") # return HTML(make_iframe(auth_url, 20, extra_html=extra_html, override_html_style=override_html_style)) - elif opt_into_type == 'browser': + elif sso_opt_into_type == 'browser': print("Please minimize browser after SSO login to back to pygraphistry") import webbrowser From 0f0c745821d0ec592ee0a57ef6e86ba09be97e50 Mon Sep 17 00:00:00 2001 From: Vaim Dev Date: Fri, 13 Oct 2023 08:48:56 +0800 Subject: [PATCH 014/104] fix (refactor variable): Refactor opt_into_type to sso_opt_into_type --- graphistry/pygraphistry.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/graphistry/pygraphistry.py b/graphistry/pygraphistry.py index cee85ea3fd..58918c36ab 100644 --- a/graphistry/pygraphistry.py +++ b/graphistry/pygraphistry.py @@ -211,8 +211,8 @@ def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SEC :type idp_name: Optional[str] :param sso_timeout: Set sso login getting token timeout in seconds (blocking mode), set to None if non-blocking mode. Default as SSO_GET_TOKEN_ELAPSE_SECONDS. :type sso_timeout: Optional[int] - :param opt_into_type: Show the SSO url with display(), webbrowser.open(), or print() - :type opt_into_type: Optional[Literal["display", "browser"]] + :param sso_opt_into_type: Show the SSO url with display(), webbrowser.open(), or print() + :type sso_opt_into_type: Optional[Literal["display", "browser"]] :returns: token or auth_url :rtype: Optional[str] @@ -259,8 +259,8 @@ def _handle_auth_url(auth_url, sso_timeout, sso_opt_into_type): :type auth_url: str :param sso_timeout: Set sso login getting token timeout in seconds (blocking mode), set to None if non-blocking mode. Default as SSO_GET_TOKEN_ELAPSE_SECONDS. :type sso_timeout: Optional[int] - :param opt_into_type: Show the SSO url with display(), webbrowser.open(), or print() - :type opt_into_type: Optional[Literal["display", "browser"]] + :param sso_opt_into_type: Show the SSO url with display(), webbrowser.open(), or print() + :type sso_opt_into_type: Optional[Literal["display", "browser"]] :returns: token :rtype: token: Optional[str] @@ -710,7 +710,7 @@ def register( PyGraphistry.api_token(token or PyGraphistry._config['api_token']) elif not (org_name is None) or is_sso_login: print(MSG_REGISTER_ENTER_SSO_LOGIN) - PyGraphistry.sso_login(org_name, idp_name, sso_timeout=sso_timeout, opt_into_type=sso_opt_into_type) + PyGraphistry.sso_login(org_name, idp_name, sso_timeout=sso_timeout, sso_opt_into_type=sso_opt_into_type) @staticmethod def __check_login_type_to_reset_token_creds( From 8f540e4b7893ef6039bc8a0741a9b11914501275 Mon Sep 17 00:00:00 2001 From: Vaim Dev Date: Fri, 13 Oct 2023 08:51:01 +0800 Subject: [PATCH 015/104] fix (sso): Change text when got a token --- graphistry/pygraphistry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphistry/pygraphistry.py b/graphistry/pygraphistry.py index 58918c36ab..aa9667fae8 100644 --- a/graphistry/pygraphistry.py +++ b/graphistry/pygraphistry.py @@ -314,7 +314,7 @@ def _handle_auth_url(auth_url, sso_timeout, sso_opt_into_type): # set org_name to sso org PyGraphistry._config['org_name'] = org_name - print("Successfully get a token") + print("Successfully got a token") return PyGraphistry.api_token() else: return None From fd0f0ad2cf8c7d2eb61b60210c59e355f43ed591 Mon Sep 17 00:00:00 2001 From: Vaim Dev Date: Fri, 13 Oct 2023 09:21:45 +0800 Subject: [PATCH 016/104] fix (sso): Add more logging to debug, refactor logging to use lazy logging --- graphistry/arrow_uploader.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/graphistry/arrow_uploader.py b/graphistry/arrow_uploader.py index e9e735be8a..c464604f8e 100644 --- a/graphistry/arrow_uploader.py +++ b/graphistry/arrow_uploader.py @@ -183,12 +183,12 @@ def __init__(self, # check current org_name from .pygraphistry import PyGraphistry if 'org_name' in PyGraphistry._config: - logger.debug("@ArrowUploader.__init__: There is an org_name : {}".format(PyGraphistry._config['org_name'])) + logger.debug("@ArrowUploader.__init__: There is an org_name : %s", PyGraphistry._config['org_name']) self.__org_name = PyGraphistry._config['org_name'] else: self.__org_name = None - logger.debug("2. @ArrowUploader.__init__: After set self.org_name: {}, self.__org_name : {}".format(self.org_name, self.__org_name)) + logger.debug("2. @ArrowUploader.__init__: After set self.org_name: %s, self.__org_name : %s", self.org_name, self.__org_name) def login(self, username, password, org_name=None): @@ -254,7 +254,7 @@ def _handle_login_response(self, out, org_name): del PyGraphistry._config['org_name'] else: if org_name in PyGraphistry._config: - logger.debug("@ArrowUploder, handle login reponse, org_name: {}".format(PyGraphistry._config['org_name'])) + logger.debug("@ArrowUploder, handle login reponse, org_name: %s", PyGraphistry._config['org_name']) PyGraphistry._config['org_name'] = logged_in_org_name # PyGraphistry.org_name(logged_in_org_name) except Exception: @@ -287,17 +287,18 @@ def sso_login(self, org_name=None, idp_name=None): url, data={'client-type': 'pygraphistry'}, verify=self.certificate_validation ) - # print(out.text) + json_response = None try: + logger.debug("@ArrowUploader.sso_login, out.text: %s", out.text) json_response = out.json() - logger.debug("@ArrowUploader.sso_login, json_response: {}".format(json_response)) + logger.debug("@ArrowUploader.sso_login, json_response: %s", json_response) self.token = None if not ('status' in json_response): raise Exception(out.text) else: if json_response['status'] == 'OK': - logger.debug("@ArrowUploader.sso_login, json_data : {}".format(json_response['data'])) + logger.debug("@ArrowUploader.sso_login, json_data : %s", json_response['data']) if 'state' in json_response['data']: self.sso_state = json_response['data']['state'] self.sso_auth_url = json_response['data']['auth_url'] @@ -336,7 +337,7 @@ def sso_get_token(self, state): if 'token' in json_response['data']: self.token = json_response['data']['token'] if 'active_organization' in json_response['data']: - logger.debug("@ArrowUploader.sso_get_token, org_name: {}".format(json_response['data']['active_organization']['slug'])) + logger.debug("@ArrowUploader.sso_get_token, org_name: %s", json_response['data']['active_organization']['slug']) self.org_name = json_response['data']['active_organization']['slug'] except Exception as e: @@ -382,7 +383,7 @@ def create_dataset(self, json): # noqa: F811 tok = self.token if self.org_name: json['org_name'] = self.org_name - logger.debug("@ArrowUploder create_dataset json: {}".format(json)) + logger.debug("@ArrowUploder create_dataset json: %s", json) res = requests.post( self.server_base_path + '/api/v2/upload/datasets/', verify=self.certificate_validation, @@ -490,7 +491,7 @@ def post(self, as_files: bool = True, memoize: bool = True): """ Note: likely want to pair with self.maybe_post_share_link(g) """ - logger.debug("@ArrowUploader.post, self.org_name : {}".format(self.org_name)) + logger.debug("@ArrowUploader.post, self.org_name : %s", self.org_name) if as_files: file_uploader = ArrowFileUploader(self) From 4eb13f402f0cd6f5afe7ce9627e25924b2cee59f Mon Sep 17 00:00:00 2001 From: Vaim Dev Date: Fri, 13 Oct 2023 09:25:07 +0800 Subject: [PATCH 017/104] fix (sso): Add additional message to tell user about the error --- graphistry/arrow_uploader.py | 1 + 1 file changed, 1 insertion(+) diff --git a/graphistry/arrow_uploader.py b/graphistry/arrow_uploader.py index c464604f8e..fa60202fb3 100644 --- a/graphistry/arrow_uploader.py +++ b/graphistry/arrow_uploader.py @@ -309,6 +309,7 @@ def sso_login(self, org_name=None, idp_name=None): except Exception: logger.error('Error: %s', out, exc_info=True) + print("\nThere is error with the sso login, please check your SSO and IDP configuration") raise return self From 222e064871fccbd5f624902e91bff25bad211861 Mon Sep 17 00:00:00 2001 From: lmeyerov Date: Thu, 12 Oct 2023 21:43:12 -0400 Subject: [PATCH 018/104] docs(sso auth): improve messages --- graphistry/arrow_uploader.py | 2 +- graphistry/pygraphistry.py | 15 +++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/graphistry/arrow_uploader.py b/graphistry/arrow_uploader.py index fa60202fb3..111e349e3e 100644 --- a/graphistry/arrow_uploader.py +++ b/graphistry/arrow_uploader.py @@ -309,7 +309,7 @@ def sso_login(self, org_name=None, idp_name=None): except Exception: logger.error('Error: %s', out, exc_info=True) - print("\nThere is error with the sso login, please check your SSO and IDP configuration") + print("\nThere is error with the SSO login, please check your SSO and IDP configuration") raise return self diff --git a/graphistry/pygraphistry.py b/graphistry/pygraphistry.py index aa9667fae8..f4d00a1b23 100644 --- a/graphistry/pygraphistry.py +++ b/graphistry/pygraphistry.py @@ -211,7 +211,7 @@ def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SEC :type idp_name: Optional[str] :param sso_timeout: Set sso login getting token timeout in seconds (blocking mode), set to None if non-blocking mode. Default as SSO_GET_TOKEN_ELAPSE_SECONDS. :type sso_timeout: Optional[int] - :param sso_opt_into_type: Show the SSO url with display(), webbrowser.open(), or print() + :param sso_opt_into_type: Show the SSO URL with display(), webbrowser.open(), or print() :type sso_opt_into_type: Optional[Literal["display", "browser"]] :returns: token or auth_url :rtype: Optional[str] @@ -247,7 +247,6 @@ def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SEC # print("auth_url : {}".format(auth_url)) if auth_url and not PyGraphistry.api_token(): PyGraphistry._handle_auth_url(auth_url, sso_timeout, sso_opt_into_type) - return auth_url @staticmethod @@ -272,20 +271,20 @@ def _handle_auth_url(auth_url, sso_timeout, sso_opt_into_type): # from IPython.core.display import HTML from IPython.display import display, HTML display(HTML(f'Login SSO')) - print("Please click the above link to open browser to login") - print(f"If you cannot see the link, please open browser, browse to this link: {auth_url}") + print("Please click the above URL to open browser to login") + print(f"If you cannot see the URL, please open browser, browse to this URL: {auth_url}") print("Please close browser tab after SSO login to back to notebook") # return HTML(make_iframe(auth_url, 20, extra_html=extra_html, override_html_style=override_html_style)) elif sso_opt_into_type == 'browser': - print("Please minimize browser after SSO login to back to pygraphistry") + print("Please minimize browser after your SSO login and go back to pygraphistry") import webbrowser input("Press Enter to open browser ...") # open browser to auth_url webbrowser.open(auth_url) else: - print(f"Please open browser, browse to this link: {auth_url}") - print("Please run graphistry.sso_get_token() to complete the authentication if you get timeout error") + print(f"Please open a browser, browse to this URL, and sign in: {auth_url}") + print("After, if you get timeout error, run graphistry.sso_get_token() to complete the authentication") if sso_timeout is not None: time.sleep(1) @@ -314,7 +313,7 @@ def _handle_auth_url(auth_url, sso_timeout, sso_opt_into_type): # set org_name to sso org PyGraphistry._config['org_name'] = org_name - print("Successfully got a token") + print("Successfully logged in") return PyGraphistry.api_token() else: return None From 812a61f4693e8c69bc544ba4a45b633415f91d1b Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Thu, 12 Oct 2023 21:04:20 -0700 Subject: [PATCH 019/104] docs(changelog) --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 173231098e..08ee62447d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +## [0.29.6 - 2023-10-23] + ### Docs * Memgraph: Add tutorial (https://github.com/graphistry/pygraphistry/pull/507 by https://github.com/karmenrabar) @@ -15,6 +17,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm * Guard against potential `requests`` null dereference in uploader error handling +### Security + +* Add control `register(..., sso_opt_into_type='browser' | 'display' | None)` +* Fix display of SSO URL + ## [0.29.5 - 2023-08-23] ### Fixed From 3bdf4387f184bdc380288ad0377a81c971484821 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sat, 28 Oct 2023 07:21:17 -0400 Subject: [PATCH 020/104] fix(ci) --- CHANGELOG.md | 5 +++++ graphistry/arrow_uploader.py | 2 +- graphistry/tests/test_hyper_dask.py | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 08ee62447d..70591ce6df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +### Fixed + +* Type error in arrow uploader exception handler +* Parsing error in hypergraph dask tests + ## [0.29.6 - 2023-10-23] ### Docs diff --git a/graphistry/arrow_uploader.py b/graphistry/arrow_uploader.py index 111e349e3e..a96b17c5b3 100644 --- a/graphistry/arrow_uploader.py +++ b/graphistry/arrow_uploader.py @@ -660,7 +660,7 @@ def post_arrow(self, arr: pa.Table, graph_type: str, opts: str = ''): except requests.exceptions.HTTPError as e: logger.error('Failed to post arrow to %s (%s)', sub_path, "{}/{}{}".format(self.server_base_path, sub_path, f"?{opts}" if len(opts) > 0 else ""), exc_info=True) logger.error('%s', e) - logger.error('%s', e.response.text) + logger.error('%s', e.response.text if e.response else None) raise e except Exception as e: logger.error('Failed to post arrow to %s', sub_path, exc_info=True) diff --git a/graphistry/tests/test_hyper_dask.py b/graphistry/tests/test_hyper_dask.py index 9cd1abc664..54e256d74b 100644 --- a/graphistry/tests/test_hyper_dask.py +++ b/graphistry/tests/test_hyper_dask.py @@ -71,7 +71,7 @@ def honeypot_pdf() -> pd.DataFrame: #'graphistry/tests/data/honeypot.csv', dtype=base_csv_dtypes, parse_dates=["time(max)", "time(min)"], - date_parser=lambda v: pd.to_datetime(int(v)), + date_parser=lambda v: pd.to_datetime(int(float(v))), ) assert df.dtypes.to_dict() == base_dtypes assert len(df) == HONEYPOT_ROWS From 819c2bd8594650eed15f3c153d7afbc1aa54985d Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sat, 28 Oct 2023 08:22:10 -0400 Subject: [PATCH 021/104] fix(igraph): ensure arrow friendly by default --- CHANGELOG.md | 11 ++++++++++- graphistry/PlotterBase.py | 4 ++-- graphistry/plugins/igraph.py | 16 ++++++++++++---- graphistry/tests/plugins/test_igraph.py | 16 ++++++++++++++-- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70591ce6df..fd34ec256d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,19 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +### Added + +* igraph: support `compute_igraph('community_optimal_modularity')` + ### Fixed * Type error in arrow uploader exception handler -* Parsing error in hypergraph dask tests +* igraph: default coerce Graph-type node labels to strings, enabling plotting of g.compute_igraph('k_core') + +### Infra + +* dask: Fixed parsing error in hypergraph dask tests +* igraph: Ensure in compute_igraph tests that default mode results coerce to arrow tables ## [0.29.6 - 2023-10-23] diff --git a/graphistry/PlotterBase.py b/graphistry/PlotterBase.py index 0af2641d2c..50833f4741 100644 --- a/graphistry/PlotterBase.py +++ b/graphistry/PlotterBase.py @@ -1462,9 +1462,9 @@ def to_igraph(self, def compute_igraph(self, - alg: str, out_col: Optional[str] = None, directed: Optional[bool] = None, use_vids: bool = False, params: dict = {} + alg: str, out_col: Optional[str] = None, directed: Optional[bool] = None, use_vids: bool = False, params: dict = {}, stringify_rich_types: bool = True ): - return compute_igraph_base(self, alg, out_col, directed, use_vids, params) + return compute_igraph_base(self, alg, out_col, directed, use_vids, params, stringify_rich_types) compute_igraph.__doc__ = compute_igraph_base.__doc__ diff --git a/graphistry/plugins/igraph.py b/graphistry/plugins/igraph.py index b7bdc0d405..d865c0f1e8 100644 --- a/graphistry/plugins/igraph.py +++ b/graphistry/plugins/igraph.py @@ -288,7 +288,8 @@ def compute_igraph( out_col: Optional[str] = None, directed: Optional[bool] = None, use_vids=False, - params: dict = {} + params: dict = {}, + stringify_rich_types=True ) -> Plottable: """Enrich or replace graph using igraph methods @@ -307,6 +308,9 @@ def compute_igraph( :param params: Any named parameters to pass to the underlying igraph method :type params: dict + :param stringify_rich_types: When rich types like igraph.Graph are returned, which may be problematic for downstream rendering, coerce them to strings + :type stringify_rich_types: bool + :returns: Plotter :rtype: Plotter @@ -374,10 +378,14 @@ def compute_igraph( return from_igraph(self, out) elif isinstance(out, list) and self._nodes is None: raise ValueError("No g._nodes table found; use .bind(), .nodes(), .materialize_nodes()") - elif len(out) == len(self._nodes): - clustering = out + elif isinstance(out, list) and len(out) == len(self._nodes): + if stringify_rich_types and len(out) > 0 and all((isinstance(c, igraph.Graph) for c in out)): + #ex: k_core + clustering = [str(c) for c in out] + else: + clustering = out else: - raise RuntimeError(f'Unexpected output type "{type(out)}"; should be VertexClustering, VertexDendrogram, Graph, or list_<|V|>') + raise RuntimeError(f'Unexpected output type "{type(out)}"; should be VertexClustering, VertexDendrogram, Graph, or list_<|V|>') ig.vs[out_col] = clustering diff --git a/graphistry/tests/plugins/test_igraph.py b/graphistry/tests/plugins/test_igraph.py index 94c2c282db..89617d02bf 100644 --- a/graphistry/tests/plugins/test_igraph.py +++ b/graphistry/tests/plugins/test_igraph.py @@ -1,3 +1,5 @@ +import pyarrow as pa + import graphistry, logging, pandas as pd, pytest, warnings from graphistry.tests.common import NoAuthTestCase from graphistry.constants import SRC, DST, NODE @@ -508,13 +510,23 @@ def test_all_calls(self): with warnings.catch_warnings(record=True) as w: # Cause all warnings to always be triggered. warnings.simplefilter("always") - assert compute_igraph(g, alg, **opts) is not None + g2 = compute_igraph(g, alg, **opts) + assert g2 is not None + assert g2._nodes is not None + assert g2._edges is not None + pa.Table.from_pandas(g2._nodes) + pa.Table.from_pandas(g2._edges) #assert len(w) == 1 assert issubclass(w[-1].category, DeprecationWarning) else: with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) - assert compute_igraph(g, alg, **opts) is not None + g2 = compute_igraph(g, alg, **opts) + assert g2 is not None + assert g2._nodes is not None + assert g2._edges is not None + pa.Table.from_pandas(g2._nodes) + pa.Table.from_pandas(g2._edges) @pytest.mark.skipif(not has_igraph, reason="Requires igraph") class Test_igraph_layouts(NoAuthTestCase): From 55aca238e8eafb9cd6bac96c1810b48214979735 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sat, 28 Oct 2023 08:22:22 -0400 Subject: [PATCH 022/104] feat(igraph): expose community_optimal_modularity --- graphistry/plugins/igraph.py | 1 + 1 file changed, 1 insertion(+) diff --git a/graphistry/plugins/igraph.py b/graphistry/plugins/igraph.py index d865c0f1e8..7639d1ca74 100644 --- a/graphistry/plugins/igraph.py +++ b/graphistry/plugins/igraph.py @@ -267,6 +267,7 @@ def to_igraph( 'community_leading_eigenvector', 'community_leiden', 'community_multilevel', + 'community_optimal_modularity', 'community_spinglass', 'community_walktrap', 'constraint', From 4f78c5c1d016f04b53f1f25ae2a2d0451cb3fff8 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sat, 28 Oct 2023 08:47:56 -0400 Subject: [PATCH 023/104] feat(igraph): add articulation_points --- CHANGELOG.md | 1 + graphistry/plugins/igraph.py | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd34ec256d..e1f7272f60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added * igraph: support `compute_igraph('community_optimal_modularity')` +* igraph: `compute_igraph('articulation_points')` labels nodes that are articulation points ### Fixed diff --git a/graphistry/plugins/igraph.py b/graphistry/plugins/igraph.py index 7639d1ca74..e69da424d0 100644 --- a/graphistry/plugins/igraph.py +++ b/graphistry/plugins/igraph.py @@ -250,6 +250,7 @@ def to_igraph( compute_algs = [ + 'articulation_points', 'authority_score', 'betweenness', 'bibcoupling', @@ -379,6 +380,12 @@ def compute_igraph( return from_igraph(self, out) elif isinstance(out, list) and self._nodes is None: raise ValueError("No g._nodes table found; use .bind(), .nodes(), .materialize_nodes()") + elif alg == 'articulation_points': + assert isinstance(out, list) # List[int] + membership = [0] * len(ig.vs) + for i in out: + membership[i] = 1 + clustering = membership elif isinstance(out, list) and len(out) == len(self._nodes): if stringify_rich_types and len(out) > 0 and all((isinstance(c, igraph.Graph) for c in out)): #ex: k_core @@ -386,7 +393,12 @@ def compute_igraph( else: clustering = out else: - raise RuntimeError(f'Unexpected output type "{type(out)}"; should be VertexClustering, VertexDendrogram, Graph, or list_<|V|>') + if isinstance(out, list) and len(out) > 0: + xtra = f" (element 0 type: {type(out[0])})" + else: + xtra = "" + + raise RuntimeError(f'Unexpected output type "{type(out)}"{xtra}; should be VertexClustering, VertexDendrogram, Graph, or list_<|V|>') ig.vs[out_col] = clustering From 0441ab64cb770d8bac11c83fcc8d2033f0264539 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Mon, 30 Oct 2023 10:11:21 -0400 Subject: [PATCH 024/104] test(igraph): chaining --- CHANGELOG.md | 3 ++- graphistry/tests/plugins/test_igraph.py | 29 +++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e1f7272f60..af40dbc0a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Infra * dask: Fixed parsing error in hypergraph dask tests -* igraph: Ensure in compute_igraph tests that default mode results coerce to arrow tables +* igraph: Ensure in compute_igraph tests that default mode results coerce to arrow tables +* igraph: Test chaining ## [0.29.6 - 2023-10-23] diff --git a/graphistry/tests/plugins/test_igraph.py b/graphistry/tests/plugins/test_igraph.py index 89617d02bf..377cefc300 100644 --- a/graphistry/tests/plugins/test_igraph.py +++ b/graphistry/tests/plugins/test_igraph.py @@ -478,6 +478,35 @@ def test_enrich_with_stat_direct(self): @pytest.mark.skipif(not has_igraph, reason="Requires igraph") class Test_igraph_compute(NoAuthTestCase): + def chain_1_rename(self, alg: str) -> None: + + g = graphistry.edges(edges3_df, 'a', 'b').materialize_nodes() + + g2 = compute_igraph(g, alg) + assert alg in g2._nodes + + g3 = compute_igraph(g2, alg, f'{alg}2') + assert f'{alg}2' in g3._nodes + assert g2._nodes[alg].equals(g3._nodes[alg]) + assert g2._nodes[alg].equals(g3._nodes[f'{alg}2']) + + g3b = compute_igraph(g2, alg) + assert alg in g3b._nodes + assert g3b._nodes.shape == g2._nodes.shape + + def test_chain_1_rename_pagerank(self): + self.chain_1_rename('pagerank') + + def test_chain_2_rename_articulation_points(self): + self.chain_1_rename('articulation_points') + + def test_chain_3_seq(self): + g = graphistry.edges(edges3_df, 'a', 'b').materialize_nodes() + g2 = compute_igraph(g, 'pagerank') + g3 = compute_igraph(g2, 'articulation_points') + assert 'pagerank' in g3._nodes + assert 'articulation_points' in g3._nodes + def test_all_calls(self): overrides = { 'bipartite_projection': { From c4521bc4d25a0a8ff5ecc9e06a0689f6082f4488 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Wed, 1 Nov 2023 09:09:09 -0400 Subject: [PATCH 025/104] harden(igraph): warn on bad invalid coercion calls --- graphistry/plugins/igraph.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/graphistry/plugins/igraph.py b/graphistry/plugins/igraph.py index e69da424d0..09538f6fab 100644 --- a/graphistry/plugins/igraph.py +++ b/graphistry/plugins/igraph.py @@ -105,6 +105,8 @@ def from_igraph(self, nodes_df = nodes_df[ node_attributes ] if g._nodes is not None and merge_if_existing: + if g._node is None: + raise ValueError('Non-None g._nodes and merge_if_existing=True, yet no g._node is defined') if len(g._nodes) != len(nodes_df): logger.warning('node tables do not match in length; switch merge_if_existing to False or load_nodes to False or add missing nodes') From 36ef22de0544041531c73f2f041abe12d4615f42 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Wed, 1 Nov 2023 09:13:24 -0400 Subject: [PATCH 026/104] fix(igraph): smarter integer index handling --- CHANGELOG.md | 1 + graphistry/plugins/igraph.py | 15 +- graphistry/tests/plugins/test_igraph.py | 174 ++++++++++++++++++++++++ 3 files changed, 189 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af40dbc0a4..2425ce9bf4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm * Type error in arrow uploader exception handler * igraph: default coerce Graph-type node labels to strings, enabling plotting of g.compute_igraph('k_core') +* igraph: fix coercions when using numeric IDs that were confused by igraph swizzling ### Infra diff --git a/graphistry/plugins/igraph.py b/graphistry/plugins/igraph.py index 09538f6fab..f70b96f6c4 100644 --- a/graphistry/plugins/igraph.py +++ b/graphistry/plugins/igraph.py @@ -128,8 +128,21 @@ def from_igraph(self, node_id_col = None elif node_col in ig_vs_df: node_id_col = node_col + #User to_igraph() with numeric IDs may swizzle id mappings (ex: sparse numeric) so try to un-swizzle + #FIXME: how to handle dense edge case's swizzling? elif g._node is not None and g._nodes[g._node].dtype.name == ig_vs_df.reset_index()['vertex ID'].dtype.name: - node_id_col = None + found = False + #FIXME: This seems quite error prone... what if any fields already exist? + for c in ['name', 'id', 'idx', NODE]: + if c in ig_vs_df.columns: + if g._nodes[g._node].min() == ig_vs_df[c].min() and g._nodes[g._node].max() == ig_vs_df[c].max(): + if g._nodes[g._node].sort_values().equals(ig_vs_df[c].sort_values()): + node_id_col = c + found = True + break + if not found: + logger.debug('lacks matching sortable dimension, likely passed integers-as-vids, continue without remapping') + node_id_col = None elif 'name' in ig_vs_df: node_id_col = 'name' else: diff --git a/graphistry/tests/plugins/test_igraph.py b/graphistry/tests/plugins/test_igraph.py index 377cefc300..d6d191caaf 100644 --- a/graphistry/tests/plugins/test_igraph.py +++ b/graphistry/tests/plugins/test_igraph.py @@ -21,6 +21,14 @@ edges = [(0, 1), (0, 2), (0, 3), (1, 2), (2, 4)] names = ["my", "list", "of", "five", "edges"] +edges_sparse = [(2, 3), (3, 4), (6, 2)] +edges_sparse_renamed = [(0, 1), (1, 2), (3, 0)] +names_sparse = ['ab', 'bc', 'da'] +nodes_sparse = [2, 3, 4, 6] +nodes_sparse_renamed = [0, 1, 2, 3] +names_sparse_v = ['a', 'b', 'c', 'd'] +names_dense_v = ['u0', 'u1', 'a', 'b', 'c', 'u5', 'd'] + nodes = [0, 1, 2, 3, 4] names_v = ["eggs", "spam", "ham", "bacon", "yello"] @@ -47,6 +55,14 @@ 't': [0, 1, 0, 1] }) +edges4_df = pd.DataFrame({ + #no 0 + 's': [5, 6, 2, 5, 4, 2, 9, 4, 7, 10], + 'd': [10, 1, 8, 7, 10, 10, 3, 10, 1, 3], + 'w': [5.58851127, 9.12320228, 4.58717668, 6.59665844, 8.62772521, + 2.48654683, 1.4533045 , 4.47252362, 3.38562727, 9.16188751] +}) + @pytest.mark.skipif(not has_igraph, reason="Requires igraph") class Test_from_igraph(NoAuthTestCase): @@ -59,6 +75,15 @@ def test_minimal_edges(self): assert len(g._edges[g._source].dropna()) == len(edges) assert len(g._edges[g._destination].dropna()) == len(edges) + def test_minimal_edges_sparse(self): + ig = igraph.Graph(edges_sparse) + g = graphistry.from_igraph(ig, load_nodes=False) + assert g._nodes is None and g._node is None + assert len(g._edges) == len(edges_sparse) + assert g._source is not None and g._destination is not None + assert len(g._edges[g._source].dropna()) == len(edges_sparse) + assert len(g._edges[g._destination].dropna()) == len(edges_sparse) + def test_minimal_attributed_edges(self): ig = igraph.Graph(edges) ig.es["name"] = names @@ -70,6 +95,17 @@ def test_minimal_attributed_edges(self): assert len(g._edges[g._destination].dropna()) == len(edges) assert (g._edges['name'] == pd.Series(names)).all() + def test_minimal_attributed_edges_sparse(self): + ig = igraph.Graph(edges_sparse) + ig.es["name"] = names_sparse + g = graphistry.from_igraph(ig, load_nodes=False) + assert g._nodes is None and g._node is None + assert len(g._edges) == len(edges_sparse) + assert g._source is not None and g._destination is not None + assert len(g._edges[g._source].dropna()) == len(edges_sparse) + assert len(g._edges[g._destination].dropna()) == len(edges_sparse) + assert (g._edges['name'] == pd.Series(names_sparse)).all() + def test_minimal_nodes(self): ig = igraph.Graph(edges) g = graphistry.from_igraph(ig) @@ -82,6 +118,19 @@ def test_minimal_nodes(self): assert len(g._edges[g._source].dropna()) == len(edges) assert len(g._edges[g._destination].dropna()) == len(edges) + def test_minimal_nodes_sparse(self): + ig = igraph.Graph(edges_sparse) + g = graphistry.from_igraph(ig) + assert g._node is not None and g._nodes is not None + assert len(g._nodes) == max(nodes_sparse) + 1 + assert len(g._nodes) == len(names_dense_v) + assert g._nodes[g._node].sort_values().to_list() == list(range(max(nodes_sparse) + 1)) + assert g._nodes.columns == [ g._node ] + assert len(g._edges) == len(edges_sparse) + assert g._source is not None and g._destination is not None + assert len(g._edges[g._source].dropna()) == len(edges_sparse) + assert len(g._edges[g._destination].dropna()) == len(edges_sparse) + def test_minimal_nodes_attributed(self): ig = igraph.Graph(edges) ig.vs["name"] = names_v @@ -96,6 +145,21 @@ def test_minimal_nodes_attributed(self): assert len(g._edges[g._source].dropna()) == len(edges) assert len(g._edges[g._destination].dropna()) == len(edges) + def test_minimal_nodes_attributed_sparse(self): + ig = igraph.Graph(edges_sparse) + ig.vs["name"] = names_dense_v + g = graphistry.from_igraph(ig) + assert g._node is not None and g._nodes is not None + assert g._node == NODE + assert len(g._nodes) == max(nodes_sparse) + 1 + assert sorted(g._nodes.columns) == sorted([ NODE ]) + assert len(g._nodes) == len(names_dense_v) + assert g._nodes[g._node].sort_values().to_list() == sorted(names_dense_v) + assert len(g._edges) == len(edges_sparse) + assert g._source is not None and g._destination is not None + assert len(g._edges[g._source].dropna()) == len(edges_sparse) + assert len(g._edges[g._destination].dropna()) == len(edges_sparse) + def test_merge_existing_nodes(self): ig = igraph.Graph(edges) ig.vs["idx"] = ['a', 'b', 'c', 'd', 'e'] @@ -236,6 +300,104 @@ def test_minimal_edges(self): })) assert g2._node == NODE + def test_sparse_edges_renamed(self): + g = graphistry.edges(pd.DataFrame([{'s': s, 'd': d} for (s, d) in edges_sparse]), 's', 'd') + ig = g.to_igraph() + logger.debug('ig: %s', ig) + g2 = graphistry.from_igraph(ig) + assert g2._edges.shape == g._edges.shape + assert g2._source == SRC_IGRAPH + assert g2._destination == DST_IGRAPH + assert g2._edge is None + logger.debug('g2._nodes: %s', g2._nodes) + assert sorted(g2._nodes[g2._node].to_list()) == sorted(nodes_sparse) + assert g2._node == NODE + + def test_swizzles_1_none(self): + g = graphistry.edges(pd.DataFrame({'s': ['a', 'b'], 'd': ['b', 'a'], 'v': ['aa', 'bb']}), 's', 'd') + ig = g.to_igraph() + g2 = g.from_igraph(ig) + assert g2._edges.equals(g._edges) + + gb = g.nodes(pd.DataFrame({'n': ['a', 'b'], 'v': ['aa', 'bb']}), 'n') + ig = gb.to_igraph() + gb2 = gb.from_igraph(ig) + assert gb2._nodes.equals(gb._nodes) + + gc = g.nodes(pd.DataFrame({'n': ['b', 'a'], 'v': ['bb', 'aa']}), 'n') + ig = gc.to_igraph() + gc2 = gc.from_igraph(ig) + assert gc2._nodes.equals(gc._nodes) + + gd = g.materialize_nodes() + ig = gd.to_igraph() + gd2 = gd.from_igraph(ig) + assert gd2._nodes.equals(gd._nodes) + + def test_swizzles_1_none_numeric(self): + g = graphistry.edges(pd.DataFrame({'s': [0, 1], 'd': [0, 1], 'v': ['aa', 'bb']}), 's', 'd') + ig = g.to_igraph() + g2 = g.from_igraph(ig) + assert g2._edges.equals(g._edges) + + gb = g.nodes(pd.DataFrame({'n': [0, 1], 'v': ['aa', 'bb']}), 'n') + ig = gb.to_igraph() + gb2 = gb.from_igraph(ig) + assert gb2._nodes.equals(gb._nodes) + + gc = g.nodes(pd.DataFrame({'n': [1, 0], 'v': ['bb', 'aa']}), 'n') + ig = gc.to_igraph() + gc2 = gc.from_igraph(ig) + assert gc2._nodes.equals(gc._nodes) + + gd = g.materialize_nodes() + ig = gd.to_igraph() + gd2 = gd.from_igraph(ig) + assert gd2._nodes.equals(gd._nodes) + + def test_swizzles_2_sparse(self): + g = graphistry.edges(pd.DataFrame({'s': [1, 2], 'd': [1, 2], 'v': ['11', '22']}), 's', 'd') + ig = g.to_igraph() + g2 = g.from_igraph(ig) + assert g2._edges.equals(g._edges) + + gb = g.nodes(pd.DataFrame({'n': [1, 2], 'v': ['11', '22']}), 'n') + ig = gb.to_igraph() + gb2 = gb.from_igraph(ig) + assert gb2._nodes.equals(gb._nodes) + + gc = g.nodes(pd.DataFrame({'n': [2, 1], 'v': ['22', '11']}), 'n') + ig = gc.to_igraph() + gc2 = gc.from_igraph(ig) + assert gc2._nodes.equals(gc._nodes) + + gd = g.materialize_nodes() + ig = gd.to_igraph() + gd2 = gd.from_igraph(ig) + assert gd2._nodes.equals(gd._nodes) + + def test_swizzles_2_dense(self): + g = graphistry.edges(pd.DataFrame({'s': [1, 0], 'd': [1, 0], 'v': ['11', '00']}), 's', 'd') + ig = g.to_igraph() + g2 = g.from_igraph(ig) + assert g2._edges.equals(g._edges) + + gb = g.nodes(pd.DataFrame({'n': [1, 0], 'v': ['11', '00']}), 'n') + ig = gb.to_igraph() + gb2 = gb.from_igraph(ig) + assert gb2._nodes.equals(gb._nodes) + + gc = g.nodes(pd.DataFrame({'n': [0, 1], 'v': ['00', '11']}), 'n') + ig = gc.to_igraph() + gc2 = gc.from_igraph(ig) + assert gc2._nodes.equals(gc._nodes) + + gd = g.materialize_nodes() + ig = gd.to_igraph() + gd2 = gd.from_igraph(ig) + assert gd2._nodes.equals(gd._nodes) + + def test_minimal_edges_renamed(self): g = (graphistry .edges(pd.DataFrame({ @@ -507,6 +669,18 @@ def test_chain_3_seq(self): assert 'pagerank' in g3._nodes assert 'articulation_points' in g3._nodes + def test_chain_4_sparse(self): + + #From https://github.com/graphistry/pygraphistry/pull/513#issuecomment-1784161313 + + g = graphistry.edges(edges4_df, 's', 'd').materialize_nodes() + g2 = g.compute_igraph('articulation_points') + assert 'articulation_points' in g2._nodes + g2b = g.compute_igraph('community_optimal_modularity') + assert 'community_optimal_modularity' in g2b._nodes + g3 = g2.compute_igraph('community_optimal_modularity') + assert g3._nodes.community_optimal_modularity.equals(g2b._nodes.community_optimal_modularity) + def test_all_calls(self): overrides = { 'bipartite_projection': { From 61f497dbfd58ae4b6d65c482bdc795ce042fad02 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Wed, 1 Nov 2023 09:14:56 -0400 Subject: [PATCH 027/104] infra(tests): mount source folder --- CHANGELOG.md | 1 + docker/test-cpu-local.sh | 1 + docker/test-gpu-local.sh | 1 + 3 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2425ce9bf4..248f45f117 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm * dask: Fixed parsing error in hypergraph dask tests * igraph: Ensure in compute_igraph tests that default mode results coerce to arrow tables * igraph: Test chaining +* tests: mount source folders to enable dev iterations without rebuilding ## [0.29.6 - 2023-10-23] diff --git a/docker/test-cpu-local.sh b/docker/test-cpu-local.sh index 8925048a62..9d7392605d 100755 --- a/docker/test-cpu-local.sh +++ b/docker/test-cpu-local.sh @@ -46,6 +46,7 @@ docker run \ -e WITH_TYPECHECK=$WITH_TYPECHECK \ -e WITH_BUILD=$WITH_BUILD \ -e WITH_TEST=$WITH_TEST \ + -v "`pwd`/../graphistry:/opt/pygraphistry/graphistry:ro" \ --rm \ ${NETWORK} \ graphistry/test-cpu:${TEST_CPU_VERSION} \ diff --git a/docker/test-gpu-local.sh b/docker/test-gpu-local.sh index d481054c47..d0d239d023 100755 --- a/docker/test-gpu-local.sh +++ b/docker/test-gpu-local.sh @@ -39,6 +39,7 @@ docker run \ -e WITH_TYPECHECK=$WITH_TYPECHECK \ -e WITH_TEST=$WITH_TEST \ -e WITH_BUILD=$WITH_BUILD \ + -v "`pwd`/../graphistry:/opt/pygraphistry/graphistry:ro" \ --security-opt seccomp=unconfined \ --rm \ ${NETWORK} \ From 11e838dd38cb4e2d6f48ebdb00cdacb296166249 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Thu, 2 Nov 2023 11:04:55 -0400 Subject: [PATCH 028/104] docs(changelog): 0.29.7 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 248f45f117..396671f961 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +## [0.29.7 - 2023-11-02] + ### Added * igraph: support `compute_igraph('community_optimal_modularity')` From ee2de1d0c965c16b774cfde1c60643af26f16b3f Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Fri, 17 Nov 2023 14:51:27 -0600 Subject: [PATCH 029/104] fix: element_id not defined in Neptune bolt python results --- graphistry/bolt_util.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/graphistry/bolt_util.py b/graphistry/bolt_util.py index 361ae401aa..4e1dd460b5 100644 --- a/graphistry/bolt_util.py +++ b/graphistry/bolt_util.py @@ -34,8 +34,8 @@ def bolt_graph_to_edges_dataframe(graph): { relationship_id_key: relationship.element_id, # noqa: E241 relationship_type_key: relationship.type, # noqa: E241 - start_node_id_key: relationship.start_node.element_id, # noqa: E241 - end_node_id_key: relationship.end_node.element_id # noqa: E241 + start_node_id_key: relationship.start_node.element_id if 'element_id' in relationship.start_node else relationship.start_node.id, # noqa: E241 + end_node_id_key: relationship.end_node.element_id if 'element_id' in relationship.end_node else relationship.end_node.id, # noqa: E241 } ) for relationship in graph.relationships @@ -56,9 +56,9 @@ def bolt_graph_to_nodes_dataframe(graph) -> pd.DataFrame: util.merge_two_dicts( { key: value for (key, value) in node.items() }, util.merge_two_dicts( - { - node_id_key: node.element_id, - node_type_key: ",".join(sorted([str(label) for label in node.labels])) + { + node_id_key: node.element_id, + node_type_key: ",".join(sorted([str(label) for label in node.labels])) }, { node_label_prefix_key + str(label): True for label in node.labels })) for node in graph.nodes @@ -171,12 +171,12 @@ def flatten_spatial(df : pd.DataFrame, col) -> pd.DataFrame: all_t0 = (with_vals.apply(lambda s: s.__class__) == t0.__class__).all() # type: ignore except: all_t0 = False - + if all_t0: out_df = flatten_spatial_col(df, col) else: out_df[col] = df[col].apply(stringify_spatial) - + return out_df From c60448a24669280a59e6ef992df38b1aa45185e3 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Fri, 17 Nov 2023 16:59:49 -0600 Subject: [PATCH 030/104] fix: use id if element_id is missing from Neptune --- graphistry/bolt_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphistry/bolt_util.py b/graphistry/bolt_util.py index 4e1dd460b5..6d138fa491 100644 --- a/graphistry/bolt_util.py +++ b/graphistry/bolt_util.py @@ -32,7 +32,7 @@ def bolt_graph_to_edges_dataframe(graph): util.merge_two_dicts( { key: value for (key, value) in relationship.items() }, { - relationship_id_key: relationship.element_id, # noqa: E241 + relationship_id_key: relationship.element_id if 'element_id' in relationship else relationship.id, # noqa: E241 relationship_type_key: relationship.type, # noqa: E241 start_node_id_key: relationship.start_node.element_id if 'element_id' in relationship.start_node else relationship.start_node.id, # noqa: E241 end_node_id_key: relationship.end_node.element_id if 'element_id' in relationship.end_node else relationship.end_node.id, # noqa: E241 From a09c865e420e1659cddc9b408c077528897bbff1 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Fri, 17 Nov 2023 17:30:11 -0600 Subject: [PATCH 031/104] PR review: use hasattr move conditional check outside of loop --- graphistry/bolt_util.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/graphistry/bolt_util.py b/graphistry/bolt_util.py index 6d138fa491..a201631902 100644 --- a/graphistry/bolt_util.py +++ b/graphistry/bolt_util.py @@ -28,15 +28,28 @@ def to_bolt_driver(driver=None): #TODO catch additional encodings def bolt_graph_to_edges_dataframe(graph): + + for relationship in graph.relationships: + if hasattr(relationship, 'element_id'): + map_dict = { + relationship_id_key: relationship.element_id, # noqa: E241 + relationship_type_key: relationship.type, # noqa: E241 + start_node_id_key: relationship.start_node.element_id, # noqa: E241 + end_node_id_key: relationship.end_node.element_id, # noqa: E241 + } + else: + map_dict = { + relationship_id_key: relationship.id, # noqa: E241 + relationship_type_key: relationship.type, # noqa: E241 + start_node_id_key: relationship.start_node.id, # noqa: E241 + end_node_id_key: relationship.end_node.id, # noqa: E241 + } + break + df = pd.DataFrame([ util.merge_two_dicts( { key: value for (key, value) in relationship.items() }, - { - relationship_id_key: relationship.element_id if 'element_id' in relationship else relationship.id, # noqa: E241 - relationship_type_key: relationship.type, # noqa: E241 - start_node_id_key: relationship.start_node.element_id if 'element_id' in relationship.start_node else relationship.start_node.id, # noqa: E241 - end_node_id_key: relationship.end_node.element_id if 'element_id' in relationship.end_node else relationship.end_node.id, # noqa: E241 - } + map_dict ) for relationship in graph.relationships ]) From 01efc6a47fa99ae2541f5cd6d69e2a278825d0ca Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Fri, 17 Nov 2023 17:40:28 -0600 Subject: [PATCH 032/104] fix: neptune: check if element_id exists, if not, use id --- graphistry/bolt_util.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/graphistry/bolt_util.py b/graphistry/bolt_util.py index a201631902..2542979744 100644 --- a/graphistry/bolt_util.py +++ b/graphistry/bolt_util.py @@ -65,12 +65,19 @@ def bolt_graph_to_edges_dataframe(graph): def bolt_graph_to_nodes_dataframe(graph) -> pd.DataFrame: + for node in graph.nodes: + if hasattr(node, 'element_id'): + map_id_col = node.element_id + else: + map_id_col = node.id + break + df = pd.DataFrame([ util.merge_two_dicts( { key: value for (key, value) in node.items() }, util.merge_two_dicts( { - node_id_key: node.element_id, + node_id_key: map_id_col, node_type_key: ",".join(sorted([str(label) for label in node.labels])) }, { node_label_prefix_key + str(label): True for label in node.labels })) From b86bab4e9bf13869eb550b41e787194bc9f07db5 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Sat, 18 Nov 2023 17:19:03 -0600 Subject: [PATCH 033/104] fix: break conditional out of node and edge loops --- graphistry/bolt_util.py | 90 ++++++++++++++++++++++++++--------------- 1 file changed, 58 insertions(+), 32 deletions(-) diff --git a/graphistry/bolt_util.py b/graphistry/bolt_util.py index 2542979744..d80b88d9ec 100644 --- a/graphistry/bolt_util.py +++ b/graphistry/bolt_util.py @@ -29,31 +29,40 @@ def to_bolt_driver(driver=None): #TODO catch additional encodings def bolt_graph_to_edges_dataframe(graph): + is_neptune=False + for relationship in graph.relationships: - if hasattr(relationship, 'element_id'): - map_dict = { - relationship_id_key: relationship.element_id, # noqa: E241 - relationship_type_key: relationship.type, # noqa: E241 - start_node_id_key: relationship.start_node.element_id, # noqa: E241 - end_node_id_key: relationship.end_node.element_id, # noqa: E241 - } - else: - map_dict = { - relationship_id_key: relationship.id, # noqa: E241 - relationship_type_key: relationship.type, # noqa: E241 - start_node_id_key: relationship.start_node.id, # noqa: E241 - end_node_id_key: relationship.end_node.id, # noqa: E241 - } + # neptune results not returing element_id, so use id instead + is_neptune = not hasattr(relationship, 'element_id') break + if is_neptune: + map_dict_df = pd.DataFrame ([ + { + relationship_id_key: relationship.id, # noqa: E241 + relationship_type_key: relationship.type, # noqa: E241 + start_node_id_key: relationship.start_node.id, # noqa: E241 + end_node_id_key: relationship.end_node.id, # noqa: E241 + } + for relationship in graph.relationships]) + else: + map_dict_df = pd.DataFrame ([ + { + relationship_id_key: relationship.element_id, # noqa: E241 + relationship_type_key: relationship.type, # noqa: E241 + start_node_id_key: relationship.start_node.element_id, # noqa: E241 + end_node_id_key: relationship.end_node.element_id, # noqa: E241 + } + for relationship in graph.relationships]) + df = pd.DataFrame([ - util.merge_two_dicts( - { key: value for (key, value) in relationship.items() }, - map_dict - ) + { key: value for (key, value) in relationship.items() } for relationship in graph.relationships ]) - if len(df) == 0: + + joined_df = map_dict_df.join(df) + + if len(joined_df) == 0: util.warn('Query returned no edges; may have surprising visual results or need to add missing columns for encodings') return pd.DataFrame({ relationship_id_key: pd.Series([], dtype='int32'), @@ -61,35 +70,52 @@ def bolt_graph_to_edges_dataframe(graph): start_node_id_key: pd.Series([], dtype='int32'), end_node_id_key: pd.Series([], dtype='int32') }) - return neo_df_to_pd_df(df) + return neo_df_to_pd_df(joined_df) def bolt_graph_to_nodes_dataframe(graph) -> pd.DataFrame: + + is_neptune=False + for node in graph.nodes: - if hasattr(node, 'element_id'): - map_id_col = node.element_id - else: - map_id_col = node.id + # neptune results not returing element_id, so use id instead + is_neptune = not hasattr(node, 'element_id') break + if is_neptune: + map_dict_df = pd.DataFrame ([ + { + node_id_key: node.id, + node_type_key: ",".join(sorted([str(label) for label in node.labels])) + } + for node in graph.nodes + ]) + else: + map_dict_df = pd.DataFrame ([ + { + node_id_key: node.element_id, + node_type_key: ",".join(sorted([str(label) for label in node.labels])) + } + for node in graph.nodes + ]) + df = pd.DataFrame([ util.merge_two_dicts( { key: value for (key, value) in node.items() }, - util.merge_two_dicts( - { - node_id_key: map_id_col, - node_type_key: ",".join(sorted([str(label) for label in node.labels])) - }, - { node_label_prefix_key + str(label): True for label in node.labels })) + { node_label_prefix_key + str(label): True for label in node.labels }) for node in graph.nodes ]) - if len(df) == 0: + + joined_df = map_dict_df.merge(df, how='outer', left_index=True, right_index=True) + + if len(joined_df) == 0: util.warn('Query returned no nodes') return pd.DataFrame({ node_id_key: pd.Series([], dtype='int32'), node_type_key: pd.Series([], dtype='object') }) - return neo_df_to_pd_df(df) + return neo_df_to_pd_df(joined_df) + # Knowing a col is all-spatial, flatten into primitive cols From 46846ee8121225d955ca5218e77696fb6099baa1 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Mon, 20 Nov 2023 15:12:28 -0600 Subject: [PATCH 034/104] new notebook for neptune cypher using bolt --- .../neptune_cypher_viz_using_bolt.ipynb | 211 ++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100755 demos/demos_databases_apis/neptune/neptune_cypher_viz_using_bolt.ipynb diff --git a/demos/demos_databases_apis/neptune/neptune_cypher_viz_using_bolt.ipynb b/demos/demos_databases_apis/neptune/neptune_cypher_viz_using_bolt.ipynb new file mode 100755 index 0000000000..49cf8e156a --- /dev/null +++ b/demos/demos_databases_apis/neptune/neptune_cypher_viz_using_bolt.ipynb @@ -0,0 +1,211 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "10436f61-3f82-4316-b9be-b6a70746d4f7", + "metadata": {}, + "source": [ + "## Graphistry for Neptune using pygraphistry bolt connector \n", + "\n", + "#### This example uses pygraphistry bolt helper class to run queries against AWS Neptune and retrieve query results as graph, then the bolt helper function extracts all the nodes and edges into the dataframes automatically. Then visualize the resulting datasets using Graphistry. \n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b55398ab", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install --user neo4j" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8481cc10-0407-4675-a966-4b09c411cb45", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "!pip install --user awswrangler" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d3f53062", + "metadata": {}, + "outputs": [], + "source": [ + "!pip install --user graphistry" + ] + }, + { + "cell_type": "markdown", + "id": "e7daa787", + "metadata": {}, + "source": [ + "## make sure to restart kernel after pip install " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c499ed6d-a4fc-44a6-9bd6-62f65dadc329", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "import awswrangler as wr\n", + "import pandas as pd\n", + "import graphistry\n", + "graphistry.__version__" + ] + }, + { + "cell_type": "markdown", + "id": "c509ab26-78fb-4a50-accd-9a5f06a7f0b3", + "metadata": {}, + "source": [ + "### Configure graphistry connnection " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3cd273d7-e81b-4a2e-81f8-09a2f1bf3a11", + "metadata": {}, + "outputs": [], + "source": [ + "# To specify Graphistry account & server, use:\n", + "# graphistry.register(api=3, username='...', password='...', protocol='https', server='hub.graphistry.com')\n", + "\n", + "# To run from a graphistry-host jupyter notebook: \n", + "# graphistry.register(api=3, username=\"...\", password=\"...\", protocol=\"http\", server=\"nginx\") \n", + "\n", + "# to use personal keys:\n", + "# graphistry.register(api=3, protocol=\"...\", server=\"...\", personal_key_id='pkey_id', personal_key_secret='pkey_secret') # Key instead of username+password+org_name\n", + "\n", + "# For more options, see https://github.com/graphistry/pygraphistry#configure\n", + "\n", + "graphistry.register(api=3, username=\"...\", password=\"...\", protocol=\"...\", server=\"...\") " + ] + }, + { + "cell_type": "markdown", + "id": "909e7d08", + "metadata": {}, + "source": [ + "## Configure Neptune connection endpoint: " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e2d83081", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# update with your Neptune endpoint name: \n", + "url='NEPTUNE_NAME.REGION.neptune.amazonaws.com' " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a053c49b-95e7-4ad3-9c16-853738437c8f", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "iam_enabled = False # Set to True/False based on the configuration of your cluster\n", + "neptune_port = 8182 # Set to the Neptune Cluster Port, Default is 8182\n", + "neptune_region = 'us-east-1' # Set to neptune region\n", + "\n", + "client = wr.neptune.connect(url, neptune_port, iam_enabled=iam_enabled, region=neptune_region)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "50391086-8ec6-4dc8-b77d-a75b8822b0e6", + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# check status of neptune connection: \n", + "client.status()" + ] + }, + { + "cell_type": "markdown", + "id": "5404d9b2", + "metadata": {}, + "source": [ + "## Connect to Neptune using pygraphistry bolt connector" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b81686d7", + "metadata": {}, + "outputs": [], + "source": [ + "from neo4j import GraphDatabase\n", + "uri = f\"bolt://{url}:8182\"\n", + "driver = GraphDatabase.driver(uri, auth=(\"ignored\", \"ignored\"), encrypted=True)\n", + "\n", + "graphistry.register(bolt=driver)\n", + "g = graphistry.cypher(\"MATCH (a)-[r]->(b) return a, r, b limit 10000\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "37270027", + "metadata": {}, + "outputs": [], + "source": [ + "g.plot()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c48999ee", + "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.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 3d1b650f91c68c2bb15b5dbc2ac3581b35c8e823 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Mon, 20 Nov 2023 19:52:21 -0600 Subject: [PATCH 035/104] cleanup: linter issues --- graphistry/bolt_util.py | 44 +++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/graphistry/bolt_util.py b/graphistry/bolt_util.py index d80b88d9ec..0529458734 100644 --- a/graphistry/bolt_util.py +++ b/graphistry/bolt_util.py @@ -29,7 +29,7 @@ def to_bolt_driver(driver=None): #TODO catch additional encodings def bolt_graph_to_edges_dataframe(graph): - is_neptune=False + is_neptune = False for relationship in graph.relationships: # neptune results not returing element_id, so use id instead @@ -37,26 +37,28 @@ def bolt_graph_to_edges_dataframe(graph): break if is_neptune: - map_dict_df = pd.DataFrame ([ + map_dict_df = pd.DataFrame([ { relationship_id_key: relationship.id, # noqa: E241 relationship_type_key: relationship.type, # noqa: E241 - start_node_id_key: relationship.start_node.id, # noqa: E241 - end_node_id_key: relationship.end_node.id, # noqa: E241 + start_node_id_key: relationship.start_node.id, # noqa: E241 + end_node_id_key: relationship.end_node.id, # noqa: E241 } for relationship in graph.relationships]) else: - map_dict_df = pd.DataFrame ([ + map_dict_df = pd.DataFrame([ { relationship_id_key: relationship.element_id, # noqa: E241 relationship_type_key: relationship.type, # noqa: E241 - start_node_id_key: relationship.start_node.element_id, # noqa: E241 - end_node_id_key: relationship.end_node.element_id, # noqa: E241 + start_node_id_key: relationship.start_node.element_id, # noqa: E241 + end_node_id_key: relationship.end_node.element_id, # noqa: E241 } for relationship in graph.relationships]) df = pd.DataFrame([ - { key: value for (key, value) in relationship.items() } + { + key: value for (key, value) in relationship.items() + } for relationship in graph.relationships ]) @@ -75,7 +77,7 @@ def bolt_graph_to_edges_dataframe(graph): def bolt_graph_to_nodes_dataframe(graph) -> pd.DataFrame: - is_neptune=False + is_neptune = False for node in graph.nodes: # neptune results not returing element_id, so use id instead @@ -83,20 +85,20 @@ def bolt_graph_to_nodes_dataframe(graph) -> pd.DataFrame: break if is_neptune: - map_dict_df = pd.DataFrame ([ - { - node_id_key: node.id, - node_type_key: ",".join(sorted([str(label) for label in node.labels])) - } - for node in graph.nodes + map_dict_df = pd.DataFrame([ + { + node_id_key: node.id, + node_type_key: ",".join(sorted([str(label) for label in node.labels])) + } + for node in graph.nodes ]) else: - map_dict_df = pd.DataFrame ([ - { - node_id_key: node.element_id, - node_type_key: ",".join(sorted([str(label) for label in node.labels])) - } - for node in graph.nodes + map_dict_df = pd.DataFrame([ + { + node_id_key: node.element_id, + node_type_key: ",".join(sorted([str(label) for label in node.labels])) + } + for node in graph.nodes ]) df = pd.DataFrame([ From cbce06ea0522fb679f4d0a762f2fe59ea51a6707 Mon Sep 17 00:00:00 2001 From: lmeyerov Date: Fri, 1 Dec 2023 00:39:04 -0800 Subject: [PATCH 036/104] docs(changelog) --- CHANGELOG.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 396671f961..bbd3cfb220 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,14 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +### Added + +* Neptune: Can now use PyGraphistry OpenCypher/BOLT bindings with Neptune, in addition to existing Gremlin bindings + +### Docs + +* Neptune: Initial tutorial for using PyGraphistry with Amazon Neptune's OpenCypher/BOLT bindings + ## [0.29.7 - 2023-11-02] ### Added From a3d56b23a743a9c3bef90b318eecbb71622e35bc Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Fri, 1 Dec 2023 01:45:36 -0800 Subject: [PATCH 037/104] fix(hop) --- CHANGELOG.md | 4 +++ graphistry/compute/hop.py | 2 +- graphistry/tests/test_compute_hops.py | 42 +++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 396671f961..9d8bfa9149 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +### Fixed + +* chain/hop: source_node_match was being mishandled when multiple node attributes exist + ## [0.29.7 - 2023-11-02] ### Added diff --git a/graphistry/compute/hop.py b/graphistry/compute/hop.py index 365cedbd88..396715515e 100644 --- a/graphistry/compute/hop.py +++ b/graphistry/compute/hop.py @@ -59,7 +59,7 @@ def hop(self: Plottable, raise ValueError('Source and destination binding cannot be None, please set g._source and g._destination via bind() or edges()') hops_remaining = hops - wave_front = filter_by_dict(nodes[[ g2._node ]], source_node_match) + wave_front = filter_by_dict(nodes, source_node_match)[[ g2._node ]] matches_nodes = None matches_edges = edges_indexed[[EDGE_ID]][:0] diff --git a/graphistry/tests/test_compute_hops.py b/graphistry/tests/test_compute_hops.py index fd53fe9dcb..02c696472b 100644 --- a/graphistry/tests/test_compute_hops.py +++ b/graphistry/tests/test_compute_hops.py @@ -138,3 +138,45 @@ def test_hop_pre_post_match_1(self): assert (g2._nodes[g2._node].sort_values().to_list() == # noqa: W504 sorted(['e', 'l'])) assert g2._edges.shape == (1, 3) + + def test_hop_filter_types(self): + + e_df = pd.DataFrame({ + 's': ['a', 'a', 'd', 'd', 'f', 'f'], + 'd': ['b', 'b', 'e', 'e', 'g', 'g'], + 't': ['x', 'h', 'x', 'h', 'x', 'h'] + }) + n_df = pd.DataFrame({ + 'n': ['a', 'b', 'd', 'e', 'f', 'g'], + 't': ['x', 'm', 'x', 'n', 'x', 'o'] + }) + g = CGFull().edges(e_df, 's', 'd').nodes(n_df, 'n') + + g2a = g.hop(source_node_match={'n': 'a'}) + assert g2a._nodes.shape == (2, 2) + assert g2a._edges.shape == (2, 3) + + g2b = g.hop(source_node_match={'t': 'm'}, direction='forward') + assert g2b._nodes.shape == (0, 2) + assert g2b._edges.shape == (0, 3) + + g3a = g.hop(edge_match={'t': 'h', 's': 'a'}) + assert g3a._nodes.shape == (2, 2) + assert g3a._edges.shape == (1, 3) + + #TODO investigate + #g4a = g.hop(destination_node_match={'t': 'n'}, direction='reverse') + #assert g4a._nodes.shape == (2, 2) + #assert g4a._edges.shape == (2, 3) + + g4a = g.hop(destination_node_match={'t': 'n'}) + assert g4a._nodes.shape == (2, 2) + assert g4a._edges.shape == (2, 3) + + #TODO investigate setting to reverse + g5a = g.hop( + source_node_match={'t': 'x', 'n': 'a'}, + edge_match={'t': 'h'}, + destination_node_match={'t': 'm'}) + assert g5a._nodes.shape == (2, 2) + assert g5a._edges.shape == (1, 3) From 462d35d68020decef3e14fc044351341834f6a54 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Fri, 1 Dec 2023 03:35:31 -0800 Subject: [PATCH 038/104] feat(is_in) --- CHANGELOG.md | 4 +++ README.md | 8 +++-- graphistry/__init__.py | 3 +- graphistry/compute/__init__.py | 1 + graphistry/compute/ast.py | 3 +- graphistry/compute/chain.py | 15 ++++++++++ graphistry/compute/filter_by_dict.py | 30 ++++++++++++++++--- graphistry/tests/test_compute_chain.py | 6 +++- .../tests/test_compute_filter_by_dict.py | 16 +++++++++- graphistry/tests/test_compute_hops.py | 6 +++- 10 files changed, 80 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d8bfa9149..c227f37572 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +### Added + +* chain/hop: `is_in()` membership predicate, `.chain([ n({'type': is_in(['a', 'b'])}) ])` + ### Fixed * chain/hop: source_node_match was being mishandled when multiple node attributes exist diff --git a/README.md b/README.md index 73388abcdb..bf67192a51 100644 --- a/README.md +++ b/README.md @@ -1108,7 +1108,8 @@ g2.plot() # nodes are values from cols s, d, k1 destination_node_match={"k2": 2}) .chain([ # filter to subgraph n(), - n({'k2': 0}), + n({'k2': 0, "m": 'ok'}), #specific values + n({'type': is_in(["type1", "type2"])}), #multiple valid values n(name="start"), # add column 'start':bool e_forward({'k1': 'x'}, hops=1), # same API as hop() e_undirected(name='second_edge'), @@ -1200,10 +1201,11 @@ g5.plot() Rich compound patterns are enabled via `.chain()`: ```python -from graphistry import n, e_forward, e_reverse, e_undirected +from graphistry import n, e_forward, e_reverse, e_undirected, is_in g2.chain([ n() ]) -g2.chain([ n({"v": 1, "y": True}) ]) +g2.chain([ n({"x": 1, "y": True}) ]), +g2.chain([ n({"z": is_in([1,2,4,'z'])}) ]), # multiple valid values g2.chain([ e_forward({"type": "x"}, hops=2) ]) # simple multi-hop g3 = g2.chain([ n(name="start"), # tag node matches diff --git a/graphistry/__init__.py b/graphistry/__init__.py index 64c2f29ba0..02e467afea 100644 --- a/graphistry/__init__.py +++ b/graphistry/__init__.py @@ -50,7 +50,8 @@ ) from graphistry.compute import ( - n, e_forward, e_reverse, e_undirected + n, e_forward, e_reverse, e_undirected, + is_in, IsIn ) from graphistry.Engine import Engine diff --git a/graphistry/compute/__init__.py b/graphistry/compute/__init__.py index 3c7c7f45e3..0b26750541 100644 --- a/graphistry/compute/__init__.py +++ b/graphistry/compute/__init__.py @@ -2,3 +2,4 @@ from .ast import ( n, e_forward, e_reverse, e_undirected ) +from .filter_by_dict import is_in, IsIn diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 2f08271c6c..c81d583bf9 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -1,7 +1,8 @@ -from typing import Any, Optional +from typing import Any, List, Optional import pandas as pd from graphistry.Plottable import Plottable +from .filter_by_dict import is_in, IsIn import logging logger = logging.getLogger(__name__) diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index 4920b74c9f..0ff193f58e 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -141,6 +141,21 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: ]) print('# hits:', len(g_risky._nodes[ g_risky._nodes.hit ])) + **Example: Filter by multiple node types at each step using is_in** + + :: + + from graphistry.ast import n, e_forward, e_reverse, is_in + + g_risky = g.chain([ + n({"type": is_in(["person", "company"])}), + e_forward({"e_type": is_in(["owns", "reviews"])}, to_fixed=True), + n({"type": is_in(["transaction", "account"])}, name="hit"), + e_reverse(to_fixed=True), + n({"risk2": True}) + ]) + print('# hits:', len(g_risky._nodes[ g_risky._nodes.hit ])) + """ if len(ops) == 0: diff --git a/graphistry/compute/filter_by_dict.py b/graphistry/compute/filter_by_dict.py index 678353dcd9..5aa9ef77fc 100644 --- a/graphistry/compute/filter_by_dict.py +++ b/graphistry/compute/filter_by_dict.py @@ -1,9 +1,17 @@ -from typing import Optional, TYPE_CHECKING +from typing import Any, Dict, List, Optional, TYPE_CHECKING import pandas as pd from graphistry.Plottable import Plottable +class IsIn(): + def __init__(self, options: List[Any]) -> None: + self.options = options + +def is_in(options: List[Any]) -> IsIn: + return IsIn(options) + + def filter_by_dict(df, filter_dict: Optional[dict] = None) -> pd.DataFrame: """ return df where rows match all values in filter_dict @@ -12,11 +20,25 @@ def filter_by_dict(df, filter_dict: Optional[dict] = None) -> pd.DataFrame: if filter_dict is None or filter_dict == {}: return df - for col in filter_dict.keys(): + ins: Dict[str, IsIn] = {} + for col, val in filter_dict.items(): if col not in df.columns: raise ValueError(f'Key "{col}" not in columns of df, available columns are: {df.columns}') - - hits = (df[list(filter_dict)] == pd.Series(filter_dict)).all(axis=1) + if isinstance(val, IsIn): + ins[col] = val + filter_dict_concrete = filter_dict if not ins else { + k: v + for k, v in filter_dict.items() + if not isinstance(v, IsIn) + } + + if filter_dict_concrete: + hits = (df[list(filter_dict_concrete)] == pd.Series(filter_dict_concrete)).all(axis=1) + else: + hits = df[[]].assign(x=True).x + if ins: + for col, val in ins.items(): + hits = hits & df[col].isin(val.options) return df[hits] diff --git a/graphistry/tests/test_compute_chain.py b/graphistry/tests/test_compute_chain.py index f66f3151b5..5779b21056 100644 --- a/graphistry/tests/test_compute_chain.py +++ b/graphistry/tests/test_compute_chain.py @@ -2,7 +2,7 @@ from common import NoAuthTestCase from graphistry.tests.test_compute_hops import hops_graph -from graphistry.compute.ast import n, e_forward, e_reverse, e_undirected +from graphistry.compute.ast import n, e_forward, e_reverse, e_undirected, is_in class TestComputeChainMixin(NoAuthTestCase): @@ -100,3 +100,7 @@ def test_chain_named(self): assert sorted(g2._edges[ g2._edges.e2 ][g2._source].to_list()) == ["g", "l"] assert sorted(g2._edges[ g2._edges.e2 ][g2._destination].to_list()) == ["a", "b"] assert sorted(g2._nodes[ g2._nodes.n2 ][g2._node].to_list()) == ["a", "b"] + + def test_chain_is_in(self): + g = hops_graph() + assert g.chain([n({'node': is_in(['e', 'k'])})])._nodes.shape == (2, 2) diff --git a/graphistry/tests/test_compute_filter_by_dict.py b/graphistry/tests/test_compute_filter_by_dict.py index e76f0c1598..5babdd9211 100644 --- a/graphistry/tests/test_compute_filter_by_dict.py +++ b/graphistry/tests/test_compute_filter_by_dict.py @@ -1,7 +1,7 @@ import pandas as pd from functools import lru_cache -from graphistry.compute.filter_by_dict import filter_by_dict +from graphistry.compute.filter_by_dict import filter_by_dict, is_in, IsIn from graphistry.tests.test_compute import CGFull @lru_cache(maxsize=1) @@ -106,3 +106,17 @@ def test_kv_multiple_good(self): def test_kv_multiple_bad(self): g = hops_graph() assert g.filter_edges_by_dict({'i': -100, 'type': 'e'})._edges.equals(g._edges[:0]) + +class TestIsIn(object): + + def test_standalone(self): + g = hops_graph() + assert g.filter_nodes_by_dict({'node': is_in(['a'])})._nodes.equals(g._nodes[:1]) + assert g.filter_nodes_by_dict({'node': is_in(['a', 'b'])})._nodes.equals(g._nodes[:2]) + + def test_combined(self): + g = hops_graph() + assert g.filter_nodes_by_dict({'node': is_in(['a', 'b']), 'type': 'n'})._nodes.equals(g._nodes[:2]) + assert g.filter_nodes_by_dict({'node': is_in(['a', 'b']), 'type': 'bad'})._nodes.equals(g._nodes[:0]) + assert g.filter_nodes_by_dict({'node': is_in(['a', 'bad']), 'type': 'n'})._nodes.equals(g._nodes[:1]) + assert g.filter_nodes_by_dict({'node': is_in(['a', 'bad']), 'type': 'bad'})._nodes.equals(g._nodes[:0]) diff --git a/graphistry/tests/test_compute_hops.py b/graphistry/tests/test_compute_hops.py index 02c696472b..5db33ff6c1 100644 --- a/graphistry/tests/test_compute_hops.py +++ b/graphistry/tests/test_compute_hops.py @@ -2,9 +2,9 @@ from common import NoAuthTestCase from functools import lru_cache +from graphistry.compute.filter_by_dict import is_in from graphistry.tests.test_compute import CGFull - @lru_cache(maxsize=1) def hops_graph(): nodes_df = pd.DataFrame([ @@ -180,3 +180,7 @@ def test_hop_filter_types(self): destination_node_match={'t': 'm'}) assert g5a._nodes.shape == (2, 2) assert g5a._edges.shape == (1, 3) + + def test_is_in(self): + g = hops_graph() + assert g.hop(source_node_match={'node': is_in(['e', 'k'])})._edges.shape == (3, 3) From fb4a038eb469213bd163fe08f8d77ae2493473a1 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Fri, 1 Dec 2023 03:46:08 -0800 Subject: [PATCH 039/104] fix(docs): IsIn --- docs/source/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index 5b421716ad..b7748c38a2 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -68,6 +68,7 @@ ('py:class', 'graphistry.embed_utils.HeterographEmbedModuleMixin'), ('py:class', 'graphistry.PlotterBase.PlotterBase'), ('py:class', 'graphistry.compute.ast.ASTObject'), + ('py:class', 'graphistry.compute.filter_by_dict.IsIn'), ('py:class', 'Plotter'), ('py:class', 'Plottable'), ('py:class', 'CuGraphKind'), From 9aae0f9bd0fa718371446a53e49c73e9cc863d49 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Fri, 1 Dec 2023 19:26:22 -0800 Subject: [PATCH 040/104] fix(logger defaults): do not override default to DEBUG --- CHANGELOG.md | 1 + graphistry/compute/ast.py | 2 +- graphistry/compute/chain.py | 2 +- graphistry/compute/collapse.py | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c227f37572..7c3f1b0b17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed * chain/hop: source_node_match was being mishandled when multiple node attributes exist +* compute logging no longer default-overrides level to DEBUG ## [0.29.7 - 2023-11-02] diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index c81d583bf9..68e447d660 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -6,7 +6,7 @@ import logging logger = logging.getLogger(__name__) -logger.setLevel(logging.DEBUG) +#logger.setLevel(logging.DEBUG) ############################################################################## diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index 0ff193f58e..6d68a1e14e 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -7,7 +7,7 @@ import logging logger = logging.getLogger(__name__) -logger.setLevel(logging.DEBUG) +#logger.setLevel(logging.DEBUG) ############################################################################### diff --git a/graphistry/compute/collapse.py b/graphistry/compute/collapse.py index e9b06e512c..ad29a6f4c1 100644 --- a/graphistry/compute/collapse.py +++ b/graphistry/compute/collapse.py @@ -4,12 +4,12 @@ from graphistry.PlotterBase import Plottable logger = logging.getLogger("collapse") -logger.setLevel(logging.DEBUG) +#logger.setLevel(logging.DEBUG) # create console handler and set level to debug # best for development or debugging consoleHandler = logging.StreamHandler() -consoleHandler.setLevel(logging.DEBUG) +#consoleHandler.setLevel(logging.DEBUG) # create formatter formatter = logging.Formatter(': %(message)s') From b3ebd2272f49a202e80f2ea9db0ca9c5760a8950 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sat, 2 Dec 2023 18:37:52 -0800 Subject: [PATCH 041/104] refactor(ast reverse): more legible, maybe a fix --- graphistry/compute/ast.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 68e447d660..4eb7a1ba79 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -136,6 +136,12 @@ def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame]) -> def reverse(self) -> 'ASTEdge': # updates both edges and nodes + if self._direction == 'reverse': + direction = 'forward' + elif self._direction == 'forward': + direction = 'reverse' + else: + direction = 'undirected' return ASTEdge( direction=( 'forward' if self._direction == 'reverse' else 'reverse' From 8989e3be6e88051dcd2005ca26e84e2797acdbaa Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sat, 2 Dec 2023 18:38:07 -0800 Subject: [PATCH 042/104] refactor(ast reverse): more legible, maybe a fix --- graphistry/compute/ast.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 4eb7a1ba79..1f526fef8c 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -143,9 +143,7 @@ def reverse(self) -> 'ASTEdge': else: direction = 'undirected' return ASTEdge( - direction=( - 'forward' if self._direction == 'reverse' else 'reverse' - ) if self._direction != 'undirected' else 'undirected', + direction=direction, edge_match=self._edge_match, hops=self._hops, to_fixed_point=self._to_fixed_point, From 26caa13fc2743d5ab8e33a70bfeb10f763480301 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sat, 2 Dec 2023 18:39:57 -0800 Subject: [PATCH 043/104] refactor(chain): make step clearer --- graphistry/compute/chain.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index 6d68a1e14e..cd2220816d 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -190,14 +190,17 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: #forwards g_stack : List[Plottable] = [] for op in ops: + prev_step_nodes = ( # start from only prev step's wavefront node + None # first uses full graph + if len(g_stack) == 0 + else g_stack[-1]._nodes + ) g_step = ( op( - g=g, - prev_node_wavefront=( - None # first uses full graph - if len(g_stack) == 0 - else g_stack[-1]._nodes - ))) + g=g, # transition via any original edge + prev_node_wavefront=prev_step_nodes, + ) + ) g_stack.append(g_step) encountered_nodes_df = pd.concat([ From ad3567dbc0af5cac1477306ed960b81b34e2a2cf Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sat, 2 Dec 2023 18:43:49 -0800 Subject: [PATCH 044/104] refactor(chain backwards): clearer indexing --- graphistry/compute/chain.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index cd2220816d..464d630aa6 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -210,9 +210,14 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: logger.debug('============ BACKWARDS ============') - #backwards - g_stack_reverse : List[Plottable] = [g_stack[-1]] + g_stack_reverse : List[Plottable] = [] for (op, g_step) in zip(reversed(ops), reversed(g_stack)): + prev_loop_step = g_stack[-1] if len(g_stack_reverse) == 0 else g_stack_reverse[-1] + if len(g_stack_reverse) == len(g_stack) - 1: + prev_orig_step = None + else: + prev_orig_step = g_stack[-(len(g_stack_reverse) + 2)] + assert prev_loop_step._nodes is not None g_step_reverse = ( (op.reverse())( @@ -227,10 +232,10 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: g_stack_reverse.append(g_step_reverse) logger.debug('============ COMBINE NODES ============') - final_nodes_df = combine_steps(g, 'nodes', list(zip(reversed(ops), g_stack_reverse[1:]))) + final_nodes_df = combine_steps(g, 'nodes', list(zip(ops, reversed(g_stack_reverse)))) logger.debug('============ COMBINE EDGES ============') - final_edges_df = combine_steps(g, 'edges', list(zip(reversed(ops), g_stack_reverse[1:]))) + final_edges_df = combine_steps(g, 'edges', list(zip(ops, reversed(g_stack_reverse)))) if added_edge_index: final_edges_df = final_edges_df.drop(columns=['index']) From 0cdbaee80719200185dfc4f3e40cd4936f722ac1 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sat, 2 Dec 2023 18:55:24 -0800 Subject: [PATCH 045/104] fix(chain): add target_wave_front check --- graphistry/Plottable.py | 3 +- graphistry/compute/ast.py | 16 +- graphistry/compute/chain.py | 21 +- graphistry/compute/hop.py | 28 ++- graphistry/tests/test_compute_chain.py | 300 +++++++++++++++++++++++++ 5 files changed, 351 insertions(+), 17 deletions(-) diff --git a/graphistry/Plottable.py b/graphistry/Plottable.py index 1f20ca35a6..4157781e46 100644 --- a/graphistry/Plottable.py +++ b/graphistry/Plottable.py @@ -209,7 +209,8 @@ def hop(self, edge_match: Optional[dict] = None, source_node_match: Optional[dict] = None, destination_node_match: Optional[dict] = None, - return_as_wave_front: bool = False + return_as_wave_front: bool = False, + target_wave_front: Optional[pd.DataFrame] = None ) -> 'Plottable': if 1 + 1: raise RuntimeError('should not happen') diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 1f526fef8c..38e91766d1 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -1,4 +1,4 @@ -from typing import Any, List, Optional +from typing import Any, List, Optional, cast import pandas as pd from graphistry.Plottable import Plottable @@ -20,7 +20,7 @@ def __init__(self, name: Optional[str] = None): self._name = name pass - def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame]) -> Plottable: + def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], target_wave_front: Optional[pd.DataFrame]) -> Plottable: raise RuntimeError('__call__ not implemented') def reverse(self) -> 'ASTObject': @@ -45,12 +45,17 @@ def __init__(self, filter_dict: Optional[dict] = None, name: Optional[str] = Non def __repr__(self) -> str: return f'ASTNode(filter_dict={self._filter_dict}, name={self._name})' - def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame]) -> Plottable: + def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], target_wave_front: Optional[pd.DataFrame]) -> Plottable: out_g = (g .nodes(prev_node_wavefront if prev_node_wavefront is not None else g._nodes) .filter_nodes_by_dict(self._filter_dict) .edges(g._edges[:0]) ) + if target_wave_front is not None: + assert g._node is not None + reduced_nodes = cast(pd.DataFrame, out_g._nodes).merge(target_wave_front[[g._node]], on=g._node, how='inner') + out_g = out_g.nodes(reduced_nodes) + if self._name is not None: out_g = out_g.nodes(out_g._nodes.assign(**{self._name: True})) @@ -111,7 +116,7 @@ def __init__( def __repr__(self) -> str: return f'ASTEdge(direction={self._direction}, edge_match={self._edge_match}, hops={self._hops}, to_fixed_point={self._to_fixed_point}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, name={self._name})' - def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame]) -> Plottable: + def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], target_wave_front: Optional[pd.DataFrame]) -> Plottable: out_g = g.hop( nodes=prev_node_wavefront, @@ -121,7 +126,8 @@ def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame]) -> source_node_match=self._source_node_match, edge_match=self._edge_match, destination_node_match=self._destination_node_match, - return_as_wave_front=True + return_as_wave_front=True, + target_wave_front=target_wave_front ) if self._name is not None: diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index 464d630aa6..4efdc47647 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -30,8 +30,14 @@ def combine_steps(g: Plottable, kind: str, steps: List[Tuple[ASTObject,Plottable logger.debug('EDGES << recompute forwards given reduced set') steps = [ ( - op, - op(g=g.edges(g_step._edges), prev_node_wavefront=g_step._nodes) + op, # forward op + op( + g=g.edges(g_step._edges), # transition via any found edge + prev_node_wavefront=g_step._nodes, # start from where backwards step says is reachable + + #target_wave_front=steps[i+1][1]._nodes # end at where next backwards step says is reachable + target_wave_front=None # ^^^ optimization: valid transitions already limit to known-good ones + ) ) for (op, g_step) in steps ] @@ -199,6 +205,7 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: op( g=g, # transition via any original edge prev_node_wavefront=prev_step_nodes, + target_wave_front=None # implicit any ) ) g_stack.append(g_step) @@ -221,12 +228,16 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: g_step_reverse = ( (op.reverse())( - # all encountered nodes + step's edges - g=g_step.nodes(encountered_nodes_df), + # Edges: edges used in step (subset matching prev_node_wavefront will be returned) + # Nodes: nodes reached in step (subset matching prev_node_wavefront will be returned) + g=g_step, # check for hits against fully valid targets - prev_node_wavefront=g_stack_reverse[-1]._nodes + # ast will replace g.node() with this as its starting points + prev_node_wavefront=prev_loop_step._nodes, + # only allow transitions to these nodes (vs prev_node_wavefront) + target_wave_front=prev_orig_step._nodes if prev_orig_step is not None else None ) ) g_stack_reverse.append(g_step_reverse) diff --git a/graphistry/compute/hop.py b/graphistry/compute/hop.py index 396715515e..68b6c1479c 100644 --- a/graphistry/compute/hop.py +++ b/graphistry/compute/hop.py @@ -6,27 +6,37 @@ def hop(self: Plottable, - nodes: Optional[pd.DataFrame] = None, + nodes: Optional[pd.DataFrame] = None, # chain: incoming wavefront hops: Optional[int] = 1, to_fixed_point: bool = False, direction: str = 'forward', edge_match: Optional[dict] = None, source_node_match: Optional[dict] = None, destination_node_match: Optional[dict] = None, - return_as_wave_front = False + return_as_wave_front = False, + target_wave_front: Optional[pd.DataFrame] = None # chain: limit hits to these for reverse pass ) -> Plottable: """ Given a graph and some source nodes, return subgraph of all paths within k-hops from the sources g: Plotter nodes: dataframe with id column matching g._node. None signifies all nodes (default). - hops: how many hops to consider, if any bound (default 1) + hops: consider paths of length 1 to 'hops' steps, if any (default 1). to_fixed_point: keep hopping until no new nodes are found (ignores hops) direction: 'forward', 'reverse', 'undirected' edge_match: dict of kv-pairs to exact match (see also: filter_edges_by_dict) source_node_match: dict of kv-pairs to match nodes before hopping destination_node_match: dict of kv-pairs to match nodes after hopping (including intermediate) return_as_wave_front: Only return the nodes/edges reached, ignoring past ones (primarily for internal use) + target_wave_front: Only consider these nodes for reachability (primarily for internal use by reverse pass) + """ + + """ + When called by chain() during reverse phase: + - return_as_wave_front: True + - this hop will be `op.reverse()` + - nodes will be the wavefront of the next step + """ if not to_fixed_point and not isinstance(hops, int): @@ -82,8 +92,9 @@ def hop(self: Plottable, new_node_ids_forward = hop_edges_forward[[g2._destination]].rename(columns={g2._destination: g2._node}).drop_duplicates() if destination_node_match is not None: + base_nodes = target_wave_front if target_wave_front is not None else g2._nodes new_node_ids_forward = filter_by_dict( - g2._nodes.merge(new_node_ids_forward, on=g2._node, how='inner'), + base_nodes.merge(new_node_ids_forward, on=g2._node, how='inner'), destination_node_match )[[g2._node]] hop_edges_forward = hop_edges_forward.merge( @@ -105,8 +116,9 @@ def hop(self: Plottable, new_node_ids_reverse = hop_edges_reverse[[g2._source]].rename(columns={g2._source: g2._node}).drop_duplicates() if destination_node_match is not None: + base_nodes = target_wave_front if target_wave_front is not None else g2._nodes new_node_ids_reverse = filter_by_dict( - g2._nodes.merge(new_node_ids_reverse, on=g2._node, how='inner'), + base_nodes.merge(new_node_ids_reverse, on=g2._node, how='inner'), destination_node_match )[[g2._node]] hop_edges_reverse = hop_edges_reverse.merge( @@ -161,7 +173,11 @@ def hop(self: Plottable, #hydrate nodes if self._nodes is not None: - final_nodes = self._nodes.merge( + if target_wave_front is not None: + rich_nodes = target_wave_front + else: + rich_nodes = self._nodes + final_nodes = rich_nodes.merge( matches_nodes if matches_nodes is not None else wave_front[:0], on=self._node, how='inner') diff --git a/graphistry/tests/test_compute_chain.py b/graphistry/tests/test_compute_chain.py index 5779b21056..e65da06e4d 100644 --- a/graphistry/tests/test_compute_chain.py +++ b/graphistry/tests/test_compute_chain.py @@ -1,9 +1,29 @@ +from functools import lru_cache +from typing import Dict, List import pandas as pd from common import NoAuthTestCase +from graphistry.tests.test_compute import CGFull from graphistry.tests.test_compute_hops import hops_graph from graphistry.compute.ast import n, e_forward, e_reverse, e_undirected, is_in +import logging +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) + + +@lru_cache(maxsize=1) +def chain_graph(): + return CGFull().edges( + pd.DataFrame({ + 's': ['a', 'b', 'c'], + 'd': ['b', 'c', 'd'] + }), + 's', 'd' + ).nodes( + pd.DataFrame({'n': ['a', 'b', 'c', 'd']}), + 'n' + ) class TestComputeChainMixin(NoAuthTestCase): @@ -104,3 +124,283 @@ def test_chain_named(self): def test_chain_is_in(self): g = hops_graph() assert g.chain([n({'node': is_in(['e', 'k'])})])._nodes.shape == (2, 2) + + def test_post_hop_node_match(self): + + ns = pd.DataFrame({ + 'n': [1, 5], + 'category': ['Port', 'Other'], + }) + + es = pd.DataFrame({ + 's': [1, 1], + 'd': [1, 5] + }) + + g = CGFull().edges(es, 's', 'd').nodes(ns, 'n') + + g2 = g.chain([ + n({'category': 'Port'}), + e_undirected(), + n({'category': 'Port'}) + ]) + assert len(g2._nodes) == 1 + + +def compare_graphs(g, nodes: List[Dict[str, str]], edges: List[Dict[str, str]]) -> None: + assert g._nodes.sort_values(by='n').to_dict(orient='records') == nodes + assert g._edges.sort_values(by=['s', 'd']).to_dict(orient='records') == edges + + +class TestComputeChainWavefront1Mixin(NoAuthTestCase): + """ + Test individual steps for 0-hop and 1-hop + """ + + def test_hop_chain_0(self): + + g = chain_graph() + + g2 = g.chain([ + n({'n': 'a'}) + ]) + + assert g2._nodes.to_dict(orient='records') == [{'n': 'a'}] + assert g2._edges.to_dict(orient='records') == [] + + g3 = g.chain([ + n({'n': 'd'}) + ]) + + assert g3._nodes.to_dict(orient='records') == [{'n': 'd'}] + assert g3._edges.to_dict(orient='records') == [] + + def test_hop_chain_1_forward(self): + + g = chain_graph() + + g_out_nodes_hop = [{'n': 'b'}] + g_out_nodes = [{'n': 'a'}, {'n': 'b'}] + g_out_edges = [{'s': 'a', 'd': 'b'}] + + g2_forward = g.hop( + nodes = pd.DataFrame({'n': ['a']}), + hops = 1, + to_fixed_point = False, + direction = 'forward', + source_node_match = None, + edge_match = None, + destination_node_match = None, + return_as_wave_front = True + ) + compare_graphs(g2_forward, g_out_nodes_hop, g_out_edges) + + g2_forward_triple = g.chain([ + e_forward({}, source_node_match={'n': 'a'}, hops=1) + ]) + compare_graphs(g2_forward_triple, g_out_nodes, g_out_edges) + + g2_forward_chain = g.chain([ + n({'n': 'a'}), + e_forward({}, hops=1) + ]) + compare_graphs(g2_forward_chain, g_out_nodes, g_out_edges) + + g2_forward_chain_closed = g.chain([ + n({'n': 'a'}), + e_forward({}, hops=1), + n({}) + ]) + compare_graphs(g2_forward_chain_closed, g_out_nodes, g_out_edges) + + def test_hop_chain_1_reverse(self): + + g = chain_graph() + + g_out_nodes_hop = [] + g_out_nodes = [] + g_out_edges = [] + + g2_reverse = g.hop( + nodes = pd.DataFrame({'n': ['a']}), + hops = 1, + to_fixed_point = False, + direction = 'reverse', + source_node_match = None, + edge_match = None, + destination_node_match = None, + return_as_wave_front = True + ) + compare_graphs(g2_reverse, g_out_nodes_hop, g_out_edges) + + g2_reverse_triple = g.chain([ + e_reverse({}, source_node_match={'n': 'a'}, hops=1) + ]) + compare_graphs(g2_reverse_triple, g_out_nodes, g_out_edges) + + g2_reverse_chain = g.chain([ + n({'n': 'a'}), + e_reverse({}, hops=1) + ]) + compare_graphs(g2_reverse_chain, g_out_nodes, g_out_edges) + + g2_reverse_chain_closed = g.chain([ + n({'n': 'a'}), + e_reverse({}, hops=1), + n({}) + ]) + compare_graphs(g2_reverse_chain_closed, g_out_nodes, g_out_edges) + + def test_hop_chain_1_undirected(self): + + g = chain_graph() + + g_out_nodes_hop = [{'n': 'b'}] + g_out_nodes = [{'n': 'a'}, {'n': 'b'}] + g_out_edges = [{'s': 'a', 'd': 'b'}] + + g2_undirected = g.hop( + nodes = pd.DataFrame({'n': ['a']}), + hops = 1, + to_fixed_point = False, + direction = 'undirected', + source_node_match = None, + edge_match = None, + destination_node_match = None, + return_as_wave_front = True + ) + compare_graphs(g2_undirected, g_out_nodes_hop, g_out_edges) + + g2_undirected_triple = g.chain([ + e_undirected({}, source_node_match={'n': 'a'}, hops=1) + ]) + compare_graphs(g2_undirected_triple, g_out_nodes, g_out_edges) + + g2_undirected_chain = g.chain([ + n({'n': 'a'}), + e_undirected({}, hops=1) + ]) + compare_graphs(g2_undirected_chain, g_out_nodes, g_out_edges) + + g2_undirected_chain_closed = g.chain([ + n({'n': 'a'}), + e_undirected({}, hops=1), + n({}) + ]) + compare_graphs(g2_undirected_chain_closed, g_out_nodes, g_out_edges) + + def test_hop_chain_1_end_forward(self): + + g = chain_graph() + + g_out_nodes_hop = [] + g_out_nodes = [] + g_out_edges = [] + + g3_forward = g.hop( + nodes = pd.DataFrame({'n': ['d']}), + hops = 2, + to_fixed_point = False, + direction = 'forward', + source_node_match = None, + edge_match = None, + destination_node_match = None, + return_as_wave_front = True + ) + compare_graphs(g3_forward, g_out_nodes_hop, g_out_edges) + + g3_forward_triple = g.chain([ + e_forward({}, source_node_match={'n': 'd'}, hops=1) + ]) + compare_graphs(g3_forward_triple, g_out_nodes, g_out_edges) + + g3_forward_chain = g.chain([ + n({'n': 'd'}), + e_forward({}, hops=1) + ]) + compare_graphs(g3_forward_chain, g_out_nodes, g_out_edges) + + g3_forward_chain_closed = g.chain([ + n({'n': 'd'}), + e_forward({}, hops=1), + n({}) + ]) + compare_graphs(g3_forward_chain_closed, g_out_nodes, g_out_edges) + + def test_hop_chain_1_end_reverse(self): + + g = chain_graph() + + g_out_nodes_hop = [{'n': 'c'}] + g_out_nodes = [{'n': 'c'}, {'n': 'd'}] + g_out_edges = [{'s': 'c', 'd': 'd'}] + + g3_reverse = g.hop( + nodes = pd.DataFrame({'n': ['d']}), + hops = 1, + to_fixed_point = False, + direction = 'reverse', + source_node_match = None, + edge_match = None, + destination_node_match = None, + return_as_wave_front = True + ) + compare_graphs(g3_reverse, g_out_nodes_hop, g_out_edges) + + g3_reverse_triple = g.chain([ + e_reverse({}, source_node_match={'n': 'd'}, hops=1) + ]) + compare_graphs(g3_reverse_triple, g_out_nodes, g_out_edges) + + g3_reverse_chain = g.chain([ + n({'n': 'd'}), + e_reverse({}, hops=1) + ]) + compare_graphs(g3_reverse_chain, g_out_nodes, g_out_edges) + + g3_reverse_chain_closed = g.chain([ + n({'n': 'd'}), + e_reverse({}, hops=1), + n({}) + ]) + compare_graphs(g3_reverse_chain_closed, g_out_nodes, g_out_edges) + + def test_hop_chain_1_end_undirected(self): + + g = chain_graph() + + g_out_nodes_hop = [{'n': 'c'}] + g_out_nodes = [{'n': 'c'}, {'n': 'd'}] + g_out_edges = [{'s': 'c', 'd': 'd'}] + + g3_undirected = g.hop( + nodes = pd.DataFrame({'n': ['d']}), + hops = 1, + to_fixed_point = False, + direction = 'undirected', + source_node_match = None, + edge_match = None, + destination_node_match = None, + return_as_wave_front = True + ) + compare_graphs(g3_undirected, g_out_nodes_hop, g_out_edges) + + g3_undirected_triple = g.chain([ + e_undirected({}, source_node_match={'n': 'd'}, hops=1) + ]) + compare_graphs(g3_undirected_triple, g_out_nodes, g_out_edges) + + g3_undirected_chain = g.chain([ + n({'n': 'd'}), + e_undirected({}, hops=1) + ]) + compare_graphs(g3_undirected_chain, g_out_nodes, g_out_edges) + + g3_undirected_chain_closed = g.chain([ + n({'n': 'd'}), + e_undirected({}, hops=1), + n({}) + ]) + compare_graphs(g3_undirected_chain_closed, g_out_nodes, g_out_edges) + + From edc1f11379af7551da79190755cc8c989e937fa5 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 16:05:25 -0800 Subject: [PATCH 046/104] fix(test): order-agnostic --- graphistry/tests/test_compute_chain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphistry/tests/test_compute_chain.py b/graphistry/tests/test_compute_chain.py index e65da06e4d..a02af0a59f 100644 --- a/graphistry/tests/test_compute_chain.py +++ b/graphistry/tests/test_compute_chain.py @@ -101,7 +101,7 @@ def test_chain_multi(self): e_forward({}, hops=1) ]) - assert g2b._nodes.equals(g2a._nodes) + assert g2b._nodes.sort_values(by=['node']).reset_index(drop=True).equals(g2a._nodes.sort_values(by=['node']).reset_index(drop=True)) assert g2b._edges.equals(g2b._edges) def test_chain_named(self): From 149dad5cac0f6afcab22b5afcda5b30cde3faabe Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 16:05:46 -0800 Subject: [PATCH 047/104] test(hop): 2-step --- graphistry/tests/test_compute_chain.py | 242 +++++++++++++++++++++++++ 1 file changed, 242 insertions(+) diff --git a/graphistry/tests/test_compute_chain.py b/graphistry/tests/test_compute_chain.py index a02af0a59f..839e32c7fc 100644 --- a/graphistry/tests/test_compute_chain.py +++ b/graphistry/tests/test_compute_chain.py @@ -404,3 +404,245 @@ def test_hop_chain_1_end_undirected(self): compare_graphs(g3_undirected_chain_closed, g_out_nodes, g_out_edges) +class TestComputeChainWavefront2Mixin(NoAuthTestCase): + """ + Test individual steps for 2-hop + """ + + def test_hop_chain_2(self): + + g = chain_graph() + + g_out_nodes_hop = [{'n': 'b'}, {'n': 'c'}] + g_out_nodes = [{'n': 'a'}, {'n': 'b'}, {'n': 'c'}] + g_out_edges = [{'s': 'a', 'd': 'b'}, {'s': 'b', 'd': 'c'}] + + g2_forward = g.hop( + nodes = pd.DataFrame({'n': ['a']}), + hops = 2, + to_fixed_point = False, + direction = 'forward', + source_node_match = None, + edge_match = None, + destination_node_match = None, + return_as_wave_front = True + ) + compare_graphs(g2_forward, g_out_nodes_hop, g_out_edges) + + # source _node_match would require each hop to start with {'n': 'a'} + #g2_forward_triple = g.chain([ + # e_forward({}, source_node_match={'n': 'a'}, hops=2) + #]) + #compare_graphs(g2_forward_triple, g_out_nodes, g_out_edges) + + g2_forward_chain = g.chain([ + n({'n': 'a'}), + e_forward({}, hops=2) + ]) + compare_graphs(g2_forward_chain, g_out_nodes, g_out_edges) + + g2_forward_chain_closed = g.chain([ + n({'n': 'a'}), + e_forward({}, hops=2), + n({}) + ]) + compare_graphs(g2_forward_chain_closed, g_out_nodes, g_out_edges) + + + def test_hop_chain_2_reverse(self): + + g = chain_graph() + + g_out_nodes_hop = [] + g_out_nodes = [] + g_out_edges = [] + + g2_reverse = g.hop( + nodes = pd.DataFrame({'n': ['a']}), + hops = 2, + to_fixed_point = False, + direction = 'reverse', + source_node_match = None, + edge_match = None, + destination_node_match = None, + return_as_wave_front = True + ) + compare_graphs(g2_reverse, g_out_nodes_hop, g_out_edges) + + # source _node_match would require each hop to start with {'n': 'a'} + #g2_reverse_triple = g.chain([ + # e_reverse({}, source_node_match={'n': 'a'}, hops=2) + #]) + #compare_graphs(g2_reverse_triple, g_out_nodes, g_out_edges) + + g2_reverse_chain = g.chain([ + n({'n': 'a'}), + e_reverse({}, hops=2) + ]) + compare_graphs(g2_reverse_chain, g_out_nodes, g_out_edges) + + g2_reverse_chain_closed = g.chain([ + n({'n': 'a'}), + e_reverse({}, hops=2), + n({}) + ]) + compare_graphs(g2_reverse_chain_closed, g_out_nodes, g_out_edges) + + def test_hop_chain_2_undirected(self): + + g = chain_graph() + + g_out_nodes_hop = [{'n': 'a'}, {'n': 'b'}, {'n': 'c'}] + g_out_nodes = [{'n': 'a'}, {'n': 'b'}, {'n': 'c'}] + g_out_edges = [{'s': 'a', 'd': 'b'}, {'s': 'b', 'd': 'c'}] + + g2_undirected = g.hop( + nodes = pd.DataFrame({'n': ['a']}), + hops = 2, + to_fixed_point = False, + direction = 'undirected', + source_node_match = None, + edge_match = None, + destination_node_match = None, + return_as_wave_front = True + ) + compare_graphs(g2_undirected, g_out_nodes_hop, g_out_edges) + + # source _node_match would require each hop to start with {'n': 'a'} + #g2_undirected_triple = g.chain([ + # e_undirected({}, source_node_match={'n': 'a'}, hops=2) + #]) + #compare_graphs(g2_undirected_triple, g_out_nodes, g_out_edges) + + g2_undirected_chain = g.chain([ + n({'n': 'a'}), + e_undirected({}, hops=2) + ]) + compare_graphs(g2_undirected_chain, g_out_nodes, g_out_edges) + + g2_undirected_chain_closed = g.chain([ + n({'n': 'a'}), + e_undirected({}, hops=2), + n({}) + ]) + compare_graphs(g2_undirected_chain_closed, g_out_nodes, g_out_edges) + + + def test_hop_chain_2_end(self): + + g = chain_graph() + + g_out_nodes_hop = [] + g_out_nodes = [] + g_out_edges = [] + + g3_forward = g.hop( + nodes = pd.DataFrame({'n': ['d']}), + hops = 2, + to_fixed_point = False, + direction = 'forward', + source_node_match = None, + edge_match = None, + destination_node_match = None, + return_as_wave_front = True + ) + compare_graphs(g3_forward, g_out_nodes_hop, g_out_edges) + + # source _node_match would require each hop to start with {'n': 'd'} + #g3_forward_triple = g.chain([ + # e_forward({}, source_node_match={'n': 'd'}, hops=2) + #]) + #compare_graphs(g3_forward_triple, g_out_nodes, g_out_edges) + + g3_forward_chain = g.chain([ + n({'n': 'd'}), + e_forward({}, hops=2) + ]) + compare_graphs(g3_forward_chain, g_out_nodes, g_out_edges) + + g3_forward_chain_closed = g.chain([ + n({'n': 'd'}), + e_forward({}, hops=2), + n({}) + ]) + compare_graphs(g3_forward_chain_closed, g_out_nodes, g_out_edges) + + + def test_hop_chain_2_end_reverse(self): + + g = chain_graph() + + g_out_nodes_hop = [{'n': 'b'}, {'n': 'c'}] + g_out_nodes = [{'n': 'b'}, {'n': 'c'}, {'n': 'd'}] + g_out_edges = [{'s': 'b', 'd': 'c'}, {'s': 'c', 'd': 'd'}] + + g3_reverse = g.hop( + nodes = pd.DataFrame({'n': ['d']}), + hops = 2, + to_fixed_point = False, + direction = 'reverse', + source_node_match = None, + edge_match = None, + destination_node_match = None, + return_as_wave_front = True + ) + compare_graphs(g3_reverse, g_out_nodes_hop, g_out_edges) + + # source _node_match would require each hop to start with {'n': 'd'} + # g3_reverse_triple = g.chain([ + # e_reverse({}, source_node_match={'n': 'd'}, hops=2) + #]) + #compare_graphs(g3_reverse_triple, g_out_nodes, g_out_edges) + + g3_reverse_chain = g.chain([ + n({'n': 'd'}), + e_reverse({}, hops=2) + ]) + compare_graphs(g3_reverse_chain, g_out_nodes, g_out_edges) + + g3_reverse_chain_closed = g.chain([ + n({'n': 'd'}), + e_reverse({}, hops=2), + n({}) + ]) + compare_graphs(g3_reverse_chain_closed, g_out_nodes, g_out_edges) + + + def test_hop_chain_2_end_undirected(self): + + g = chain_graph() + + g_out_nodes_hop = [{'n': 'b'}, {'n': 'c'}, {'n': 'd'}] + g_out_nodes = [{'n': 'b'}, {'n': 'c'}, {'n': 'd'}] + g_out_edges = [{'s': 'b', 'd': 'c'}, {'s': 'c', 'd': 'd'}] + + g3_undirected = g.hop( + nodes = pd.DataFrame({'n': ['d']}), + hops = 2, + to_fixed_point = False, + direction = 'undirected', + source_node_match = None, + edge_match = None, + destination_node_match = None, + return_as_wave_front = True + ) + compare_graphs(g3_undirected, g_out_nodes_hop, g_out_edges) + + # source _node_match would require each hop to start with {'n': 'd'} + #g3_undirected_triple = g.chain([ + # e_undirected({}, source_node_match={'n': 'd'}, hops=2) + #]) + #compare_graphs(g3_undirected_triple, g_out_nodes, g_out_edges) + + g3_undirected_chain = g.chain([ + n({'n': 'd'}), + e_undirected({}, hops=2) + ]) + compare_graphs(g3_undirected_chain, g_out_nodes, g_out_edges) + + g3_undirected_chain_closed = g.chain([ + n({'n': 'd'}), + e_undirected({}, hops=2), + n({}) + ]) + compare_graphs(g3_undirected_chain_closed, g_out_nodes, g_out_edges) From 7c9bf31b315b321ab7fd5ec4161c744d024a3933 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 16:08:28 -0800 Subject: [PATCH 048/104] fix(hop): source_node_match within multi-hop not just first hop --- graphistry/compute/hop.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/graphistry/compute/hop.py b/graphistry/compute/hop.py index 68b6c1479c..049fed8b2e 100644 --- a/graphistry/compute/hop.py +++ b/graphistry/compute/hop.py @@ -69,21 +69,36 @@ def hop(self: Plottable, raise ValueError('Source and destination binding cannot be None, please set g._source and g._destination via bind() or edges()') hops_remaining = hops - wave_front = filter_by_dict(nodes, source_node_match)[[ g2._node ]] + + wave_front = nodes[[g2._node]][:0] + matches_nodes = None matches_edges = edges_indexed[[EDGE_ID]][:0] + #richly-attributed subset for dest matching & return-enriching + base_target_nodes = target_wave_front if target_wave_front is not None else g2._nodes + + first_iter = True while True: if not to_fixed_point and hops_remaining is not None: if hops_remaining < 1: break hops_remaining = hops_remaining - 1 + + assert len(wave_front.columns) == 1, "just indexes" + wave_front_iter : pd.DataFrame = ( + filter_by_dict( + nodes if first_iter else wave_front.merge(nodes, on=g2._node, how='left'), + source_node_match + )[[ g2._node ]] + ) + first_iter = False hop_edges_forward = None new_node_ids_forward = None if direction in ['forward', 'undirected']: hop_edges_forward = ( - wave_front.merge( + wave_front_iter.merge( edges_indexed[[g2._source, g2._destination, EDGE_ID]].assign(**{g2._node: edges_indexed[g2._source]}), how='inner', on=g2._node) @@ -94,7 +109,7 @@ def hop(self: Plottable, if destination_node_match is not None: base_nodes = target_wave_front if target_wave_front is not None else g2._nodes new_node_ids_forward = filter_by_dict( - base_nodes.merge(new_node_ids_forward, on=g2._node, how='inner'), + base_target_nodes.merge(new_node_ids_forward, on=g2._node, how='inner'), destination_node_match )[[g2._node]] hop_edges_forward = hop_edges_forward.merge( @@ -106,8 +121,9 @@ def hop(self: Plottable, hop_edges_reverse = None new_node_ids_reverse = None if direction in ['reverse', 'undirected']: + #TODO limit by target_wave_front if exists? hop_edges_reverse = ( - wave_front.merge( + wave_front_iter.merge( edges_indexed[[g2._destination, g2._source, EDGE_ID]].assign(**{g2._node: edges_indexed[g2._destination]}), how='inner', on=g2._node) @@ -118,7 +134,7 @@ def hop(self: Plottable, if destination_node_match is not None: base_nodes = target_wave_front if target_wave_front is not None else g2._nodes new_node_ids_reverse = filter_by_dict( - base_nodes.merge(new_node_ids_reverse, on=g2._node, how='inner'), + base_target_nodes.merge(new_node_ids_reverse, on=g2._node, how='inner'), destination_node_match )[[g2._node]] hop_edges_reverse = hop_edges_reverse.merge( From c2766631026f01d0d371b317d8ad0968032d7abc Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 16:09:15 -0800 Subject: [PATCH 049/104] docs(changelog) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c3f1b0b17..ffcbfdf5ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Fixed * chain/hop: source_node_match was being mishandled when multiple node attributes exist +* chain: backwards validation pass was too permissive; add `target_wave_front` check` +* hop: multi-hops with `source_node_match` specified was not checking intermediate hops * compute logging no longer default-overrides level to DEBUG ## [0.29.7 - 2023-11-02] From 8452edb5f11c62196ac1350d6efe698512bdd370 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 17:24:15 -0800 Subject: [PATCH 050/104] feat(hop chain): df query --- CHANGELOG.md | 4 ++ README.md | 54 ++++++++++++++++++++++-- graphistry/Plottable.py | 3 ++ graphistry/compute/ast.py | 58 ++++++++++++++++++++------ graphistry/compute/hop.py | 55 ++++++++++++++---------- graphistry/tests/test_compute_chain.py | 44 +++++++++++++++++++ graphistry/tests/test_compute_hops.py | 21 +++++++++- 7 files changed, 200 insertions(+), 39 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffcbfdf5ff..452346e1d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added * chain/hop: `is_in()` membership predicate, `.chain([ n({'type': is_in(['a', 'b'])}) ])` +* hop: optional df queries - `hop(..., source_node_query='...', edge_query='...', destination_node_query='...')` +* chain: optional df queries: + - `chain([n(query='...')])` + - `chain([e_forward(..., source_node_query='...', edge_query='...', destination_node_query='...')])` ### Fixed diff --git a/README.md b/README.md index bf67192a51..70c8692891 100644 --- a/README.md +++ b/README.md @@ -1073,7 +1073,7 @@ g.addStyle(logo={ The below methods let you quickly manipulate graphs directly and with dataframe methods: Search, pattern mine, transform, and more: ```python -from graphistry import n, e_forward, e_reverse, e_undirected +from graphistry import n, e_forward, e_reverse, e_undirected, is_in g = (graphistry .edges(pd.DataFrame({ 's': ['a', 'b'], @@ -1101,19 +1101,41 @@ g2.plot() # nodes are values from cols s, d, k1 .hop( # filter to subgraph #almost all optional direction='forward', # 'reverse', 'undirected' - hops=1, # number or None if to_fixed_point + hops=2, # number (1..n hops, inclusive) or None if to_fixed_point to_fixed_point=False, - source_node_match={"k2": 0}, + + #every edge source node must match these + source_node_match={"k2": 0, "k3": is_in(['a', 'b', 3, 4])}, + source_node_query='k2 == 0', + + #every edge must match these edge_match={"k1": "x"}, - destination_node_match={"k2": 2}) + edge_query='k1 == "x"', + + #every edge destination node must match these + destination_node_match={"k2": 2}, + destination_node_query='k2 == 2 or k2 == 4', + ) .chain([ # filter to subgraph n(), n({'k2': 0, "m": 'ok'}), #specific values n({'type': is_in(["type1", "type2"])}), #multiple valid values + n(query='k2 == 0 or k2 == 4'), #dataframe query n(name="start"), # add column 'start':bool e_forward({'k1': 'x'}, hops=1), # same API as hop() e_undirected(name='second_edge'), + e_reverse( + {'k1': 'x'}, # edge property match + hops=2, # 1 to 2 hops + #same API as hop() + source_node_match={"k2": 2}, + source_node_query='k2 == 2 or k2 == 4', + edge_match={"k1": "x"}, + edge_query='k1 == "x"', + destination_node_match={"k2": 0}, + destination_node_query='k2 == 0') ]) + # replace as one node the node w/ given id + transitively connected nodes w/ col=attr .collapse(node='some_id', column='some_col', attribute='some val') ``` @@ -1126,6 +1148,30 @@ g = hg['graph'] # g._edges: | src, dst, user, email, org, time, ... | g.plot() ``` +```python +hg = graphistry.hypergraph( + df, + ['from_user', 'to_user', 'email', 'org'], + direct=True, + opts={ + + # when direct=True, can define src -> [ dst1, dst2, ...] edges + 'EDGES': { + 'org': ['from_user'], # org->from_user + 'from_user': ['email', 'to_user'], #from_user->email, from_user->to_user + }, + + 'CATEGORIES': { + # determine which columns share the same namespace for node generation: + # - if user 'louie' is both a from_user and to_user, show as 1 node + # - if a user & org are both named 'louie', they will appear as 2 different nodes + 'user': ['from_user', 'to_user'] + } +}) +g = hg['graph'] +g.plot() +``` + #### Generate node table ```python diff --git a/graphistry/Plottable.py b/graphistry/Plottable.py index 4157781e46..56cb22cc9b 100644 --- a/graphistry/Plottable.py +++ b/graphistry/Plottable.py @@ -209,6 +209,9 @@ def hop(self, edge_match: Optional[dict] = None, source_node_match: Optional[dict] = None, destination_node_match: Optional[dict] = None, + source_node_query: Optional[str] = None, + destination_node_query: Optional[str] = None, + edge_query: Optional[str] = None, return_as_wave_front: bool = False, target_wave_front: Optional[pd.DataFrame] = None ) -> 'Plottable': diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 38e91766d1..c7edcf9eb9 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -34,13 +34,14 @@ class ASTNode(ASTObject): """ Internal, not intended for use outside of this module. """ - def __init__(self, filter_dict: Optional[dict] = None, name: Optional[str] = None): + def __init__(self, filter_dict: Optional[dict] = None, name: Optional[str] = None, query: Optional[str] = None): super().__init__(name) if filter_dict == {}: filter_dict = None self._filter_dict = filter_dict + self._query = query def __repr__(self) -> str: return f'ASTNode(filter_dict={self._filter_dict}, name={self._name})' @@ -49,6 +50,7 @@ def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], ta out_g = (g .nodes(prev_node_wavefront if prev_node_wavefront is not None else g._nodes) .filter_nodes_by_dict(self._filter_dict) + .nodes(lambda g_dynamic: g_dynamic._nodes.query(self._query) if self._query is not None else g_dynamic._nodes) .edges(g._edges[:0]) ) if target_wave_front is not None: @@ -92,6 +94,9 @@ def __init__( to_fixed_point: bool = DEFAULT_FIXED_POINT, source_node_match: Optional[dict] = DEFAULT_FILTER_DICT, destination_node_match: Optional[dict] = DEFAULT_FILTER_DICT, + source_node_query: Optional[str] = None, + destination_node_query: Optional[str] = None, + edge_query: Optional[str] = None, name: Optional[str] = None ): @@ -112,9 +117,12 @@ def __init__( self._source_node_match = source_node_match self._edge_match = edge_match self._destination_node_match = destination_node_match + self._source_node_query = source_node_query + self._destination_node_query = destination_node_query + self._edge_query = edge_query def __repr__(self) -> str: - return f'ASTEdge(direction={self._direction}, edge_match={self._edge_match}, hops={self._hops}, to_fixed_point={self._to_fixed_point}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, name={self._name})' + return f'ASTEdge(direction={self._direction}, edge_match={self._edge_match}, hops={self._hops}, to_fixed_point={self._to_fixed_point}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, name={self._name}, source_node_query={self._source_node_query}, destination_node_query={self._destination_node_query}, edge_query={self._edge_query})' def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], target_wave_front: Optional[pd.DataFrame]) -> Plottable: @@ -127,7 +135,10 @@ def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], ta edge_match=self._edge_match, destination_node_match=self._destination_node_match, return_as_wave_front=True, - target_wave_front=target_wave_front + target_wave_front=target_wave_front, + source_node_query=self._source_node_query, + destination_node_query=self._destination_node_query, + edge_query=self._edge_query ) if self._name is not None: @@ -154,7 +165,10 @@ def reverse(self) -> 'ASTEdge': hops=self._hops, to_fixed_point=self._to_fixed_point, source_node_match=self._destination_node_match, - destination_node_match=self._source_node_match + destination_node_match=self._source_node_match, + source_node_query=self._destination_node_query, + destination_node_query=self._source_node_query, + edge_query=self._edge_query ) e = ASTEdge # noqa: E305 @@ -168,7 +182,10 @@ def __init__(self, source_node_match: Optional[dict] = DEFAULT_FILTER_DICT, destination_node_match: Optional[dict] = DEFAULT_FILTER_DICT, to_fixed_point: bool = DEFAULT_FIXED_POINT, - name: Optional[str] = None + name: Optional[str] = None, + source_node_query: Optional[str] = None, + destination_node_query: Optional[str] = None, + edge_query: Optional[str] = None ): super().__init__( direction='forward', @@ -177,11 +194,14 @@ def __init__(self, source_node_match=source_node_match, destination_node_match=destination_node_match, to_fixed_point=to_fixed_point, - name=name + name=name, + source_node_query=source_node_query, + destination_node_query=destination_node_query, + edge_query=edge_query ) def __repr__(self) -> str: - return f'ASTEdgeForward(edge_match={self._edge_match}, hops={self._hops}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, to_fixed_point={self._to_fixed_point}, name={self._name})' + return f'ASTEdgeForward(edge_match={self._edge_match}, hops={self._hops}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, to_fixed_point={self._to_fixed_point}, name={self._name}, source_node_query={self._source_node_query}, destination_node_query={self._destination_node_query}, edge_query={self._edge_query})' e_forward = ASTEdgeForward # noqa: E305 @@ -195,7 +215,10 @@ def __init__(self, source_node_match: Optional[dict] = DEFAULT_FILTER_DICT, destination_node_match: Optional[dict] = DEFAULT_FILTER_DICT, to_fixed_point: bool = DEFAULT_FIXED_POINT, - name: Optional[str] = None + name: Optional[str] = None, + source_node_query: Optional[str] = None, + destination_node_query: Optional[str] = None, + edge_query: Optional[str] = None ): super().__init__( direction='reverse', @@ -204,11 +227,14 @@ def __init__(self, source_node_match=source_node_match, destination_node_match=destination_node_match, to_fixed_point=to_fixed_point, - name=name + name=name, + source_node_query=source_node_query, + destination_node_query=destination_node_query, + edge_query=edge_query ) def __repr__(self) -> str: - return f'ASTEdgeReverse(edge_match={self._edge_match}, hops={self._hops}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, to_fixed_point={self._to_fixed_point}, name={self._name})' + return f'ASTEdgeReverse(edge_match={self._edge_match}, hops={self._hops}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, to_fixed_point={self._to_fixed_point}, name={self._name}, source_node_query={self._source_node_query}, destination_node_query={self._destination_node_query}, edge_query={self._edge_query})' e_reverse = ASTEdgeReverse # noqa: E305 @@ -222,7 +248,10 @@ def __init__(self, source_node_match: Optional[dict] = DEFAULT_FILTER_DICT, destination_node_match: Optional[dict] = DEFAULT_FILTER_DICT, to_fixed_point: bool = DEFAULT_FIXED_POINT, - name: Optional[str] = None + name: Optional[str] = None, + source_node_query: Optional[str] = None, + destination_node_query: Optional[str] = None, + edge_query: Optional[str] = None ): super().__init__( direction='undirected', @@ -231,10 +260,13 @@ def __init__(self, source_node_match=source_node_match, destination_node_match=destination_node_match, to_fixed_point=to_fixed_point, - name=name + name=name, + source_node_query=source_node_query, + destination_node_query=destination_node_query, + edge_query=edge_query ) def __repr__(self) -> str: - return f'ASTEdgeUndirected(edge_match={self._edge_match}, hops={self._hops}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, to_fixed_point={self._to_fixed_point}, name={self._name})' + return f'ASTEdgeUndirected(edge_match={self._edge_match}, hops={self._hops}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, to_fixed_point={self._to_fixed_point}, name={self._name}, source_node_query={self._source_node_query}, destination_node_query={self._destination_node_query}, edge_query={self._edge_query})' e_undirected = ASTEdgeUndirected # noqa: E305 diff --git a/graphistry/compute/hop.py b/graphistry/compute/hop.py index 049fed8b2e..c0ade90e53 100644 --- a/graphistry/compute/hop.py +++ b/graphistry/compute/hop.py @@ -5,6 +5,12 @@ from .filter_by_dict import filter_by_dict +def query_if_not_none(query: Optional[str], df: pd.DataFrame) -> pd.DataFrame: + if query is None: + return df + return df.query(query) + + def hop(self: Plottable, nodes: Optional[pd.DataFrame] = None, # chain: incoming wavefront hops: Optional[int] = 1, @@ -13,6 +19,9 @@ def hop(self: Plottable, edge_match: Optional[dict] = None, source_node_match: Optional[dict] = None, destination_node_match: Optional[dict] = None, + source_node_query: Optional[str] = None, + destination_node_query: Optional[str] = None, + edge_query: Optional[str] = None, return_as_wave_front = False, target_wave_front: Optional[pd.DataFrame] = None # chain: limit hits to these for reverse pass ) -> Plottable: @@ -25,8 +34,11 @@ def hop(self: Plottable, to_fixed_point: keep hopping until no new nodes are found (ignores hops) direction: 'forward', 'reverse', 'undirected' edge_match: dict of kv-pairs to exact match (see also: filter_edges_by_dict) - source_node_match: dict of kv-pairs to match nodes before hopping + source_node_match: dict of kv-pairs to match nodes before hopping (including intermediate) destination_node_match: dict of kv-pairs to match nodes after hopping (including intermediate) + source_node_query: dataframe query to match nodes before hopping (including intermediate) + destination_node_query: dataframe query to match nodes after hopping (including intermediate) + edge_query: dataframe query to match edges before hopping (including intermediate) return_as_wave_front: Only return the nodes/edges reached, ignoring past ones (primarily for internal use) target_wave_front: Only consider these nodes for reachability (primarily for internal use by reverse pass) """ @@ -56,10 +68,10 @@ def hop(self: Plottable, if g2._edge is None: if 'index' in g2._edges.columns: raise ValueError('Edges cannot have column "index", please remove or set as g._edge via bind() or edges()') - edges_indexed = g2.filter_edges_by_dict(edge_match)._edges.reset_index() + edges_indexed = query_if_not_none(edge_query, g2.filter_edges_by_dict(edge_match)._edges).reset_index() EDGE_ID = 'index' else: - edges_indexed = g2.filter_edges_by_dict(edge_match)._edges + edges_indexed = query_if_not_none(edge_query, g2.filter_edges_by_dict(edge_match)._edges) EDGE_ID = g2._edge if g2._node is None: @@ -86,12 +98,13 @@ def hop(self: Plottable, hops_remaining = hops_remaining - 1 assert len(wave_front.columns) == 1, "just indexes" - wave_front_iter : pd.DataFrame = ( - filter_by_dict( - nodes if first_iter else wave_front.merge(nodes, on=g2._node, how='left'), - source_node_match - )[[ g2._node ]] - ) + wave_front_iter : pd.DataFrame = query_if_not_none( + source_node_query, + filter_by_dict( + nodes if first_iter else wave_front.merge(nodes, on=g2._node, how='left'), + source_node_match + ) + )[[ g2._node ]] first_iter = False hop_edges_forward = None @@ -106,12 +119,12 @@ def hop(self: Plottable, ) new_node_ids_forward = hop_edges_forward[[g2._destination]].rename(columns={g2._destination: g2._node}).drop_duplicates() - if destination_node_match is not None: - base_nodes = target_wave_front if target_wave_front is not None else g2._nodes - new_node_ids_forward = filter_by_dict( - base_target_nodes.merge(new_node_ids_forward, on=g2._node, how='inner'), - destination_node_match - )[[g2._node]] + new_node_ids_forward = query_if_not_none( + destination_node_query, + filter_by_dict( + base_target_nodes.merge(new_node_ids_forward, on=g2._node, how='inner'), + destination_node_match + ))[[g2._node]] hop_edges_forward = hop_edges_forward.merge( new_node_ids_forward.rename(columns={g2._node: g2._destination}), how='inner', @@ -131,12 +144,12 @@ def hop(self: Plottable, ) new_node_ids_reverse = hop_edges_reverse[[g2._source]].rename(columns={g2._source: g2._node}).drop_duplicates() - if destination_node_match is not None: - base_nodes = target_wave_front if target_wave_front is not None else g2._nodes - new_node_ids_reverse = filter_by_dict( - base_target_nodes.merge(new_node_ids_reverse, on=g2._node, how='inner'), - destination_node_match - )[[g2._node]] + new_node_ids_reverse = query_if_not_none( + destination_node_query, + filter_by_dict( + base_target_nodes.merge(new_node_ids_reverse, on=g2._node, how='inner'), + destination_node_match + ))[[g2._node]] hop_edges_reverse = hop_edges_reverse.merge( new_node_ids_reverse.rename(columns={g2._node: g2._source}), how='inner', diff --git a/graphistry/tests/test_compute_chain.py b/graphistry/tests/test_compute_chain.py index 839e32c7fc..55f112b840 100644 --- a/graphistry/tests/test_compute_chain.py +++ b/graphistry/tests/test_compute_chain.py @@ -646,3 +646,47 @@ def test_hop_chain_2_end_undirected(self): n({}) ]) compare_graphs(g3_undirected_chain_closed, g_out_nodes, g_out_edges) + +class TestComputeChainQuery(NoAuthTestCase): + + def test_node_query(self): + + g = chain_graph() + + g2 = g.chain([ + n(query='n == "a"') + ]) + + assert g2._nodes.to_dict(orient='records') == [{'n': 'a'}] + assert g2._edges.to_dict(orient='records') == [] + + def test_edge_query(self): + + g = chain_graph() + + g2 = g.chain([ + e_forward(edge_query='s == "a"') + ]) + + assert g2._nodes.to_dict(orient='records') == [{'n': 'a'}, {'n': 'b'}] + assert g2._edges.to_dict(orient='records') == [{'s': 'a', 'd': 'b'}] + + def test_edge_source_query(self): + + g = chain_graph() + + g2 = g.chain([ + e_forward(source_node_query='n == "a"') + ]) + assert g2._nodes.to_dict(orient='records') == [{'n': 'a'}, {'n': 'b'}] + assert g2._edges.to_dict(orient='records') == [{'s': 'a', 'd': 'b'}] + + def test_edge_destination_query(self): + + g = chain_graph() + + g2 = g.chain([ + e_forward(destination_node_query='n == "b"') + ]) + assert g2._nodes.to_dict(orient='records') == [{'n': 'a'}, {'n': 'b'}] + assert g2._edges.to_dict(orient='records') == [{'s': 'a', 'd': 'b'}] diff --git a/graphistry/tests/test_compute_hops.py b/graphistry/tests/test_compute_hops.py index 5db33ff6c1..01ff225c3e 100644 --- a/graphistry/tests/test_compute_hops.py +++ b/graphistry/tests/test_compute_hops.py @@ -49,7 +49,6 @@ def hops_graph(): return CGFull().nodes(nodes_df, 'node').edges(edges_df, 's', 'd') - class TestComputeHopMixin(NoAuthTestCase): @@ -184,3 +183,23 @@ def test_hop_filter_types(self): def test_is_in(self): g = hops_graph() assert g.hop(source_node_match={'node': is_in(['e', 'k'])})._edges.shape == (3, 3) + +class TestComputeHopMixinQuery(NoAuthTestCase): + + def test_hop_source_query(self): + g = hops_graph() + g2 = g.hop(source_node_query='node == "d"', direction='forward', hops=1) + assert g2._nodes.shape == (6, 2) + assert g2._edges.shape == (5, 3) + + def test_hop_destination_query(self): + g = hops_graph() + g2 = g.hop(destination_node_query='node == "d"', direction='reverse', hops=1) + assert g2._nodes.shape == (6, 2) + assert g2._edges.shape == (5, 3) + + def test_hop_edge_query(self): + g = hops_graph() + g2 = g.hop(edge_query='s == "d"', direction='forward', hops=1) + assert g2._nodes.shape == (6, 2) + assert g2._edges.shape == (5, 3) From 3c31c0d35043013d9adf2c113955455ec214711a Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 17:32:19 -0800 Subject: [PATCH 051/104] docs(hop): comments --- graphistry/compute/chain.py | 9 ++++++++- graphistry/compute/hop.py | 5 +++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index 4efdc47647..5d7f70cc66 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -193,7 +193,11 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: logger.debug('============ FORWARDS ============') - #forwards + # Forwards + # This computes valid path *prefixes*, where each g nodes/edges is the path wavefront: + # g_step._nodes: The nodes reached in this step + # g_step._edges: The edges used to reach those nodes + # At the paths are prefixes, wavefront nodes may invalid wrt subsequent steps (e.g., halt early) g_stack : List[Plottable] = [] for op in ops: prev_step_nodes = ( # start from only prev step's wavefront node @@ -217,6 +221,9 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: logger.debug('============ BACKWARDS ============') + # Backwards + # Compute reverse and thus complete paths. Dropped nodes/edges are thus the incomplete path prefixes. + # Each g node/edge represents a valid wavefront entry for that step. g_stack_reverse : List[Plottable] = [] for (op, g_step) in zip(reversed(ops), reversed(g_stack)): prev_loop_step = g_stack[-1] if len(g_stack_reverse) == 0 else g_stack_reverse[-1] diff --git a/graphistry/compute/hop.py b/graphistry/compute/hop.py index c0ade90e53..edca8fa9e3 100644 --- a/graphistry/compute/hop.py +++ b/graphistry/compute/hop.py @@ -169,8 +169,9 @@ def hop(self: Plottable, + ( [ new_node_ids_forward ] if new_node_ids_forward is not None else mt ) # noqa: W503 + ( [ new_node_ids_reverse] if new_node_ids_reverse is not None else mt ), # noqa: W503 ignore_index=True, sort=False).drop_duplicates() - - # Finally add initial nodes as confirmed also match edge + post-node predicates, not just pre-node predicates + # Finally include all initial root nodes matched against, now that edge triples satisfy all source/dest/edge predicates + # Only run first iteration b/c root nodes already accounted for in subsequent + # In wavefront mode, skip, as we only want to return reached nodes if matches_nodes is None: if return_as_wave_front: matches_nodes = new_node_ids[:0] From 575c4fd909856a9c7d36cea78d312d60c601d1fd Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 17:36:13 -0800 Subject: [PATCH 052/104] fix(lint) --- graphistry/compute/chain.py | 5 ----- graphistry/compute/hop.py | 2 ++ 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index 5d7f70cc66..c8be5d12c1 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -214,11 +214,6 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: ) g_stack.append(g_step) - encountered_nodes_df = pd.concat([ - g_step._nodes - for g_step in g_stack - ]).drop_duplicates(subset=[g._node]) - logger.debug('============ BACKWARDS ============') # Backwards diff --git a/graphistry/compute/hop.py b/graphistry/compute/hop.py index edca8fa9e3..b14a7bc08a 100644 --- a/graphistry/compute/hop.py +++ b/graphistry/compute/hop.py @@ -119,6 +119,7 @@ def hop(self: Plottable, ) new_node_ids_forward = hop_edges_forward[[g2._destination]].rename(columns={g2._destination: g2._node}).drop_duplicates() + if destination_node_query is not None or destination_node_match is not None: new_node_ids_forward = query_if_not_none( destination_node_query, filter_by_dict( @@ -144,6 +145,7 @@ def hop(self: Plottable, ) new_node_ids_reverse = hop_edges_reverse[[g2._source]].rename(columns={g2._source: g2._node}).drop_duplicates() + if destination_node_query is not None or destination_node_match is not None: new_node_ids_reverse = query_if_not_none( destination_node_query, filter_by_dict( From b1bfac7b5ff5f035164fe3a1043736b7491c7802 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 17:51:59 -0800 Subject: [PATCH 053/104] refactor(is_in, astpredicate) --- CHANGELOG.md | 6 ++++++ graphistry/compute/__init__.py | 3 +-- graphistry/compute/ast.py | 5 ++++- graphistry/compute/filter_by_dict.py | 9 +-------- graphistry/compute/predicates/ASTPredicate.py | 6 ++++++ graphistry/compute/predicates/is_in.py | 9 +++++++++ graphistry/tests/test_compute_filter_by_dict.py | 3 ++- graphistry/tests/test_compute_hops.py | 2 +- 8 files changed, 30 insertions(+), 13 deletions(-) create mode 100644 graphistry/compute/predicates/ASTPredicate.py create mode 100644 graphistry/compute/predicates/is_in.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 452346e1d5..5f6e35e36a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm * chain: optional df queries: - `chain([n(query='...')])` - `chain([e_forward(..., source_node_query='...', edge_query='...', destination_node_query='...')])` +* `ASTPredicate` base class for filter matching ### Fixed @@ -22,6 +23,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm * hop: multi-hops with `source_node_match` specified was not checking intermediate hops * compute logging no longer default-overrides level to DEBUG +### Changed + +* refactor: move `is_in`, `IsIn` implementations to `graphistry.ast.predicates`; old imports preserved +* `IsIn` now implements `ASTPredicate` + ## [0.29.7 - 2023-11-02] ### Added diff --git a/graphistry/compute/__init__.py b/graphistry/compute/__init__.py index 0b26750541..cd5a814c7c 100644 --- a/graphistry/compute/__init__.py +++ b/graphistry/compute/__init__.py @@ -1,5 +1,4 @@ from .ComputeMixin import ComputeMixin from .ast import ( - n, e_forward, e_reverse, e_undirected + n, e_forward, e_reverse, e_undirected, is_in, IsIn ) -from .filter_by_dict import is_in, IsIn diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index c7edcf9eb9..a481336e7f 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -2,7 +2,9 @@ import pandas as pd from graphistry.Plottable import Plottable -from .filter_by_dict import is_in, IsIn +from .predicates.ASTPredicate import ASTPredicate +from .predicates.is_in import is_in, IsIn +from .filter_by_dict import filter_by_dict import logging logger = logging.getLogger(__name__) @@ -15,6 +17,7 @@ class ASTObject(object): """ Internal, not intended for use outside of this module. + These are operator-level expressions used as g.chain(List) """ def __init__(self, name: Optional[str] = None): self._name = name diff --git a/graphistry/compute/filter_by_dict.py b/graphistry/compute/filter_by_dict.py index 5aa9ef77fc..22c294815f 100644 --- a/graphistry/compute/filter_by_dict.py +++ b/graphistry/compute/filter_by_dict.py @@ -2,14 +2,7 @@ import pandas as pd from graphistry.Plottable import Plottable - - -class IsIn(): - def __init__(self, options: List[Any]) -> None: - self.options = options - -def is_in(options: List[Any]) -> IsIn: - return IsIn(options) +from .predicates.is_in import IsIn def filter_by_dict(df, filter_dict: Optional[dict] = None) -> pd.DataFrame: diff --git a/graphistry/compute/predicates/ASTPredicate.py b/graphistry/compute/predicates/ASTPredicate.py new file mode 100644 index 0000000000..135940e3c6 --- /dev/null +++ b/graphistry/compute/predicates/ASTPredicate.py @@ -0,0 +1,6 @@ +class ASTPredicate(): + """ + Internal, not intended for use outside of this module. + These are fancy columnar predicates used in {k: v, ...} node/edge df matching when going beyond primitive equality + """ + pass diff --git a/graphistry/compute/predicates/is_in.py b/graphistry/compute/predicates/is_in.py new file mode 100644 index 0000000000..9d337b5e94 --- /dev/null +++ b/graphistry/compute/predicates/is_in.py @@ -0,0 +1,9 @@ +from typing import Any, List +from .ASTPredicate import ASTPredicate + +class IsIn(ASTPredicate): + def __init__(self, options: List[Any]) -> None: + self.options = options + +def is_in(options: List[Any]) -> IsIn: + return IsIn(options) diff --git a/graphistry/tests/test_compute_filter_by_dict.py b/graphistry/tests/test_compute_filter_by_dict.py index 5babdd9211..570e14e865 100644 --- a/graphistry/tests/test_compute_filter_by_dict.py +++ b/graphistry/tests/test_compute_filter_by_dict.py @@ -1,7 +1,8 @@ import pandas as pd from functools import lru_cache -from graphistry.compute.filter_by_dict import filter_by_dict, is_in, IsIn +from graphistry.compute.ast import is_in, IsIn +from graphistry.compute.filter_by_dict import filter_by_dict from graphistry.tests.test_compute import CGFull @lru_cache(maxsize=1) diff --git a/graphistry/tests/test_compute_hops.py b/graphistry/tests/test_compute_hops.py index 01ff225c3e..157cebfa8e 100644 --- a/graphistry/tests/test_compute_hops.py +++ b/graphistry/tests/test_compute_hops.py @@ -2,7 +2,7 @@ from common import NoAuthTestCase from functools import lru_cache -from graphistry.compute.filter_by_dict import is_in +from graphistry.compute.ast import is_in from graphistry.tests.test_compute import CGFull @lru_cache(maxsize=1) From 66e9dbe3ce8835979d8387793f98d39c538f0ac4 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 19:24:24 -0800 Subject: [PATCH 054/104] feat(predicates): many predicates for hop/chain --- CHANGELOG.md | 9 + README.md | 33 +++ graphistry/__init__.py | 38 +++- graphistry/compute/__init__.py | 44 +++- graphistry/compute/ast.py | 43 +++- graphistry/compute/filter_by_dict.py | 20 +- graphistry/compute/predicates/ASTPredicate.py | 9 +- graphistry/compute/predicates/categorical.py | 17 ++ graphistry/compute/predicates/is_in.py | 6 + graphistry/compute/predicates/numeric.py | 121 ++++++++++ graphistry/compute/predicates/str.py | 210 ++++++++++++++++++ graphistry/compute/predicates/temporal.py | 82 +++++++ graphistry/tests/test_compute_chain.py | 2 +- .../tests/test_compute_filter_by_dict.py | 2 +- graphistry/tests/test_compute_hops.py | 2 +- 15 files changed, 621 insertions(+), 17 deletions(-) create mode 100644 graphistry/compute/predicates/categorical.py create mode 100644 graphistry/compute/predicates/numeric.py create mode 100644 graphistry/compute/predicates/str.py create mode 100644 graphistry/compute/predicates/temporal.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f6e35e36a..2120896df7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,11 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm - `chain([n(query='...')])` - `chain([e_forward(..., source_node_query='...', edge_query='...', destination_node_query='...')])` * `ASTPredicate` base class for filter matching +* Additional predicates for hop and chain match expressions: + - categorical: is_in (example above), duplicated + - temporal: is_month_start, is_month_end, is_quarter_start, is_quarter_end, is_year_start, is_year_end, is_leap_year + - numeric: gt, lt, ge, le, eq, ne, between, isna, notna + - str: contains, startswith, endswith, match, isnumeric, isalpha, isdigit, islower, isupper, isspace, isalnum, isdecimal, istitle, isnull, notnull ### Fixed @@ -28,6 +33,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm * refactor: move `is_in`, `IsIn` implementations to `graphistry.ast.predicates`; old imports preserved * `IsIn` now implements `ASTPredicate` +### Docs + +* hop/chain: new query and predicate forms + ## [0.29.7 - 2023-11-02] ### Added diff --git a/README.md b/README.md index 70c8692891..cede790c01 100644 --- a/README.md +++ b/README.md @@ -1139,6 +1139,15 @@ g2.plot() # nodes are values from cols s, d, k1 .collapse(node='some_id', column='some_col', attribute='some val') ``` +Both `hop()` and `chain()` match dictionary expressions support dataframe series *predicates*. The above examples show `is_in([x, y, z, ...])`. Additional predicates include: + +* categorical: is_in, duplicated +* temporal: is_month_start, is_month_end, is_quarter_start, is_quarter_end, is_year_start, is_year_end +* numeric: gt, lt, ge, le, eq, ne, between, isna, notna +* string: contains, startswith, endswith, match, isnumeric, isalpha, isdigit, islower, isupper, isspace, isalnum, isdecimal, istitle, isnull, notnull + + + #### Table to graph ```python @@ -1225,16 +1234,37 @@ Method `.hop()` enables slightly more complicated edge filters: ```python +from graphistry import is_in, gt + # (a)-[{"v": 1, "type": "z"}]->(b) based on g g2b = g2.hop( source_node_match={g2._node: "a"}, edge_match={"v": 1, "type": "z"}, destination_node_match={g2._node: "b"}) +g2b = g2.hop( + source_node_query='n == "a", + edge_query='v == 1 and type == "z"', + destination_node_query='n == "b"') + +# (a {x in [1,2] and y > 3})-[e]->(b) based on g +g2c = g2.hop( + source_node_match={ + g2._node: "a", + "x": is_in([1,2]), + "y": gt(3) + }, + destination_node_match={g2._node: "b"}) +) # (a or b)-[1 to 8 hops]->(anynode), based on graph g2 g3 = g2.hop(pd.DataFrame({g2._node: ['a', 'b']}), hops=8) +# (a or b)-[1 to 8 hops]->(anynode), based on graph g2 +g3 = g2.hop(pd.DataFrame({g2._node: is_in(['a', 'b'])}), hops=8) + # (c)<-[any number of hops]-(any node), based on graph g3 +# Note multihop matches check source/destination/edge match/query predicates +# against every encountered edge for it to be included g4 = g3.hop(source_node_match={"node": "c"}, direction='reverse', to_fixed_point=True) # (c)-[incoming or outgoing edge]-(any node), @@ -1251,6 +1281,7 @@ from graphistry import n, e_forward, e_reverse, e_undirected, is_in g2.chain([ n() ]) g2.chain([ n({"x": 1, "y": True}) ]), +g2.chain([ n(query='x == 1 and y == True') ]), g2.chain([ n({"z": is_in([1,2,4,'z'])}) ]), # multiple valid values g2.chain([ e_forward({"type": "x"}, hops=2) ]) # simple multi-hop g3 = g2.chain([ @@ -1264,6 +1295,8 @@ print('# end nodes: ', len(g3._nodes[ g3._nodes.end ])) print('# end edges: ', len(g3._edges[ g3._edges.final_edge ])) ``` +See table above for more predicates like `is_in()` and `gt()` + #### Pipelining ```python diff --git a/graphistry/__init__.py b/graphistry/__init__.py index 02e467afea..246fdf6cb7 100644 --- a/graphistry/__init__.py +++ b/graphistry/__init__.py @@ -51,7 +51,43 @@ from graphistry.compute import ( n, e_forward, e_reverse, e_undirected, - is_in, IsIn + + is_in, IsIn, + + duplicated, Duplicated, + + is_month_start, IsMonthStart, + is_month_end, IsMonthEnd, + is_quarter_start, IsQuarterStart, + is_quarter_end, IsQuarterEnd, + is_year_start, IsYearStart, + is_leap_year, IsLeapYear, + + gt, GT, + lt, LT, + ge, GE, + le, LE, + eq, EQ, + ne, NE, + between, Between, + isna, IsNA, + notna, NotNA, + + contains, Contains, + startswith, Startswith, + endswith, Endswith, + match, Match, + isnumeric, IsNumeric, + isalpha, IsAlpha, + isdigit, IsDigit, + islower, IsLower, + isupper, IsUpper, + isspace, IsSpace, + isalnum, IsAlnum, + isdecimal, IsDecimal, + istitle, IsTitle, + isnull, IsNull, + notnull, NotNull, ) from graphistry.Engine import Engine diff --git a/graphistry/compute/__init__.py b/graphistry/compute/__init__.py index cd5a814c7c..d321b0915e 100644 --- a/graphistry/compute/__init__.py +++ b/graphistry/compute/__init__.py @@ -1,4 +1,46 @@ from .ComputeMixin import ComputeMixin from .ast import ( - n, e_forward, e_reverse, e_undirected, is_in, IsIn + n, e_forward, e_reverse, e_undirected +) +from .predicates.is_in import ( + is_in, IsIn +) +from .predicates.categorical import ( + duplicated, Duplicated, +) +from .predicates.temporal import ( + is_month_start, IsMonthStart, + is_month_end, IsMonthEnd, + is_quarter_start, IsQuarterStart, + is_quarter_end, IsQuarterEnd, + is_year_start, IsYearStart, + is_leap_year, IsLeapYear +) +from .predicates.numeric import ( + gt, GT, + lt, LT, + ge, GE, + le, LE, + eq, EQ, + ne, NE, + between, Between, + isna, IsNA, + notna, NotNA +) +from .predicates.str import ( + contains, Contains, + startswith, Startswith, + endswith, Endswith, + match, Match, + isnumeric, IsNumeric, + isalpha, IsAlpha, + isdigit, IsDigit, + islower, IsLower, + isupper, IsUpper, + isspace, IsSpace, + isalnum, IsAlnum, + isdecimal, IsDecimal, + istitle, IsTitle, + isnull, IsNull, + notnull, NotNull, ) diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index a481336e7f..39695edc21 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -3,7 +3,48 @@ from graphistry.Plottable import Plottable from .predicates.ASTPredicate import ASTPredicate -from .predicates.is_in import is_in, IsIn +from .predicates.is_in import ( + is_in, IsIn +) +from .predicates.categorical import ( + duplicated, Duplicated, +) +from .predicates.temporal import ( + is_month_start, IsMonthStart, + is_month_end, IsMonthEnd, + is_quarter_start, IsQuarterStart, + is_quarter_end, IsQuarterEnd, + is_year_start, IsYearStart, + is_leap_year, IsLeapYear +) +from .predicates.numeric import ( + gt, GT, + lt, LT, + ge, GE, + le, LE, + eq, EQ, + ne, NE, + between, Between, + isna, IsNA, + notna, NotNA +) +from .predicates.str import ( + contains, Contains, + startswith, Startswith, + endswith, Endswith, + match, Match, + isnumeric, IsNumeric, + isalpha, IsAlpha, + isdigit, IsDigit, + islower, IsLower, + isupper, IsUpper, + isspace, IsSpace, + isalnum, IsAlnum, + isdecimal, IsDecimal, + istitle, IsTitle, + isnull, IsNull, + notnull, NotNull +) from .filter_by_dict import filter_by_dict import logging diff --git a/graphistry/compute/filter_by_dict.py b/graphistry/compute/filter_by_dict.py index 22c294815f..db59d2605d 100644 --- a/graphistry/compute/filter_by_dict.py +++ b/graphistry/compute/filter_by_dict.py @@ -1,8 +1,8 @@ -from typing import Any, Dict, List, Optional, TYPE_CHECKING +from typing import Dict, Optional import pandas as pd from graphistry.Plottable import Plottable -from .predicates.is_in import IsIn +from .predicates.ASTPredicate import ASTPredicate def filter_by_dict(df, filter_dict: Optional[dict] = None) -> pd.DataFrame: @@ -13,25 +13,25 @@ def filter_by_dict(df, filter_dict: Optional[dict] = None) -> pd.DataFrame: if filter_dict is None or filter_dict == {}: return df - ins: Dict[str, IsIn] = {} + predicates: Dict[str, ASTPredicate] = {} for col, val in filter_dict.items(): if col not in df.columns: raise ValueError(f'Key "{col}" not in columns of df, available columns are: {df.columns}') - if isinstance(val, IsIn): - ins[col] = val - filter_dict_concrete = filter_dict if not ins else { + if isinstance(val, ASTPredicate): + predicates[col] = val + filter_dict_concrete = filter_dict if not predicates else { k: v for k, v in filter_dict.items() - if not isinstance(v, IsIn) + if not isinstance(v, ASTPredicate) } if filter_dict_concrete: hits = (df[list(filter_dict_concrete)] == pd.Series(filter_dict_concrete)).all(axis=1) else: hits = df[[]].assign(x=True).x - if ins: - for col, val in ins.items(): - hits = hits & df[col].isin(val.options) + if predicates: + for col, op in predicates.items(): + hits = hits & op(df[col]) return df[hits] diff --git a/graphistry/compute/predicates/ASTPredicate.py b/graphistry/compute/predicates/ASTPredicate.py index 135940e3c6..24c2d08bc8 100644 --- a/graphistry/compute/predicates/ASTPredicate.py +++ b/graphistry/compute/predicates/ASTPredicate.py @@ -1,6 +1,13 @@ +from abc import abstractmethod +import pandas as pd + + class ASTPredicate(): """ Internal, not intended for use outside of this module. These are fancy columnar predicates used in {k: v, ...} node/edge df matching when going beyond primitive equality """ - pass + + @abstractmethod + def __call__(self, s: pd.Series) -> pd.Series: + pass diff --git a/graphistry/compute/predicates/categorical.py b/graphistry/compute/predicates/categorical.py new file mode 100644 index 0000000000..3e786d3153 --- /dev/null +++ b/graphistry/compute/predicates/categorical.py @@ -0,0 +1,17 @@ +from typing import Literal, Optional +import pandas as pd + +from .ASTPredicate import ASTPredicate + +class Duplicated(ASTPredicate): + def __init__(self, keep: Literal['first', 'last', False] = 'first') -> None: + self.keep = keep + + def __call__(self, s: pd.Series) -> pd.Series: + return s.duplicated(keep=self.keep) + +def duplicated(keep: Literal['first', 'last', False] = 'first') -> Duplicated: + """ + Return whether a given value is duplicated + """ + return Duplicated(keep=keep) diff --git a/graphistry/compute/predicates/is_in.py b/graphistry/compute/predicates/is_in.py index 9d337b5e94..77c9f2505a 100644 --- a/graphistry/compute/predicates/is_in.py +++ b/graphistry/compute/predicates/is_in.py @@ -1,9 +1,15 @@ from typing import Any, List +import pandas as pd + from .ASTPredicate import ASTPredicate + class IsIn(ASTPredicate): def __init__(self, options: List[Any]) -> None: self.options = options + + def __call__(self, s: pd.Series) -> pd.Series: + return s.isin(self.options) def is_in(options: List[Any]) -> IsIn: return IsIn(options) diff --git a/graphistry/compute/predicates/numeric.py b/graphistry/compute/predicates/numeric.py new file mode 100644 index 0000000000..d17b07bc0c --- /dev/null +++ b/graphistry/compute/predicates/numeric.py @@ -0,0 +1,121 @@ +from typing import Optional +import pandas as pd + +from .ASTPredicate import ASTPredicate + +class GT(ASTPredicate): + def __init__(self, val: float) -> None: + self.val = val + + def __call__(self, s: pd.Series) -> pd.Series: + return s > self.val + +def gt(val: float) -> GT: + """ + Return whether a given value is greater than a threshold + """ + return GT(val) + +class LT(ASTPredicate): + def __init__(self, val: float) -> None: + self.val = val + + def __call__(self, s: pd.Series) -> pd.Series: + return s < self.val + +def lt(val: float) -> LT: + """ + Return whether a given value is less than a threshold + """ + return LT(val) + +class GE(ASTPredicate): + def __init__(self, val: float) -> None: + self.val = val + + def __call__(self, s: pd.Series) -> pd.Series: + return s >= self.val + +def ge(val: float) -> GE: + """ + Return whether a given value is greater than or equal to a threshold + """ + return GE(val) + +class LE(ASTPredicate): + def __init__(self, val: float) -> None: + self.val = val + + def __call__(self, s: pd.Series) -> pd.Series: + return s <= self.val + +def le(val: float) -> LE: + """ + Return whether a given value is less than or equal to a threshold + """ + return LE(val) + +class EQ(ASTPredicate): + def __init__(self, val: float) -> None: + self.val = val + + def __call__(self, s: pd.Series) -> pd.Series: + return s == self.val + +def eq(val: float) -> EQ: + """ + Return whether a given value is equal to a threshold + """ + return EQ(val) + +class NE(ASTPredicate): + def __init__(self, val: float) -> None: + self.val = val + + def __call__(self, s: pd.Series) -> pd.Series: + return s != self.val + +def ne(val: float) -> NE: + """ + Return whether a given value is not equal to a threshold + """ + return NE(val) + +class Between(ASTPredicate): + def __init__(self, lower: float, upper: float, inclusive: bool = True) -> None: + self.lower = lower + self.upper = upper + self.inclusive = inclusive + + def __call__(self, s: pd.Series) -> pd.Series: + if self.inclusive: + return (s >= self.lower) & (s <= self.upper) + else: + return (s > self.lower) & (s < self.upper) + +def between(lower: float, upper: float, inclusive: bool = True) -> Between: + """ + Return whether a given value is between a lower and upper threshold + """ + return Between(lower, upper, inclusive) + +class IsNA(ASTPredicate): + def __call__(self, s: pd.Series) -> pd.Series: + return s.isna() + +def isna() -> IsNA: + """ + Return whether a given value is NA + """ + return IsNA() + + +class NotNA(ASTPredicate): + def __call__(self, s: pd.Series) -> pd.Series: + return s.notna() + +def notna() -> NotNA: + """ + Return whether a given value is not NA + """ + return NotNA() diff --git a/graphistry/compute/predicates/str.py b/graphistry/compute/predicates/str.py new file mode 100644 index 0000000000..14a8ae2de5 --- /dev/null +++ b/graphistry/compute/predicates/str.py @@ -0,0 +1,210 @@ +from typing import Optional +import pandas as pd + +from .ASTPredicate import ASTPredicate + + +class Contains(ASTPredicate): + def __init__(self, pat: str, case: bool = True, flags: int = 0, na: Optional[bool] = None, regex: bool = True) -> None: + self.pat = pat + self.case = case + self.flags = flags + self.na = na + self.regex = regex + + def __call__(self, s: pd.Series) -> pd.Series: + return s.str.contains(self.pat, self.case, self.flags, self.na, self.regex) + +def contains(pat: str, case: bool = True, flags: int = 0, na: Optional[bool] = None, regex: bool = True) -> Contains: + """ + Return whether a given pattern or regex is contained within a string + """ + return Contains(pat, case, flags, na, regex) + + +class Startswith(ASTPredicate): + def __init__(self, pat: str, na: Optional[str] = None) -> None: + self.pat = pat + self.na = na + + def __call__(self, s: pd.Series) -> pd.Series: + return s.str.startswith(self.pat, self.na) + +def startswith(pat: str, na: Optional[str] = None) -> Startswith: + """ + Return whether a given pattern is at the start of a string + """ + return Startswith(pat, na) + +class Endswith(ASTPredicate): + def __init__(self, pat: str, na: Optional[str] = None) -> None: + self.pat = pat + self.na = na + + def __call__(self, s: pd.Series) -> pd.Series: + """ + Return whether a given pattern is at the end of a string + """ + return s.str.endswith(self.pat, self.na) + +def endswith(pat: str, na: Optional[str] = None) -> Endswith: + return Endswith(pat, na) + +class Match(ASTPredicate): + def __init__(self, pat: str, case: bool = True, flags: int = 0, na: Optional[bool] = None) -> None: + self.pat = pat + self.case = case + self.flags = flags + self.na = na + + def __call__(self, s: pd.Series) -> pd.Series: + return s.str.match(self.pat, self.case, self.flags, self.na) + +def match(pat: str, case: bool = True, flags: int = 0, na: Optional[bool] = None) -> Match: + """ + Return whether a given pattern is at the start of a string + """ + return Match(pat, case, flags, na) + +class IsNumeric(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.str.isnumeric() + +def isnumeric() -> IsNumeric: + """ + Return whether a given string is numeric + """ + return IsNumeric() + +class IsAlpha(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.str.isalpha() + +def isalpha() -> IsAlpha: + """ + Return whether a given string is alphabetic + """ + return IsAlpha() + +class IsDigit(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.str.isdigit() + +def isdigit() -> IsDigit: + """ + Return whether a given string is numeric + """ + return IsDigit() + +class IsLower(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.str.islower() + +def islower() -> IsLower: + """ + Return whether a given string is lowercase + """ + return IsLower() + +class IsUpper(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.str.isupper() + +def isupper() -> IsUpper: + """ + Return whether a given string is uppercase + """ + return IsUpper() + +class IsSpace(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.str.isspace() + +def isspace() -> IsSpace: + """ + Return whether a given string is whitespace + """ + return IsSpace() + +class IsAlnum(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.str.isalnum() + +def isalnum() -> IsAlnum: + """ + Return whether a given string is alphanumeric + """ + return IsAlnum() + +class IsDecimal(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.str.isdecimal() + +def isdecimal() -> IsDecimal: + """ + Return whether a given string is decimal + """ + return IsDecimal() + +class IsTitle(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.str.istitle() + +def istitle() -> IsTitle: + """ + Return whether a given string is title case + """ + return IsTitle() + +class IsNull(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.isnull() + +def isnull() -> IsNull: + """ + Return whether a given string is null + """ + return IsNull() + +class NotNull(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.notnull() + +def notnull() -> NotNull: + """ + Return whether a given string is not null + """ + return NotNull() diff --git a/graphistry/compute/predicates/temporal.py b/graphistry/compute/predicates/temporal.py new file mode 100644 index 0000000000..b18984fe97 --- /dev/null +++ b/graphistry/compute/predicates/temporal.py @@ -0,0 +1,82 @@ +from typing import Optional +import pandas as pd + +from .ASTPredicate import ASTPredicate + +class IsMonthStart(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.dt.is_month_start + +def is_month_start() -> IsMonthStart: + """ + Return whether a given value is a month start + """ + return IsMonthStart() + +class IsMonthEnd(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.dt.is_month_end + +def is_month_end() -> IsMonthEnd: + """ + Return whether a given value is a month end + """ + return IsMonthEnd() + +class IsQuarterStart(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.dt.is_quarter_start + +def is_quarter_start() -> IsQuarterStart: + """ + Return whether a given value is a quarter start + """ + return IsQuarterStart() + +class IsQuarterEnd(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.dt.is_quarter_end + +def is_quarter_end() -> IsQuarterEnd: + """ + Return whether a given value is a quarter end + """ + return IsQuarterEnd() + +class IsYearStart(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.dt.is_year_start + +def is_year_start() -> IsYearStart: + """ + Return whether a given value is a year start + """ + return IsYearStart() + +class IsLeapYear(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.dt.is_leap_year + +def is_leap_year() -> IsLeapYear: + """ + Return whether a given value is a leap year + """ + return IsLeapYear() diff --git a/graphistry/tests/test_compute_chain.py b/graphistry/tests/test_compute_chain.py index 55f112b840..8d92852447 100644 --- a/graphistry/tests/test_compute_chain.py +++ b/graphistry/tests/test_compute_chain.py @@ -121,7 +121,7 @@ def test_chain_named(self): assert sorted(g2._edges[ g2._edges.e2 ][g2._destination].to_list()) == ["a", "b"] assert sorted(g2._nodes[ g2._nodes.n2 ][g2._node].to_list()) == ["a", "b"] - def test_chain_is_in(self): + def test_chain_predicate_is_in(self): g = hops_graph() assert g.chain([n({'node': is_in(['e', 'k'])})])._nodes.shape == (2, 2) diff --git a/graphistry/tests/test_compute_filter_by_dict.py b/graphistry/tests/test_compute_filter_by_dict.py index 570e14e865..07224a23bb 100644 --- a/graphistry/tests/test_compute_filter_by_dict.py +++ b/graphistry/tests/test_compute_filter_by_dict.py @@ -108,7 +108,7 @@ def test_kv_multiple_bad(self): g = hops_graph() assert g.filter_edges_by_dict({'i': -100, 'type': 'e'})._edges.equals(g._edges[:0]) -class TestIsIn(object): +class TestPredicateIsIn(object): def test_standalone(self): g = hops_graph() diff --git a/graphistry/tests/test_compute_hops.py b/graphistry/tests/test_compute_hops.py index 157cebfa8e..cf2c8e6b02 100644 --- a/graphistry/tests/test_compute_hops.py +++ b/graphistry/tests/test_compute_hops.py @@ -180,7 +180,7 @@ def test_hop_filter_types(self): assert g5a._nodes.shape == (2, 2) assert g5a._edges.shape == (1, 3) - def test_is_in(self): + def test_predicate_is_in(self): g = hops_graph() assert g.hop(source_node_match={'node': is_in(['e', 'k'])})._edges.shape == (3, 3) From 29a2d27f29bcdd8c8ae7c0f06b138fe41122177c Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 19:27:09 -0800 Subject: [PATCH 055/104] fix(py 3.7): use old Literal import --- graphistry/compute/predicates/categorical.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphistry/compute/predicates/categorical.py b/graphistry/compute/predicates/categorical.py index 3e786d3153..bcc08c7c84 100644 --- a/graphistry/compute/predicates/categorical.py +++ b/graphistry/compute/predicates/categorical.py @@ -1,4 +1,4 @@ -from typing import Literal, Optional +from typing_extensions import Literal import pandas as pd from .ASTPredicate import ASTPredicate From 5b3795c773b29edd1426a84293a3b7d024db262b Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 19:52:41 -0800 Subject: [PATCH 056/104] fix(modules): prediciates __init__.py --- graphistry/compute/predicates/__init__.py | 1 + 1 file changed, 1 insertion(+) create mode 100644 graphistry/compute/predicates/__init__.py diff --git a/graphistry/compute/predicates/__init__.py b/graphistry/compute/predicates/__init__.py new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/graphistry/compute/predicates/__init__.py @@ -0,0 +1 @@ + From 053f66544c43571c0207e6878fe1c97bf3297ab8 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 22:17:51 -0800 Subject: [PATCH 057/104] docs(hop and chain): tutorial --- CHANGELOG.md | 1 + README.md | 23 + .../hop_and_chain_graph_pattern_mining.ipynb | 2736 +++++++++++++++++ 3 files changed, 2760 insertions(+) create mode 100644 demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb diff --git a/CHANGELOG.md b/CHANGELOG.md index 2120896df7..af4b890248 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Docs * hop/chain: new query and predicate forms +* hop/chain graph pattern mining tutorial: [ipynb demo](demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb) ## [0.29.7 - 2023-11-02] diff --git a/README.md b/README.md index cede790c01..0f0cefe3e0 100644 --- a/README.md +++ b/README.md @@ -147,6 +147,25 @@ It is easy to turn arbitrary data into insightful graphs. PyGraphistry comes wit g2.plot() ``` +* Cypher-style graph pattern mining queries on dataframes ([ipynb demo](demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb)) + + Run Cypher-style graph queries natively on dataframes without going to a database or Java: + + ```python + from graphistry import n, e_undirected, is_in + + g2 = g.chain([ + n({'user': 'Biden'}), + e_undirected(), + n(name='bridge'), + e_undirected(), + n({'user': is_in(['Trump', 'Obama'])}) + ]) + + print('# bridges', len(g2._nodes[g2._nodes.bridge])) + g2.plot() + ``` + * [Spark](https://spark.apache.org/)/[Databricks](https://databricks.com/) ([ipynb demo](demos/demos_databases_apis/databricks_pyspark/graphistry-notebook-dashboard.ipynb), [dbc demo](demos/demos_databases_apis/databricks_pyspark/graphistry-notebook-dashboard.dbc)) ```python @@ -1218,6 +1237,10 @@ assert 'pagerank' in g2._nodes.columns #### Graph pattern matching +PyGraphistry supports a PyData-native variant of the popular Cypher graph query language, meaning you can do graph pattern matching directly from Pandas dataframes without installing a database or Java + +See also [graph pattern matching tutorial](demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb) + Traverse within a graph, or expand one graph against another Simple node and edge filtering via `filter_edges_by_dict()` and `filter_nodes_by_dict()`: diff --git a/demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb b/demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb new file mode 100644 index 0000000000..af8024a9eb --- /dev/null +++ b/demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb @@ -0,0 +1,2736 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# Hop-and-chain: PyGraphistry Cypher-style graph pattern matching on dataframes\n", + "\n", + "PyGraphistry supports a rich subset of the popular Cypher graph query language, which you can run on dataframes without needing to install a database nor native libraries. It is natively integrated with dataframes and thus has a Python-native syntax rather than the traditional string syntax.\n", + "\n", + "**PyGraphistry graph pattern matching features key similarities with Cypher**\n", + "\n", + "* Multi-hop searching\n", + "* Predicates on node and edge attributes\n", + "* Ability to identify matching nodes and edges\n", + "\n", + "**It is different in a few key ways**\n", + "\n", + "* Pure PyData (Python/C++/Fortran): No need to install databases, Java, etc., `pip install pygraphistry` is enough\n", + "* It is collection-oriented rather than path-oriented: All operations are guaranteed to translate to efficiently vectorized dataframe operations rather than asymptotically slower per-row path operations typical of traditional graph query engines\n", + "* Advanced users can insert custom predicates as native Python dataframe code\n", + "\n", + "---\n", + "\n", + "# Tutorial:\n", + "\n", + "1. Install & configure\n", + "1. Load & enrich a US congress twitter interaction dataset\n", + "1. Simple graph filtering: `g.hop()` and `g.chain([...])`\n", + "1. Multi-hop and paths-between-nodes pattern mining\n", + "1. Advanced filter predicates\n", + "1. Result labeling\n", + "\n" + ], + "metadata": { + "id": "GZxoiU8sQDk_" + } + }, + { + "cell_type": "markdown", + "source": [ + "# 1. Install & configure" + ], + "metadata": { + "id": "QQpsrtwBT7sa" + } + }, + { + "cell_type": "code", + "source": [ + "#! pip install graphistry[igraph]" + ], + "metadata": { + "id": "cYjRbgkU9Sx8" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Imports" + ], + "metadata": { + "id": "Ff6Tt9DhkePl" + } + }, + { + "cell_type": "code", + "source": [ + "import pandas as pd\n", + "\n", + "import graphistry\n", + "\n", + "from graphistry import (\n", + "\n", + " # graph operators\n", + " n, e_undirected, e_forward, e_reverse,\n", + "\n", + " # attribute predicates\n", + " is_in, ge, startswith, contains, match as match_re\n", + ")" + ], + "metadata": { + "id": "S5_y0CbLkjft" + }, + "execution_count": 141, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "graphistry.register(api=3, username='...', password='...')" + ], + "metadata": { + "id": "GQ83i-sKUaw9" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# 2. Load & enrich a US congress twitter interaction dataset" + ], + "metadata": { + "id": "eU9SyauNUHtR" + } + }, + { + "cell_type": "markdown", + "source": [ + "## Data\n", + "\n", + "* Download\n", + "* Turn json into a Pandas edges dataframe\n", + "* Turn edges dataframe into a PyGraphistry graph\n", + "* Enrich nodes and edges with some useful graph metrics\n", + "* Visualize full graph to test" + ], + "metadata": { + "id": "AM9JhnaQkRd3" + } + }, + { + "cell_type": "code", + "source": [ + "! wget -q https://snap.stanford.edu/data/congress_network.zip\n", + "! unzip congress_network.zip\n" + ], + "metadata": { + "id": "55xeNAyDXhAm", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "287758f0-0df2-49ff-ecdc-283313f7e07a" + }, + "execution_count": 9, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "total 1.2M\n", + "drwxr-xr-x 1 root root 4.0K Dec 4 03:56 .\n", + "drwxr-xr-x 1 root root 4.0K Dec 4 03:33 ..\n", + "-rw-r--r-- 1 root root 150K May 9 2017 Attribute\n", + "-rw-r--r-- 1 root root 14K May 9 2017 Class_info\n", + "drwxr-xr-x 4 root root 4.0K Nov 30 14:24 .config\n", + "-rw-r--r-- 1 root root 190K Aug 5 05:26 congress_network.zip\n", + "-rw-r--r-- 1 root root 320K May 9 2017 edgelist\n", + "drwxr-xr-x 1 root root 4.0K Nov 30 14:27 sample_data\n", + "-rw-r--r-- 1 root root 16 May 9 2017 Statistics\n", + "-rw-r--r-- 1 root root 221K Dec 4 03:53 twitter.zip\n", + "-rw-r--r-- 1 root root 299K May 9 2017 vertex2aid\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import json\n", + "\n", + "with open('congress_network/congress_network_data.json', 'r') as file:\n", + " data = json.load(file)\n", + "\n", + "edges = []\n", + "for i, name in enumerate(data[0]['usernameList']):\n", + " for ii, j in enumerate(data[0]['outList'][i]):\n", + " edges.append({\n", + " 'from': name,\n", + " 'to': names[j],\n", + " 'weight': data[0]['outWeight'][i][ii]\n", + " })\n", + "edges_df = pd.DataFrame(edges)\n", + "\n", + "print(edges_df.shape)\n", + "edges_df.sample(5)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 224 + }, + "id": "6CmULn4N-8oh", + "outputId": "61a1a4cf-dfe1-4260-a427-46009f4e4aaf" + }, + "execution_count": 40, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(13289, 3)\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " from to weight\n", + "11112 RepBobbyRush janschakowsky 0.034364\n", + "3836 RepCori Ilhan 0.015936\n", + "5282 RepTedDeutch RepDWStweets 0.003268\n", + "12352 BennieGThompson RepStricklandWA 0.006849\n", + "9358 RepCarolMiller RepTroyNehls 0.005291" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
fromtoweight
11112RepBobbyRushjanschakowsky0.034364
3836RepCoriIlhan0.015936
5282RepTedDeutchRepDWStweets0.003268
12352BennieGThompsonRepStricklandWA0.006849
9358RepCarolMillerRepTroyNehls0.005291
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 40 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Load dataframe as a PyGraphistry graph\n", + "\n", + "Turn into a graph and precompute some useful graph metrics\n", + "\n", + "Recall that a `g` object, underneath, is essentially just two dataframes, `g._edges` and `g._nodes`, and with many useful graph methods:" + ], + "metadata": { + "id": "XLFTgDTEDSeA" + } + }, + { + "cell_type": "code", + "source": [ + "# Shape\n", + "g = graphistry.edges(edges_df, 'from', 'to')\n", + "\n", + "# Enrich & style\n", + "# Tip: Switch from compute_igraph to compute_cugraph when GPUs are available\n", + "g2 = (g\n", + " .materialize_nodes()\n", + " .nodes(lambda g: g._nodes.assign(title=g._nodes.id))\n", + " .edges(lambda g: g._edges.assign(weight2=g._edges.weight))\n", + " .bind(point_title='title')\n", + " .compute_igraph('community_infomap')\n", + " .compute_igraph('pagerank')\n", + " .get_degrees()\n", + " .encode_point_color(\n", + " 'community_infomap',\n", + " as_categorical=True,\n", + " categorical_mapping={\n", + " 0: '#32a9a2', # vibrant teal\n", + " 1: '#ff6b6b', # soft coral\n", + " 2: '#f9d342', # muted yellow\n", + " }\n", + " )\n", + ")\n", + "\n", + "g2._nodes" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 461 + }, + "id": "aB1U7e0HXmHh", + "outputId": "53b9fa91-0caf-4866-c5a9-d9cf80e3c9ac" + }, + "execution_count": 77, + "outputs": [ + { + "output_type": "stream", + "name": "stderr", + "text": [ + "WARNING:root:edge index g._edge not set so using edge index as ID; set g._edge via g.edges(), or change merge_if_existing to FalseWARNING:root:edge index g._edge __edge_index__ missing as attribute in ig; using ig edge order for IDsWARNING:root:edge index g._edge not set so using edge index as ID; set g._edge via g.edges(), or change merge_if_existing to FalseWARNING:root:edge index g._edge __edge_index__ missing as attribute in ig; using ig edge order for IDs" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " id title community_infomap pagerank degree_in \\\n", + "0 SenatorBaldwin SenatorBaldwin 0 0.001422 26 \n", + "1 SenJohnBarrasso SenJohnBarrasso 0 0.001179 22 \n", + "2 SenatorBennet SenatorBennet 0 0.001995 33 \n", + "3 MarshaBlackburn MarshaBlackburn 0 0.001331 18 \n", + "4 SenBlumenthal SenBlumenthal 0 0.001672 30 \n", + ".. ... ... ... ... ... \n", + "470 RepJoeWilson RepJoeWilson 1 0.001780 21 \n", + "471 RobWittman RobWittman 1 0.001017 13 \n", + "472 rep_stevewomack rep_stevewomack 1 0.002637 35 \n", + "473 RepJohnYarmuth RepJohnYarmuth 2 0.000555 5 \n", + "474 RepLeeZeldin RepLeeZeldin 1 0.000511 3 \n", + "\n", + " degree_out degree \n", + "0 20 46 \n", + "1 19 41 \n", + "2 22 55 \n", + "3 38 56 \n", + "4 35 65 \n", + ".. ... ... \n", + "470 38 59 \n", + "471 19 32 \n", + "472 19 54 \n", + "473 20 25 \n", + "474 25 28 \n", + "\n", + "[475 rows x 7 columns]" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idtitlecommunity_infomappagerankdegree_indegree_outdegree
0SenatorBaldwinSenatorBaldwin00.001422262046
1SenJohnBarrassoSenJohnBarrasso00.001179221941
2SenatorBennetSenatorBennet00.001995332255
3MarshaBlackburnMarshaBlackburn00.001331183856
4SenBlumenthalSenBlumenthal00.001672303565
........................
470RepJoeWilsonRepJoeWilson10.001780213859
471RobWittmanRobWittman10.001017131932
472rep_stevewomackrep_stevewomack10.002637351954
473RepJohnYarmuthRepJohnYarmuth20.00055552025
474RepLeeZeldinRepLeeZeldin10.00051132528
\n", + "

475 rows × 7 columns

\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 77 + } + ] + }, + { + "cell_type": "code", + "source": [ + "g2.plot()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 543 + }, + "id": "GY9Q7KyqBMq8", + "outputId": "5b4b277e-17fd-4201-9518-25168b927c6f" + }, + "execution_count": 79, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ] + }, + "metadata": {}, + "execution_count": 79 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 3. Simple filtering: `g.hop()` & `g.chain([...])`\n", + "\n", + "We can filter by nodes, edges, and combinations of them\n", + "\n", + "The result is a graph where we can inspect the node and edge tables, or perform further graph operations, like visualization or further searches\n", + "\n", + "**Key concepts**\n", + "\n", + "There are 2 key methods:\n", + "* `g.hop(...)`: filter triples of source node, edge, destination node\n", + "* `g.chain([....])`: arbitrarily long sequence of node and edge predicates\n", + "\n", + "They reuse column operations core to dataframe libraries, such as comparison operators on strings, numbers, and dates\n", + "\n", + "**Sample tasks**\n", + "\n", + "This section shows how to:\n", + "\n", + "* Find SenSchumer and his immediate community (infomap metric)\n", + "* Look at his entire community\n", + "* Find everyone with high edge weight from/to SenSchumer; 2 hops either direction\n", + "* Find everyone in his community\n", + "\n" + ], + "metadata": { + "id": "2sB-Mi7qkrM0" + } + }, + { + "cell_type": "code", + "source": [ + "g2.chain([n({'title': 'SenSchumer'})])._nodes" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 81 + }, + "id": "ydS6ZUiUqx8E", + "outputId": "825b0c85-e3c9-4453-abed-1b252cf804d1" + }, + "execution_count": 80, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " id title community_infomap pagerank degree_in degree_out \\\n", + "0 SenSchumer SenSchumer 2 0.001296 25 97 \n", + "\n", + " degree \n", + "0 122 " + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
idtitlecommunity_infomappagerankdegree_indegree_outdegree
0SenSchumerSenSchumer20.0012962597122
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 80 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "You can also pass `chain()` a sequence of node and edge expressions" + ], + "metadata": { + "id": "5vGMFFveUyLK" + } + }, + { + "cell_type": "code", + "source": [ + "g_immediate_community2 = g2.chain([n({'title': 'SenSchumer'}), e_undirected(), n({'community_infomap': 2})])\n", + "\n", + "print(len(g_immediate_community2._nodes), 'senators', len(g_immediate_community2._edges), 'relns')\n", + "g_immediate_community2._edges[['from', 'to', 'weight2']].sort_values(by=['weight2']).head(10)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 380 + }, + "id": "I1L0ZRTrF_HH", + "outputId": "9ded72c2-694b-40a4-f55a-ee86b51f290d" + }, + "execution_count": 81, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "58 senators 69 relns\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " from to weight2\n", + "22 SenSchumer JacksonLeeTX18 0.001546\n", + "46 SenSchumer RepSarbanes 0.001546\n", + "23 SenSchumer RepJayapal 0.001546\n", + "53 SenSchumer PeterWelch 0.001546\n", + "25 SenSchumer RepDaveJoyce 0.001546\n", + "26 SenSchumer RepRobinKelly 0.001546\n", + "28 SenSchumer RepAndyKimNJ 0.001546\n", + "29 SenSchumer RepBarbaraLee 0.001546\n", + "50 SenSchumer RepPaulTonko 0.001546\n", + "32 SenSchumer RepMeijer 0.001546" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
fromtoweight2
22SenSchumerJacksonLeeTX180.001546
46SenSchumerRepSarbanes0.001546
23SenSchumerRepJayapal0.001546
53SenSchumerPeterWelch0.001546
25SenSchumerRepDaveJoyce0.001546
26SenSchumerRepRobinKelly0.001546
28SenSchumerRepAndyKimNJ0.001546
29SenSchumerRepBarbaraLee0.001546
50SenSchumerRepPaulTonko0.001546
32SenSchumerRepMeijer0.001546
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 81 + } + ] + }, + { + "cell_type": "code", + "source": [ + "g_immediate_community2.plot()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 543 + }, + "id": "3oR6PhBAGVdX", + "outputId": "3a2e4fa7-eb73-4a16-efa2-eb5b43bd929f" + }, + "execution_count": 82, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ] + }, + "metadata": {}, + "execution_count": 82 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Often, we are just filtering on a src node / edge / dst node triple, so `hop()` is a short-form for this. All the `hop()` parameters can also be passed to edge expressions as well." + ], + "metadata": { + "id": "hRWJkIFAU5t_" + } + }, + { + "cell_type": "code", + "source": [ + "g_community2 = g2.hop(source_node_match={'community_infomap': 2}, destination_node_match={'community_infomap': 2})\n", + "\n", + "print(len(g_community2._nodes), 'senators', len(g_community2._edges), 'relns')\n", + "g_community2._edges.sort_values(by=['weight2']).head(10)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 380 + }, + "id": "5T5UWt8kFPuZ", + "outputId": "fd709a7d-a697-40d5-c34b-354eea84b72f" + }, + "execution_count": 83, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "214 senators 4993 relns\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " from to weight weight2\n", + "378 RepDonBeyer RepSpeier 0.000658 0.000658\n", + "354 RepDonBeyer repcleaver 0.000658 0.000658\n", + "353 RepDonBeyer RepYvetteClarke 0.000658 0.000658\n", + "352 RepDonBeyer RepCasten 0.000658 0.000658\n", + "349 RepDonBeyer RepBeatty 0.000658 0.000658\n", + "360 RepDonBeyer RepGaramendi 0.000658 0.000658\n", + "361 RepDonBeyer RepChuyGarcia 0.000658 0.000658\n", + "362 RepDonBeyer RepRaulGrijalva 0.000658 0.000658\n", + "365 RepDonBeyer USRepKeating 0.000658 0.000658\n", + "366 RepDonBeyer RepRickLarsen 0.000658 0.000658" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
fromtoweightweight2
378RepDonBeyerRepSpeier0.0006580.000658
354RepDonBeyerrepcleaver0.0006580.000658
353RepDonBeyerRepYvetteClarke0.0006580.000658
352RepDonBeyerRepCasten0.0006580.000658
349RepDonBeyerRepBeatty0.0006580.000658
360RepDonBeyerRepGaramendi0.0006580.000658
361RepDonBeyerRepChuyGarcia0.0006580.000658
362RepDonBeyerRepRaulGrijalva0.0006580.000658
365RepDonBeyerUSRepKeating0.0006580.000658
366RepDonBeyerRepRickLarsen0.0006580.000658
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 83 + } + ] + }, + { + "cell_type": "code", + "source": [ + "g_community2.encode_point_color('pagerank', ['blue', 'yellow', 'red'], as_continuous=True).plot()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 543 + }, + "id": "uMnOYQypG-KH", + "outputId": "a498472b-3fe5-4e1c-f9c5-b7b45515b63f" + }, + "execution_count": 86, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ] + }, + "metadata": {}, + "execution_count": 86 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 4. Multi-hop and paths-between-nodes pattern mining\n", + "\n", + "Method `chain([...])` can be used for looking more than one hop out, and even finding paths between nodes." + ], + "metadata": { + "id": "o6yJ48oxKPiz" + } + }, + { + "cell_type": "markdown", + "source": [ + "Ex: All people bridging SenSchumer and SpeakerPelosi" + ], + "metadata": { + "id": "fT4y7fH4KzAr" + } + }, + { + "cell_type": "code", + "source": [ + "g_shumer_pelosi_bridges = g2.chain([\n", + " n({'title': 'SenSchumer'}),\n", + " e_undirected(),\n", + " n(),\n", + " e_undirected(),\n", + " n({'title': 'SpeakerPelosi'})\n", + "])\n", + "\n", + "print(len(g_shumer_pelosi_bridges._nodes), 'senators')\n", + "g_shumer_pelosi_bridges._edges.sort_values(by='weight').head(5)" + ], + "metadata": { + "id": "tItpDvqCBjvC", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 224 + }, + "outputId": "5f701cb9-8e0a-4495-bfbd-baff50466ae9" + }, + "execution_count": 94, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "66 senators\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " from to weight weight2\n", + "86 RepJayapal SpeakerPelosi 0.000871 0.000871\n", + "47 SenSchumer RepMeijer 0.001546 0.001546\n", + "23 SenSchumer RepBuddyCarter 0.001546 0.001546\n", + "24 SenSchumer RepJudyChu 0.001546 0.001546\n", + "26 SenSchumer repcleaver 0.001546 0.001546" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
fromtoweightweight2
86RepJayapalSpeakerPelosi0.0008710.000871
47SenSchumerRepMeijer0.0015460.001546
23SenSchumerRepBuddyCarter0.0015460.001546
24SenSchumerRepJudyChu0.0015460.001546
26SenSchumerrepcleaver0.0015460.001546
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 94 + } + ] + }, + { + "cell_type": "code", + "source": [ + "g_shumer_pelosi_bridges.plot()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 543 + }, + "id": "L_YmDjeRJzcw", + "outputId": "c1abf11a-e48c-43ce-8087-a56052f3097c" + }, + "execution_count": 92, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ] + }, + "metadata": {}, + "execution_count": 92 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 5. Advanced filter predicates\n", + "\n", + "We can use a variety of predicates for filtering nodes and edges beyond attribute value equality.\n", + "\n", + "Common tasks include comparing attributes using:\n", + "* Set inclusion: `is_in([...])`\n", + "* Numeric comparisons: `gt(...)`, `lt(...)`, `ge(...)`, `le(...)`\n", + "* String comparison: `startswith(...)`, `endswith(...)`, `contains(...)`\n", + "* Regular expression matching: `matches(...)`\n", + "* Duplicate checking: `duplicated()`" + ], + "metadata": { + "id": "dbRgj3qxLU5I" + } + }, + { + "cell_type": "markdown", + "source": [ + "Graph where nodes are in the top 20 pagerank:" + ], + "metadata": { + "id": "z69kYi3wMMHK" + } + }, + { + "cell_type": "code", + "source": [ + "top_20_pr = g2._nodes.pagerank.sort_values(ascending=False, ignore_index=True)[19]\n", + "top_20_pr" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "tHkjtTw-MVuA", + "outputId": "d377eef1-8a2b-484a-b190-e95491eef4c2" + }, + "execution_count": 134, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "0.005888600097034367" + ] + }, + "metadata": {}, + "execution_count": 134 + } + ] + }, + { + "cell_type": "code", + "source": [ + "g_high_pr = g2.chain([\n", + " n({'pagerank': ge(top_20_pr)}),\n", + " e_undirected(),\n", + " n({'pagerank': ge(top_20_pr)}),\n", + "])\n", + "\n", + "len(g_high_pr._nodes)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "wDg9pUyJMR3V", + "outputId": "7ba923cd-5faa-431e-8f8f-8da223a28a39" + }, + "execution_count": 128, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "20" + ] + }, + "metadata": {}, + "execution_count": 128 + } + ] + }, + { + "cell_type": "code", + "source": [ + "g_high_pr.plot()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 543 + }, + "id": "FVc-Mou-M6TO", + "outputId": "68ea6daa-b75b-46ad-cc54-010512aab919" + }, + "execution_count": 129, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ] + }, + "metadata": {}, + "execution_count": 129 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Graph where the name includes Leader" + ], + "metadata": { + "id": "MDHyapjHOY-b" + } + }, + { + "cell_type": "code", + "source": [ + "g_leaders = g2.hop(\n", + " source_node_match={'title': contains('Leader')},\n", + " destination_node_match = {'title': contains('Leader')}\n", + ")\n", + "\n", + "print(len(g_leaders._nodes), 'leaders')\n", + "\n", + "g_leaders.plot()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 561 + }, + "id": "mwkKlqhoO29-", + "outputId": "9f3329f1-08f5-4d8f-c203-c049e59a101a" + }, + "execution_count": 136, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "2 leaders\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ] + }, + "metadata": {}, + "execution_count": 136 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "Graph of leaders and senators" + ], + "metadata": { + "id": "g--y65A0PHNJ" + } + }, + { + "cell_type": "code", + "source": [ + "g_leaders_and_senators = g2.hop(\n", + " source_node_match={'title': match_re(r'Sen|Leader')},\n", + " destination_node_match = {'title': match_re(r'Sen|Leader')}\n", + ")\n", + "\n", + "print(len(g_leaders_and_senators._nodes), 'leaders and senators')\n", + "\n", + "g_leaders_and_senators.plot()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 561 + }, + "id": "mBfeLkA_Ol2i", + "outputId": "e0353e9e-34c2-4fc5-a756-b0a9260e1edb" + }, + "execution_count": 139, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "67 leaders and senators\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ] + }, + "metadata": {}, + "execution_count": 139 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 6. Result labeling\n", + "\n", + "It can be useful to name node and edges within the path query for downstream reasoning:" + ], + "metadata": { + "id": "DuxWkZFVV9-R" + } + }, + { + "cell_type": "code", + "source": [ + "g_bridges2 = g2.chain([\n", + " n({'title': 'SenSchumer'}),\n", + " e_undirected(name='from_schumer'),\n", + " n(name='found_bridge'),\n", + " e_undirected(name='from_pelosi'),\n", + " n({'title': 'SpeakerPelosi'})\n", + "])\n", + "\n", + "print(len(g_bridges2._nodes), 'senators in full graph')\n", + "\n", + "named = g_bridges2._nodes[ g_bridges2._nodes.found_bridge ]\n", + "print(len(named), 'bridging senators')\n", + "edges = g_bridges2._edges\n", + "print(len(edges[edges.from_schumer]), 'relns from_schumer', len(edges[edges.from_pelosi]), 'relns from_pelosi')\n", + "\n", + "g_bridges2.encode_point_color(\n", + " 'found_bridge',\n", + " as_categorical=True,\n", + " categorical_mapping={\n", + " True: 'orange',\n", + " False: 'silver'\n", + " }\n", + ").plot()" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 595 + }, + "id": "6G4nnclNPpY8", + "outputId": "5139bef8-45ba-4ae9-cadb-9da856eb6bc8" + }, + "execution_count": 156, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "66 senators in full graph\n", + "64 bridging senators\n", + "75 relns from_schumer 83 relns from_pelosi\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "" + ], + "text/html": [ + "\n", + " \n", + " \n", + " \n", + " " + ] + }, + "metadata": {}, + "execution_count": 156 + } + ] + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "w3w4RRYkWXKo" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file From 238cb1daa6904ab3316ff9cf6445334f9f7890fd Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 22:17:59 -0800 Subject: [PATCH 058/104] fix(lint) --- graphistry/compute/predicates/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/graphistry/compute/predicates/__init__.py b/graphistry/compute/predicates/__init__.py index 8b13789179..e69de29bb2 100644 --- a/graphistry/compute/predicates/__init__.py +++ b/graphistry/compute/predicates/__init__.py @@ -1 +0,0 @@ - From 3800a7b5e5115e0aa2cd0f5c564b7dafb8620918 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 3 Dec 2023 23:26:12 -0800 Subject: [PATCH 059/104] fix(docs) --- docs/source/conf.py | 34 +++++++++++ docs/source/graphistry.compute.predicates.rst | 58 +++++++++++++++++++ docs/source/graphistry.compute.rst | 17 +++++- 3 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 docs/source/graphistry.compute.predicates.rst diff --git a/docs/source/conf.py b/docs/source/conf.py index b7748c38a2..5d182ca6d0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -15,6 +15,7 @@ # sys.path.insert(0, os.path.abspath('.')) sys.path.insert(0, os.path.abspath("../..")) +sys.path.insert(0, os.path.abspath('../../')) import graphistry # -- Project information ----------------------------------------------------- @@ -47,6 +48,39 @@ ('py:class', '3'), ('py:class', ""), ('py:class', ""), + ('py:class', "graphistry.compute.predicates.ASTPredicate.ASTPredicate"), + ('py:class', 'graphistry.compute.predicates.categorical.Duplicated'), + ('py:class', 'graphistry.compute.predicates.is_in.IsIn'), + ('py:class', 'graphistry.compute.predicates.numeric.Between'), + ('py:class', 'graphistry.compute.predicates.numeric.EQ'), + ('py:class', 'graphistry.compute.predicates.numeric.GE'), + ('py:class', 'graphistry.compute.predicates.numeric.GT'), + ('py:class', 'graphistry.compute.predicates.numeric.IsNA'), + ('py:class', 'graphistry.compute.predicates.numeric.LE'), + ('py:class', 'graphistry.compute.predicates.numeric.LT'), + ('py:class', 'graphistry.compute.predicates.numeric.NE'), + ('py:class', 'graphistry.compute.predicates.numeric.NotNA'), + ('py:class', 'graphistry.compute.predicates.str.Contains'), + ('py:class', 'graphistry.compute.predicates.str.Endswith'), + ('py:class', 'graphistry.compute.predicates.str.IsAlnum'), + ('py:class', 'graphistry.compute.predicates.str.IsAlpha'), + ('py:class', 'graphistry.compute.predicates.str.IsDecimal'), + ('py:class', 'graphistry.compute.predicates.str.IsDigit'), + ('py:class', 'graphistry.compute.predicates.str.IsLower'), + ('py:class', 'graphistry.compute.predicates.str.IsNull'), + ('py:class', 'graphistry.compute.predicates.str.IsNumeric'), + ('py:class', 'graphistry.compute.predicates.str.IsSpace'), + ('py:class', 'graphistry.compute.predicates.str.IsTitle'), + ('py:class', 'graphistry.compute.predicates.str.IsUpper'), + ('py:class', 'graphistry.compute.predicates.str.Match'), + ('py:class', 'graphistry.compute.predicates.str.NotNull'), + ('py:class', 'graphistry.compute.predicates.str.Startswith'), + ('py:class', 'graphistry.compute.predicates.temporal.IsLeapYear'), + ('py:class', 'graphistry.compute.predicates.temporal.IsMonthEnd'), + ('py:class', 'graphistry.compute.predicates.temporal.IsMonthStart'), + ('py:class', 'graphistry.compute.predicates.temporal.IsQuarterEnd'), + ('py:class', 'graphistry.compute.predicates.temporal.IsQuarterStart'), + ('py:class', 'graphistry.compute.predicates.temporal.IsYearStart'), ('py:class', 'graphistry.Engine.Engine'), ('py:class', 'graphistry.gremlin.CosmosMixin'), ('py:class', 'graphistry.gremlin.GremlinMixin'), diff --git a/docs/source/graphistry.compute.predicates.rst b/docs/source/graphistry.compute.predicates.rst new file mode 100644 index 0000000000..5494a24da1 --- /dev/null +++ b/docs/source/graphistry.compute.predicates.rst @@ -0,0 +1,58 @@ +predicates module +------------------------------------------------ + +.. automodule:: graphistry.compute.predicates + :members: + :undoc-members: + :show-inheritance: + :noindex: + + +ASTPredicate +--------------- + +.. automodule:: graphistry.compute.predicates.ASTPredicate + :members: + :undoc-members: + :show-inheritance: + :noindex: + +categorical +--------------- +.. automodule:: graphistry.compute.predicates.categorical + :members: + :undoc-members: + :show-inheritance: + :noindex: + +is_in +--------------- +.. automodule:: graphistry.compute.predicates.is_in + :members: + :undoc-members: + :show-inheritance: + :noindex: + +numeric +--------------- +.. automodule:: graphistry.compute.predicates.numeric + :members: + :undoc-members: + :show-inheritance: + :noindex: + +str +-------------------- +.. automodule:: graphistry.compute.predicates.str + :members: + :undoc-members: + :show-inheritance: + :noindex: + +temporal +--------------- +.. automodule:: graphistry.compute.predicates.temporal + :members: + :undoc-members: + :show-inheritance: + :noindex: diff --git a/docs/source/graphistry.compute.rst b/docs/source/graphistry.compute.rst index c610034aab..87811ed705 100644 --- a/docs/source/graphistry.compute.rst +++ b/docs/source/graphistry.compute.rst @@ -1,3 +1,11 @@ +Compute Modules +--------------- + +.. toctree:: + :maxdepth: 2 + + graphistry.compute.predicates + ComputeMixin module ------------------------------------------------ @@ -7,7 +15,6 @@ ComputeMixin module :show-inheritance: :noindex: - Chain --------------- @@ -56,3 +63,11 @@ Hop :undoc-members: :show-inheritance: :noindex: + +predicates +--------------- +.. automodule:: graphistry.compute.predicates + :members: + :undoc-members: + :show-inheritance: + :noindex: From baad7383bb2489cd4b5b3032ac76692055adf0aa Mon Sep 17 00:00:00 2001 From: lmeyerov Date: Mon, 4 Dec 2023 11:10:01 -0800 Subject: [PATCH 060/104] fix(docs): readme md syntax --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0f0cefe3e0..757cfcb5b5 100644 --- a/README.md +++ b/README.md @@ -1265,7 +1265,7 @@ g2b = g2.hop( edge_match={"v": 1, "type": "z"}, destination_node_match={g2._node: "b"}) g2b = g2.hop( - source_node_query='n == "a", + source_node_query='n == "a"', edge_query='v == 1 and type == "z"', destination_node_query='n == "b"') From 2ac465a636b69024bb1e05b687080755fe19760a Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Mon, 4 Dec 2023 21:54:53 -0800 Subject: [PATCH 061/104] fix(imports): setup_logger and drop unused --- graphistry/compute/ComputeMixin.py | 3 ++- graphistry/compute/ast.py | 9 +++++---- graphistry/compute/chain.py | 12 +++++------- graphistry/compute/hop.py | 4 ++++ graphistry/dgl_utils.py | 2 +- graphistry/feature_utils.py | 4 +--- graphistry/features.py | 3 +-- graphistry/tests/test_compute_chain.py | 10 +++++----- graphistry/tests/test_dgl_utils.py | 2 +- 9 files changed, 25 insertions(+), 24 deletions(-) diff --git a/graphistry/compute/ComputeMixin.py b/graphistry/compute/ComputeMixin.py index 7a9b2f71c7..a5a0431f00 100644 --- a/graphistry/compute/ComputeMixin.py +++ b/graphistry/compute/ComputeMixin.py @@ -4,6 +4,7 @@ from graphistry.Engine import Engine from graphistry.Plottable import Plottable +from graphistry.util import setup_logger from .chain import chain as chain_base from .collapse import collapse_by from .hop import hop as hop_base @@ -12,7 +13,7 @@ filter_nodes_by_dict as filter_nodes_by_dict_base ) -logger = logging.getLogger("compute") +logger = setup_logger(__name__) if TYPE_CHECKING: MIXIN_BASE = Plottable diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 39695edc21..942856de45 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -1,7 +1,9 @@ -from typing import Any, List, Optional, cast +import logging +from typing import Optional, cast import pandas as pd from graphistry.Plottable import Plottable +from graphistry.util import setup_logger from .predicates.ASTPredicate import ASTPredicate from .predicates.is_in import ( is_in, IsIn @@ -47,9 +49,8 @@ ) from .filter_by_dict import filter_by_dict -import logging -logger = logging.getLogger(__name__) -#logger.setLevel(logging.DEBUG) + +logger = setup_logger(__name__) ############################################################################## diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index c8be5d12c1..a3132ca624 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -1,13 +1,11 @@ -from typing import cast, List, Optional, Tuple, Union +from typing import cast, List, Tuple import pandas as pd from graphistry.Plottable import Plottable +from graphistry.util import setup_logger from .ast import ASTObject, ASTNode, ASTEdge -from .filter_by_dict import filter_by_dict -import logging -logger = logging.getLogger(__name__) -#logger.setLevel(logging.DEBUG) +logger = setup_logger(__name__) ############################################################################### @@ -191,7 +189,7 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: added_edge_index = False - logger.debug('============ FORWARDS ============') + logger.debug('======================== FORWARDS ========================') # Forwards # This computes valid path *prefixes*, where each g nodes/edges is the path wavefront: @@ -214,7 +212,7 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: ) g_stack.append(g_step) - logger.debug('============ BACKWARDS ============') + logger.debug('======================== BACKWARDS ========================') # Backwards # Compute reverse and thus complete paths. Dropped nodes/edges are thus the incomplete path prefixes. diff --git a/graphistry/compute/hop.py b/graphistry/compute/hop.py index b14a7bc08a..93ea3c6d2f 100644 --- a/graphistry/compute/hop.py +++ b/graphistry/compute/hop.py @@ -1,9 +1,13 @@ +import logging from typing import List, Optional import pandas as pd from graphistry.Plottable import Plottable +from graphistry.util import setup_logger from .filter_by_dict import filter_by_dict +logger = setup_logger(__name__) + def query_if_not_none(query: Optional[str], df: pd.DataFrame) -> pd.DataFrame: if query is None: diff --git a/graphistry/dgl_utils.py b/graphistry/dgl_utils.py index 0999ea7982..56b5670f33 100644 --- a/graphistry/dgl_utils.py +++ b/graphistry/dgl_utils.py @@ -54,7 +54,7 @@ def lazy_torch_import_has_dependency(): return False, e, None -logger = setup_logger(name=__name__, verbose=config.VERBOSE) +logger = setup_logger(name=__name__) diff --git a/graphistry/feature_utils.py b/graphistry/feature_utils.py index 1ca5272df0..5d80e7e5bf 100644 --- a/graphistry/feature_utils.py +++ b/graphistry/feature_utils.py @@ -8,7 +8,6 @@ from functools import partial from typing import ( - Hashable, List, Union, Dict, @@ -16,7 +15,6 @@ Optional, Tuple, TYPE_CHECKING, - Type ) # noqa from typing_extensions import Literal # Literal native to py3.8+ @@ -27,7 +25,7 @@ from .ai_utils import infer_graph, infer_self_graph # add this inside classes and have a method that can set log level -logger = setup_logger(name=__name__, verbose=config.VERBOSE) +logger = setup_logger(__name__) if TYPE_CHECKING: MIXIN_BASE = ComputeMixin diff --git a/graphistry/features.py b/graphistry/features.py index 32e83a3a28..0ae4a49942 100644 --- a/graphistry/features.py +++ b/graphistry/features.py @@ -1,8 +1,7 @@ from .util import setup_logger -from .constants import VERBOSE, TRACE from .util import ModelDict -logger = setup_logger("graphistry.features", verbose=VERBOSE, fullpath=TRACE) +logger = setup_logger(__name__) # ############################################################### UNK = "UNK" diff --git a/graphistry/tests/test_compute_chain.py b/graphistry/tests/test_compute_chain.py index 8d92852447..c674edf06a 100644 --- a/graphistry/tests/test_compute_chain.py +++ b/graphistry/tests/test_compute_chain.py @@ -1,15 +1,15 @@ from functools import lru_cache from typing import Dict, List +import logging import pandas as pd + from common import NoAuthTestCase +from graphistry.compute.ast import n, e_forward, e_reverse, e_undirected, is_in from graphistry.tests.test_compute import CGFull - from graphistry.tests.test_compute_hops import hops_graph -from graphistry.compute.ast import n, e_forward, e_reverse, e_undirected, is_in +from graphistry.util import setup_logger -import logging -logger = logging.getLogger() -logger.setLevel(logging.DEBUG) +logger = setup_logger(__name__) @lru_cache(maxsize=1) diff --git a/graphistry/tests/test_dgl_utils.py b/graphistry/tests/test_dgl_utils.py index bf3610885b..760045eee6 100644 --- a/graphistry/tests/test_dgl_utils.py +++ b/graphistry/tests/test_dgl_utils.py @@ -11,7 +11,7 @@ if has_dgl: import torch -logger = setup_logger("test_DGL_utils", verbose=True) +logger = setup_logger(__name__) edf = pd.read_csv( "graphistry/tests/data/malware_capture_bot.csv", index_col=0, nrows=50 From 0480b01f9e556f29959e3cbc76225b2575c53416 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Mon, 4 Dec 2023 21:57:06 -0800 Subject: [PATCH 062/104] fix(hop): handle intermediate matches --- graphistry/compute/ast.py | 23 +++-- graphistry/compute/hop.py | 132 +++++++++++++++++++++++-- graphistry/tests/test_compute_chain.py | 37 +++++++ 3 files changed, 178 insertions(+), 14 deletions(-) diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 942856de45..bc45c7fa4b 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -106,10 +106,9 @@ def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], ta if self._name is not None: out_g = out_g.nodes(out_g._nodes.assign(**{self._name: True})) - logger.debug(f'CALL NODE {self} ===>') - logger.debug(out_g._nodes) - logger.debug(out_g._edges) - logger.debug('----------------------------------------') + if logger.isEnabledFor(logging.DEBUG): + logger.debug('CALL NODE %s ====>\nnodes:\n%s\nedges:\n%s\n', self, out_g._nodes, out_g._edges) + logger.debug('----------------------------------------') return out_g @@ -171,6 +170,15 @@ def __repr__(self) -> str: def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], target_wave_front: Optional[pd.DataFrame]) -> Plottable: + if logger.isEnabledFor(logging.DEBUG): + logger.debug('----------------------------------------') + logger.debug('@CALL EDGE START {%s} ===>\n', self) + logger.debug('prev_node_wavefront:\n%s\n', prev_node_wavefront) + logger.debug('target_wave_front:\n%s\n', target_wave_front) + logger.debug('g._nodes:\n%s\n', g._nodes) + logger.debug('g._edges:\n%s\n', g._edges) + logger.debug('----------------------------------------') + out_g = g.hop( nodes=prev_node_wavefront, hops=self._hops, @@ -189,10 +197,9 @@ def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], ta if self._name is not None: out_g = out_g.edges(out_g._edges.assign(**{self._name: True})) - logger.debug(f'CALL EDGE {self} ===>') - logger.debug(out_g._nodes) - logger.debug(out_g._edges) - logger.debug('----------------------------------------') + if logger.isEnabledFor(logging.DEBUG): + logger.debug('/CALL EDGE END {%s} ===>\nnodes:\n%s\nedges:\n%s\n', self, out_g._nodes, out_g._edges) + logger.debug('----------------------------------------') return out_g diff --git a/graphistry/compute/hop.py b/graphistry/compute/hop.py index 93ea3c6d2f..a94725b9bf 100644 --- a/graphistry/compute/hop.py +++ b/graphistry/compute/hop.py @@ -44,7 +44,7 @@ def hop(self: Plottable, destination_node_query: dataframe query to match nodes after hopping (including intermediate) edge_query: dataframe query to match edges before hopping (including intermediate) return_as_wave_front: Only return the nodes/edges reached, ignoring past ones (primarily for internal use) - target_wave_front: Only consider these nodes for reachability (primarily for internal use by reverse pass) + target_wave_front: Only consider these nodes for reachability, and for intermediate hops, also consider nodes (primarily for internal use by reverse pass) """ """ @@ -55,19 +55,45 @@ def hop(self: Plottable, """ + #TODO target_wave_front code also includes nodes for handling intermediate hops + # ... better to make an explicit param of allowed intermediates? (vs recording each intermediate hop) + + debugging_hop = True + + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('=======================') + logger.debug('======== HOP ==========') + logger.debug('nodes:\n%s', nodes) + logger.debug('self._nodes:\n%s', self._nodes) + logger.debug('self._edges:\n%s', self._edges) + logger.debug('hops: %s', hops) + logger.debug('to_fixed_point: %s', to_fixed_point) + logger.debug('direction: %s', direction) + logger.debug('edge_match: %s', edge_match) + logger.debug('source_node_match: %s', source_node_match) + logger.debug('destination_node_match: %s', destination_node_match) + logger.debug('source_node_query: %s', source_node_query) + logger.debug('destination_node_query: %s', destination_node_query) + logger.debug('edge_query: %s', edge_query) + logger.debug('return_as_wave_front: %s', return_as_wave_front) + logger.debug('target_wave_front:\n%s', target_wave_front) + logger.debug('---------------------') + if not to_fixed_point and not isinstance(hops, int): raise ValueError(f'Must provide hops int when to_fixed_point is False, received: {hops}') if direction not in ['forward', 'reverse', 'undirected']: raise ValueError(f'Invalid direction: "{direction}", must be one of: "forward" (default), "reverse", "undirected"') + + if target_wave_front is not None and nodes is None: + raise ValueError('target_wave_front requires nodes to target against (for intermediate hops)') if destination_node_match == {}: destination_node_match = None g2 = self.materialize_nodes() - if nodes is None: - nodes = g2._nodes + starting_nodes = nodes if nodes is not None else g2._nodes if g2._edge is None: if 'index' in g2._edges.columns: @@ -86,7 +112,7 @@ def hop(self: Plottable, hops_remaining = hops - wave_front = nodes[[g2._node]][:0] + wave_front = starting_nodes[[g2._node]][:0] matches_nodes = None matches_edges = edges_indexed[[EDGE_ID]][:0] @@ -94,8 +120,25 @@ def hop(self: Plottable, #richly-attributed subset for dest matching & return-enriching base_target_nodes = target_wave_front if target_wave_front is not None else g2._nodes + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('~~~~~~~~~~ LOOP PRE ~~~~~~~~~~~') + logger.debug('starting_nodes:\n%s', starting_nodes) + logger.debug('g2._nodes:\n%s', g2._nodes) + logger.debug('g2._edges:\n%s', g2._edges) + logger.debug('edges_indexed:\n%s', edges_indexed) + logger.debug('=====================') + first_iter = True while True: + + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('~~~~~~~~~~ LOOP STEP BEGIN ~~~~~~~~~~~') + logger.debug('hops_remaining: %s', hops_remaining) + logger.debug('wave_front:\n%s', wave_front) + logger.debug('matches_nodes:\n%s', matches_nodes) + logger.debug('matches_edges:\n%s', matches_edges) + logger.debug('first_iter: %s', first_iter) + if not to_fixed_point and hops_remaining is not None: if hops_remaining < 1: break @@ -105,12 +148,18 @@ def hop(self: Plottable, wave_front_iter : pd.DataFrame = query_if_not_none( source_node_query, filter_by_dict( - nodes if first_iter else wave_front.merge(nodes, on=g2._node, how='left'), + starting_nodes + if first_iter else + wave_front.merge(self._nodes, on=g2._node, how='left'), source_node_match ) )[[ g2._node ]] first_iter = False + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('~~~~~~~~~~ LOOP STEP CONTINUE ~~~~~~~~~~~') + logger.debug('wave_front_iter:\n%s', wave_front_iter) + hop_edges_forward = None new_node_ids_forward = None if direction in ['forward', 'undirected']: @@ -121,6 +170,21 @@ def hop(self: Plottable, on=g2._node) [[g2._source, g2._destination, EDGE_ID]] ) + if target_wave_front is not None: + assert nodes is not None, "target_wave_front indicates nodes" + if hops_remaining: + intermediate_target_wave_front = pd.concat([ + target_wave_front[[g2._node]], + nodes[[g2._node]] + ], sort=False, ignore_index=True + ).drop_duplicates() + else: + intermediate_target_wave_front = target_wave_front[[g2._node]] + hop_edges_forward = hop_edges_forward.merge( + intermediate_target_wave_front.rename(columns={g2._node: g2._destination}), + how='inner', + on=g2._destination + ) new_node_ids_forward = hop_edges_forward[[g2._destination]].rename(columns={g2._destination: g2._node}).drop_duplicates() if destination_node_query is not None or destination_node_match is not None: @@ -136,10 +200,14 @@ def hop(self: Plottable, on=g2._destination ) + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('--- direction in [forward, undirected] ---') + logger.debug('hop_edges_forward:\n%s', hop_edges_forward) + logger.debug('new_node_ids_forward:\n%s', new_node_ids_forward) + hop_edges_reverse = None new_node_ids_reverse = None if direction in ['reverse', 'undirected']: - #TODO limit by target_wave_front if exists? hop_edges_reverse = ( wave_front_iter.merge( edges_indexed[[g2._destination, g2._source, EDGE_ID]].assign(**{g2._node: edges_indexed[g2._destination]}), @@ -147,6 +215,28 @@ def hop(self: Plottable, on=g2._node) [[g2._destination, g2._source, EDGE_ID]] ) + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('--- direction in [reverse, undirected] ---') + logger.debug('hop_edges_reverse basic:\n%s', hop_edges_reverse) + + if target_wave_front is not None: + assert nodes is not None, "target_wave_front indicates nodes" + if hops_remaining: + intermediate_target_wave_front = pd.concat([ + target_wave_front[[g2._node]], + nodes[[g2._node]] + ], sort=False, ignore_index=True + ).drop_duplicates() + else: + intermediate_target_wave_front = target_wave_front[[g2._node]] + hop_edges_reverse = hop_edges_reverse.merge( + intermediate_target_wave_front.rename(columns={g2._node: g2._source}), + how='inner', + on=g2._source + ) + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('hop_edges_reverse filtered by target_wave_front:\n%s', hop_edges_reverse) + new_node_ids_reverse = hop_edges_reverse[[g2._source]].rename(columns={g2._source: g2._node}).drop_duplicates() if destination_node_query is not None or destination_node_match is not None: @@ -161,6 +251,12 @@ def hop(self: Plottable, how='inner', on=g2._source ) + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('hop_edges_reverse filtered by destination predicates:\n%s', hop_edges_reverse) + + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('hop_edges_reverse:\n%s', hop_edges_reverse) + logger.debug('new_node_ids_reverse:\n%s', new_node_ids_reverse) mt : List[pd.DataFrame] = [] # help mypy @@ -175,6 +271,12 @@ def hop(self: Plottable, + ( [ new_node_ids_forward ] if new_node_ids_forward is not None else mt ) # noqa: W503 + ( [ new_node_ids_reverse] if new_node_ids_reverse is not None else mt ), # noqa: W503 ignore_index=True, sort=False).drop_duplicates() + + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('~~~~~~~~~~ LOOP STEP MERGES 1 ~~~~~~~~~~~') + logger.debug('matches_edges:\n%s', matches_edges) + logger.debug('new_node_ids:\n%s', new_node_ids) + # Finally include all initial root nodes matched against, now that edge triples satisfy all source/dest/edge predicates # Only run first iteration b/c root nodes already accounted for in subsequent # In wavefront mode, skip, as we only want to return reached nodes @@ -192,6 +294,10 @@ def hop(self: Plottable, else mt), ignore_index=True, sort=False).drop_duplicates(subset=[g2._node]) + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('~~~~~~~~~~ LOOP STEP MERGES 2 ~~~~~~~~~~~') + logger.debug('matches_edges:\n%s', matches_edges) + combined_node_ids = pd.concat([matches_nodes, new_node_ids], ignore_index=True, sort=False).drop_duplicates() if len(combined_node_ids) == len(matches_nodes): @@ -201,6 +307,13 @@ def hop(self: Plottable, wave_front = new_node_ids matches_nodes = combined_node_ids + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('~~~~~~~~~~ LOOP STEP POST ~~~~~~~~~~~') + logger.debug('matches_nodes:\n%s', matches_nodes) + logger.debug('combined_node_ids:\n%s', combined_node_ids) + logger.debug('wave_front:\n%s', wave_front) + logger.debug('matches_nodes:\n%s', matches_nodes) + #hydrate edges final_edges = edges_indexed.merge(matches_edges, on=EDGE_ID, how='inner') if EDGE_ID not in self._edges: @@ -219,4 +332,11 @@ def hop(self: Plottable, how='inner') g_out = g_out.nodes(final_nodes) + if debugging_hop and logger.isEnabledFor(logging.DEBUG): + logger.debug('~~~~~~~~~~ HOP OUTPUT ~~~~~~~~~~~') + logger.debug('nodes:\n%s', g_out._nodes) + logger.debug('edges:\n%s', g_out._edges) + logger.debug('======== /HOP =============') + logger.debug('==========================') + return g_out diff --git a/graphistry/tests/test_compute_chain.py b/graphistry/tests/test_compute_chain.py index c674edf06a..3f98324100 100644 --- a/graphistry/tests/test_compute_chain.py +++ b/graphistry/tests/test_compute_chain.py @@ -403,6 +403,43 @@ def test_hop_chain_1_end_undirected(self): ]) compare_graphs(g3_undirected_chain_closed, g_out_nodes, g_out_edges) + def test_tricky_topology_1(self): + + nodes = pd.DataFrame({ + 'n': ['a1', 'a2', 'b1', 'b2'], + 't': [0, 0, 1, 1] + }) + + edges = pd.DataFrame({ + 's': ['a1', 'a1' ], + 'd': ['a2', 'b1'] + }) + + n_out = pd.DataFrame({ + 'n': ['a1', 'a2'], + 't': [0, 0] + }) + + e_out = pd.DataFrame({ + 's': ['a1'], + 'd': ['a2'] + }) + + g = CGFull().edges(edges, 's', 'd').nodes(nodes, 'n') + + g2 = g.chain([ + n({'t': 0}), + e_undirected(), + n({'t': 0}) + ]) + + if logger.isEnabledFor(logging.DEBUG): + logger.debug('\nNODES\n') + logger.debug(g2._nodes.to_dict(orient='records')) + logger.debug('\nEDGES\n') + logger.debug(g2._edges.to_dict(orient='records')) + + compare_graphs(g2, n_out.to_dict(orient='records'), e_out.to_dict(orient='records')) class TestComputeChainWavefront2Mixin(NoAuthTestCase): """ From ff5eefcf45b7fdcb3d16c835207142ada9a5680a Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Mon, 4 Dec 2023 22:07:37 -0800 Subject: [PATCH 063/104] refactor(setup_logger): handlers --- CHANGELOG.md | 4 ++++ graphistry/util.py | 41 +++++++++++++++++++++++++---------------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af4b890248..c31b75edfd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,12 +26,16 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm * chain/hop: source_node_match was being mishandled when multiple node attributes exist * chain: backwards validation pass was too permissive; add `target_wave_front` check` * hop: multi-hops with `source_node_match` specified was not checking intermediate hops +* hop: multi-hops reverse validation was mishandling intermediate nodes * compute logging no longer default-overrides level to DEBUG ### Changed * refactor: move `is_in`, `IsIn` implementations to `graphistry.ast.predicates`; old imports preserved * `IsIn` now implements `ASTPredicate` +* Refactor: use `setup_logger(__name__)` more consistently instead of `logging.getLogger(__name__)` +* Refactor: drop unused imports +* Redo `setup_logger()` to activate formatted stream handler iff verbose / LOG_LEVEL ### Docs diff --git a/graphistry/util.py b/graphistry/util.py index 025e8853b3..3498b33572 100644 --- a/graphistry/util.py +++ b/graphistry/util.py @@ -17,25 +17,34 @@ # ##################################### +@lru_cache(maxsize=1) +def get_handler(short=False): + if short: + formatter = logging.Formatter("%(filename)s:%(lineno)s %(message)s\n") + else: + formatter = logging.Formatter("\n[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s\n") + handler = logging.StreamHandler() + handler.setFormatter(formatter) + return handler -def global_logger(): - logger = logging.getLogger() - return logger +def setup_logger(name='', verbose=VERBOSE, fullpath=TRACE): + logger = logging.getLogger(name) + + if verbose is not None: + if verbose: + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(logging.ERROR) + elif os.environ.get('LOG_LEVEL', None) is not None: + if os.environ['LOG_LEVEL'] == 'TRACE': + logger.setLevel(logging.DEBUG) + else: + logger.setLevel(os.environ['LOG_LEVEL']) + if not logger.handlers and (verbose is not None or os.environ.get('LOG_LEVEL', None) is not None): + logger.addHandler(get_handler(short=False)) -def setup_logger(name, verbose=VERBOSE, fullpath=TRACE): - # if fullpath: - # FORMAT = "[%(filename)s:%(lineno)s - %(funcName)20s() ]\n %(message)s\n" - # else: - # FORMAT = " %(message)s\n" - # logging.basicConfig(format=FORMAT) - # logger = logging.getLogger()#f'graphistry.{name}') - # if verbose is None: - # logger.setLevel(logging.ERROR) - # else: - # logger.setLevel(logging.INFO if verbose else logging.DEBUG) - # return logger - return global_logger() + return logger # ##################################### From c585802474fe87f98ee0a76f1b57a2b2cf76354b Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Mon, 4 Dec 2023 22:10:44 -0800 Subject: [PATCH 064/104] infra(docker tests): propagate LOG_LEVEL --- CHANGELOG.md | 4 ++++ docker/test-cpu-local.sh | 2 ++ 2 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c31b75edfd..cc9c977aad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm * hop: multi-hops reverse validation was mishandling intermediate nodes * compute logging no longer default-overrides level to DEBUG +### Infra + +* Docker tests support LOG_LEVEL + ### Changed * refactor: move `is_in`, `IsIn` implementations to `graphistry.ast.predicates`; old imports preserved diff --git a/docker/test-cpu-local.sh b/docker/test-cpu-local.sh index 9d7392605d..7f6279495e 100755 --- a/docker/test-cpu-local.sh +++ b/docker/test-cpu-local.sh @@ -11,6 +11,7 @@ WITH_TYPECHECK=${WITH_TYPECHECK:-1} WITH_TEST=${WITH_TEST:-1} WITH_BUILD=${WITH_BUILD:-1} TEST_CPU_VERSION=${TEST_CPU_VERSION:-latest} +LOG_LEVEL=${LOG_LEVEL:-DEBUG} SENTENCE_TRANSFORMER=${SENTENCE_TRANSFORMER-average_word_embeddings_komninos} NETWORK="" @@ -46,6 +47,7 @@ docker run \ -e WITH_TYPECHECK=$WITH_TYPECHECK \ -e WITH_BUILD=$WITH_BUILD \ -e WITH_TEST=$WITH_TEST \ + -e LOG_LEVEL=$LOG_LEVEL \ -v "`pwd`/../graphistry:/opt/pygraphistry/graphistry:ro" \ --rm \ ${NETWORK} \ From c12c2cefd2f7762d7b358bfec4f561fc72c8a005 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Mon, 4 Dec 2023 23:27:10 -0800 Subject: [PATCH 065/104] docs(changelog) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc9c977aad..b59cae05d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +## [0.30.0 - 2023-12-04] + ### Added * chain/hop: `is_in()` membership predicate, `.chain([ n({'type': is_in(['a', 'b'])}) ])` From cd1d75fd077531b44e4f7e353b45fe4d9abe993c Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Tue, 5 Dec 2023 00:21:50 -0800 Subject: [PATCH 066/104] fix(readthedocs) --- .readthedocs.yml | 5 +++++ CHANGELOG.md | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/.readthedocs.yml b/.readthedocs.yml index f68f0b53f7..e3932a2520 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -9,6 +9,11 @@ version: 2 sphinx: configuration: docs/source/conf.py +build: + os: ubuntu-22.04 + tools: + python: "3.8" + # Optionally build your docs in additional formats such as PDF formats: - pdf diff --git a/CHANGELOG.md b/CHANGELOG.md index 28f9b01d91..56d12025c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +### Docs + +* Update readthedocs yml to work around ReadTheDocs v2 yml interpretation regressions + ## [0.30.0 - 2023-12-04] ### Added From 37a231452bb67d82a4ba274858744dcf7b355925 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Tue, 5 Dec 2023 00:28:15 -0800 Subject: [PATCH 067/104] fix(readthedocs): changes to v2 format interp --- .readthedocs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index e3932a2520..609e875f73 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -21,7 +21,6 @@ formats: - epub python: - version: "3.8" install: - method: pip path: . From 302083e91ea63b9ea395f987a7ef537303a25f5a Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Tue, 5 Dec 2023 00:58:26 -0800 Subject: [PATCH 068/104] fix(markdownlint) --- .github/workflows/ci.yml | 2 +- .markdownlint.yaml | 267 +++++++++++++++++++++++++++++++++++++++ CHANGELOG.md | 2 + README.md | 16 +-- 4 files changed, 276 insertions(+), 11 deletions(-) create mode 100644 .markdownlint.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0b110f586c..15a357a183 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -303,6 +303,6 @@ jobs: - name: Test building docs continue-on-error: true run: | - docker run --rm -v "$(pwd)/README.md:/README.md:ro" avtodev/markdown-lint:v1 README.md + docker run --rm -v "$(pwd)/README.md:/workdir/README.md:ro" -v "$(pwd)/.markdownlint.yaml:/workdir/.markdownlint.yaml:ro" ghcr.io/igorshubovych/markdownlint-cli:v0.37.0 README.md diff --git a/.markdownlint.yaml b/.markdownlint.yaml new file mode 100644 index 0000000000..02794720db --- /dev/null +++ b/.markdownlint.yaml @@ -0,0 +1,267 @@ +# ------------------------------------------------------------------------------ +# Based on markdownlint/schema/.markdownlint.yml +# ------------------------------------------------------------------------------ + + +# Example markdownlint configuration with all properties set to their default value + +# Default state for all rules +default: true + +# Path to configuration file to extend +extends: null + +# MD001/heading-increment : Heading levels should only increment by one level at a time : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md001.md +MD001: true + +# MD003/heading-style : Heading style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md003.md +MD003: + # Heading style + style: "consistent" + +# MD004/ul-style : Unordered list style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md004.md +MD004: + # List style + style: "consistent" + +# MD005/list-indent : Inconsistent indentation for list items at the same level : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md005.md +MD005: true + +# MD007/ul-indent : Unordered list indentation : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md007.md +MD007: + # Spaces for indent + indent: 2 + # Whether to indent the first level of the list + start_indented: false + # Spaces for first level indent (when start_indented is set) + start_indent: 2 + +# MD009/no-trailing-spaces : Trailing spaces : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md009.md +MD009: + # Spaces for line break + br_spaces: 2 + # Allow spaces for empty lines in list items + list_item_empty_lines: false + # Include unnecessary breaks + strict: false + +# MD010/no-hard-tabs : Hard tabs : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md010.md +MD010: + # Include code blocks + code_blocks: true + # Fenced code languages to ignore + ignore_code_languages: [] + # Number of spaces for each hard tab + spaces_per_tab: 1 + +# MD011/no-reversed-links : Reversed link syntax : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md011.md +MD011: true + +# MD012/no-multiple-blanks : Multiple consecutive blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md012.md +MD012: + # Consecutive blank lines + maximum: 1 + +# MD013/line-length : Line length : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md013.md +MD013: + # Number of characters + line_length: 2000 + # Number of characters for headings + heading_line_length: 2000 + # Number of characters for code blocks + code_block_line_length: 2000 + # Include code blocks + code_blocks: true + # Include tables + tables: false + # Include headings + headings: true + # Strict length checking + strict: false + # Stern length checking + stern: false + +# MD014/commands-show-output : Dollar signs used before commands without showing output : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md014.md +MD014: true + +# MD018/no-missing-space-atx : No space after hash on atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md018.md +MD018: true + +# MD019/no-multiple-space-atx : Multiple spaces after hash on atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md019.md +MD019: true + +# MD020/no-missing-space-closed-atx : No space inside hashes on closed atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md020.md +MD020: true + +# MD021/no-multiple-space-closed-atx : Multiple spaces inside hashes on closed atx style heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md021.md +MD021: true + +# MD022/blanks-around-headings : Headings should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md022.md +MD022: + # Blank lines above heading + lines_above: 1 + # Blank lines below heading + lines_below: 1 + +# MD023/heading-start-left : Headings must start at the beginning of the line : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md023.md +MD023: true + +# MD024/no-duplicate-heading : Multiple headings with the same content : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md024.md +MD024: + # Only check sibling headings + allow_different_nesting: false + # Only check sibling headings + siblings_only: false + +# MD025/single-title/single-h1 : Multiple top-level headings in the same document : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md025.md +MD025: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD026/no-trailing-punctuation : Trailing punctuation in heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md026.md +MD026: + # Punctuation characters + punctuation: ".,;:!。,;:!" + +# MD027/no-multiple-space-blockquote : Multiple spaces after blockquote symbol : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md027.md +MD027: true + +# MD028/no-blanks-blockquote : Blank line inside blockquote : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md028.md +MD028: true + +# MD029/ol-prefix : Ordered list item prefix : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md029.md +MD029: + # List style + style: "one_or_ordered" + +# MD030/list-marker-space : Spaces after list markers : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md030.md +MD030: + # Spaces for single-line unordered list items + ul_single: 1 + # Spaces for single-line ordered list items + ol_single: 1 + # Spaces for multi-line unordered list items + ul_multi: 1 + # Spaces for multi-line ordered list items + ol_multi: 1 + +# MD031/blanks-around-fences : Fenced code blocks should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md031.md +MD031: + # Include list items + list_items: true + +# MD032/blanks-around-lists : Lists should be surrounded by blank lines : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md032.md +MD032: true + +# MD033/no-inline-html : Inline HTML : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md033.md +MD033: + # Allowed elements + allowed_elements: [table, tr, td, img, em,br, a] + +# MD034/no-bare-urls : Bare URL used : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md034.md +MD034: true + +# MD035/hr-style : Horizontal rule style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md035.md +MD035: + # Horizontal rule style + style: "consistent" + +# MD036/no-emphasis-as-heading : Emphasis used instead of a heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md036.md +MD036: + # Punctuation characters + punctuation: ".,;:!?。,;:!?" + +# MD037/no-space-in-emphasis : Spaces inside emphasis markers : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md037.md +MD037: true + +# MD038/no-space-in-code : Spaces inside code span elements : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md038.md +MD038: true + +# MD039/no-space-in-links : Spaces inside link text : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md039.md +MD039: true + +# MD040/fenced-code-language : Fenced code blocks should have a language specified : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md040.md +MD040: + # List of languages + allowed_languages: [] + # Require language only + language_only: false + +# MD041/first-line-heading/first-line-h1 : First line in a file should be a top-level heading : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md041.md +MD041: + # Heading level + level: 1 + # RegExp for matching title in front matter + front_matter_title: "^\\s*title\\s*[:=]" + +# MD042/no-empty-links : No empty links : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md042.md +MD042: true + +# MD043/required-headings : Required heading structure : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md043.md +MD043: false + +# MD044/proper-names : Proper names should have the correct capitalization : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md044.md +MD044: + # List of proper names + names: [] + # Include code blocks + code_blocks: true + # Include HTML elements + html_elements: true + +# MD045/no-alt-text : Images should have alternate text (alt text) : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md045.md +MD045: true + +# MD046/code-block-style : Code block style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md046.md +MD046: + # Block style + style: "consistent" + +# MD047/single-trailing-newline : Files should end with a single newline character : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md047.md +MD047: true + +# MD048/code-fence-style : Code fence style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md048.md +MD048: + # Code fence style + style: "consistent" + +# MD049/emphasis-style : Emphasis style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md049.md +MD049: + # Emphasis style + style: "consistent" + +# MD050/strong-style : Strong style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md050.md +MD050: + # Strong style + style: "consistent" + +# MD051/link-fragments : Link fragments should be valid : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md051.md +MD051: true + +# MD052/reference-links-images : Reference links and images should use a label that is defined : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md052.md +MD052: + # Include shortcut syntax + shortcut_syntax: false + +# MD053/link-image-reference-definitions : Link and image reference definitions should be needed : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md053.md +MD053: + # Ignored definitions + ignored_definitions: + - "//" + +# MD054/link-image-style : Link and image style : https://github.com/DavidAnson/markdownlint/blob/v0.32.1/doc/md054.md +MD054: + # Allow autolinks + autolink: true + # Allow inline links and images + inline: true + # Allow full reference links and images + full: true + # Allow collapsed reference links and images + collapsed: true + # Allow shortcut reference links and images + shortcut: true + # Allow URLs as inline links + url_inline: true \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 56d12025c6..96fce3472d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Docs * Update readthedocs yml to work around ReadTheDocs v2 yml interpretation regressions +* Make README.md pass markdownlint +* Switch markdownlint docker channel to official and pin ## [0.30.0 - 2023-12-04] diff --git a/README.md b/README.md index 757cfcb5b5..5818b142a2 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ You can use PyGraphistry with traditional Python data sources like CSVs, SQL, Ne -## PyGraphistry is... +## **PyGraphistry is:** * **Fast & gorgeous:** Interactively cluster, filter, inspect large amounts of data, and zip through timebars. It clusters large graphs with a descendant of the gorgeous ForceAtlas2 layout algorithm introduced in Gephi. Our data explorer connects to Graphistry's GPU cluster to layout and render hundreds of thousand of nodes+edges in your browser at unparalleled speeds. @@ -410,7 +410,7 @@ Automatically and intelligently transform text, numbers, booleans, and other for preds = model.predict(X_new) ``` - * Encode model definitions and compare models against each other +* Encode model definitions and compare models against each other ```python # graphistry @@ -434,7 +434,6 @@ Automatically and intelligently transform text, numbers, booleans, and other for # compare g2 vs g3 or add to different pipelines ``` - See `help(g.featurize)` for more options ### [sklearn-based UMAP](https://umap-learn.readthedocs.io/en/latest/), [cuML-based UMAP](https://docs.rapids.ai/api/cuml/stable/api.html?highlight=umap#cuml.UMAP) @@ -455,6 +454,7 @@ See `help(g.featurize)` for more options new_df = pd.read_csv(...) embeddings, X_new, _ = g.transform_umap(new_df, None, kind='nodes', return_graph=False) ``` + * Infer a new graph from new data using the old umap coordinates to run inference without having to train a new umap model. ```python @@ -466,7 +466,6 @@ See `help(g.featurize)` for more options g3 = g.transform_umap(new_df, return_graph=True, merge_policy=True) g3.plot() # useful to see how new data connects to old -- play with `sample` and `n_neighbors` to control how much of old to include ``` - * UMAP supports many options, such as supervised mode, working on a subset of columns, and passing arguments to underlying `featurize()` and UMAP implementations (see `help(g.umap)`): @@ -551,8 +550,7 @@ GNN support is rapidly evolving, please contact the team directly or on Slack fo ``` - -* If edges are not given, `g.umap(..)` will supply them: +* If edges are not given, `g.umap(..)` will supply them: ```python ndf = pd.read_csv(nodes.csv) @@ -561,7 +559,7 @@ GNN support is rapidly evolving, please contact the team directly or on Slack fo g2.search_graph('my natural language query', ...).plot() ``` - + See `help(g.search_graph)` for options ### Knowledge Graph Embeddings @@ -617,7 +615,7 @@ See `help(g.search_graph)` for options See `help(g.embed)`, `help(g.predict_links)` , or `help(g.predict_links_all)` for options -### DBSCAN +### DBSCAN * Enrich UMAP embeddings or featurization dataframe with GPU or CPU DBSCAN @@ -1165,8 +1163,6 @@ Both `hop()` and `chain()` match dictionary expressions support dataframe series * numeric: gt, lt, ge, le, eq, ne, between, isna, notna * string: contains, startswith, endswith, match, isnumeric, isalpha, isdigit, islower, isupper, isspace, isalnum, isdecimal, istitle, isnull, notnull - - #### Table to graph ```python From cf5d3096dcff4244a551ce2f5c8a68e2fea23fea Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Tue, 5 Dec 2023 00:58:54 -0800 Subject: [PATCH 069/104] docs(changelog) --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96fce3472d..a05d5b6d5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +## [0.30.1 - 2023-12-05] + ### Docs * Update readthedocs yml to work around ReadTheDocs v2 yml interpretation regressions From ff8f7ee9f532bd9b4d67c1e37b74a9482351523e Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Tue, 5 Dec 2023 01:05:42 -0800 Subject: [PATCH 070/104] fix(docs): match tag --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a05d5b6d5e..6b1a330bf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] -## [0.30.1 - 2023-12-05] +## [0.31.0 - 2023-12-05] ### Docs From a2992c885519d7e1f828f8659532834e0341cb12 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Tue, 5 Dec 2023 01:06:23 -0800 Subject: [PATCH 071/104] docs(version): clean as 0.31.1 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6b1a330bf8..6642a01b8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] -## [0.31.0 - 2023-12-05] +## [0.31.1 - 2023-12-05] ### Docs From d96039e25b8aacf88402e955487329f192fc2d46 Mon Sep 17 00:00:00 2001 From: Akshat Balyan <89499072+B4K2@users.noreply.github.com> Date: Thu, 7 Dec 2023 18:40:56 +0530 Subject: [PATCH 072/104] Update hop_and_chain_graph_pattern_mining.ipynb --- .../hop_and_chain_graph_pattern_mining.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb b/demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb index af8024a9eb..98cc207cdb 100644 --- a/demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb +++ b/demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb @@ -183,7 +183,7 @@ " for ii, j in enumerate(data[0]['outList'][i]):\n", " edges.append({\n", " 'from': name,\n", - " 'to': names[j],\n", + " 'to': data[0]['usernameList'][j],\n", " 'weight': data[0]['outWeight'][i][ii]\n", " })\n", "edges_df = pd.DataFrame(edges)\n", @@ -2733,4 +2733,4 @@ "outputs": [] } ] -} \ No newline at end of file +} From 5a48d95cf615c330f99469bcc26d99c6a02fa2e2 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Wed, 20 Dec 2023 14:30:40 -0600 Subject: [PATCH 073/104] fix: test change for the register command to use markdown instead of HTML to allow viewing for databricks users --- graphistry/pygraphistry.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/graphistry/pygraphistry.py b/graphistry/pygraphistry.py index f4d00a1b23..2346ab1ab0 100644 --- a/graphistry/pygraphistry.py +++ b/graphistry/pygraphistry.py @@ -147,7 +147,7 @@ def login(username, password, org_name=None, fail_silent=False): """Authenticate and set token for reuse (api=3). If token_refresh_ms (default: 10min), auto-refreshes token. By default, must be reinvoked within 24hr.""" logger.debug("@PyGraphistry login : org_name :{} vs PyGraphistry.org_name() : {}".format(org_name, PyGraphistry.org_name())) - + if not org_name: org_name = PyGraphistry.org_name() @@ -167,7 +167,7 @@ def login(username, password, org_name=None, fail_silent=False): .login(username, password, org_name) .token ) - + logger.debug("@PyGraphistry login After ArrowUploader.login: org_name :{} vs PyGraphistry.org_name() : {}".format(org_name, PyGraphistry.org_name())) PyGraphistry.api_token(token) @@ -246,12 +246,12 @@ def sso_login(org_name=None, idp_name=None, sso_timeout=SSO_GET_TOKEN_ELAPSE_SEC auth_url = arrow_uploader.sso_auth_url # print("auth_url : {}".format(auth_url)) if auth_url and not PyGraphistry.api_token(): - PyGraphistry._handle_auth_url(auth_url, sso_timeout, sso_opt_into_type) + PyGraphistry._handle_auth_url(auth_url, sso_timeout, sso_opt_into_type) return auth_url @staticmethod def _handle_auth_url(auth_url, sso_timeout, sso_opt_into_type): - """Internal function to handle what to do with the auth_url + """Internal function to handle what to do with the auth_url based on the client mode python/ipython console or notebook. :param auth_url: SSO auth url retrieved via API @@ -270,7 +270,8 @@ def _handle_auth_url(auth_url, sso_timeout, sso_opt_into_type): if in_ipython() or in_databricks() or sso_opt_into_type == 'display': # If run in notebook, just display the HTML # from IPython.core.display import HTML from IPython.display import display, HTML - display(HTML(f'Login SSO')) + display(HTML(f'old: Login SSO')) + display(Markdown(f'[new: Login SSO]({auth_url})")) print("Please click the above URL to open browser to login") print(f"If you cannot see the URL, please open browser, browse to this URL: {auth_url}") print("Please close browser tab after SSO login to back to notebook") @@ -290,7 +291,7 @@ def _handle_auth_url(auth_url, sso_timeout, sso_opt_into_type): time.sleep(1) elapsed_time = 1 token = None - + while True: token, org_name = PyGraphistry._sso_get_token() try: @@ -328,7 +329,7 @@ def sso_get_token(): # set org_name to sso org PyGraphistry._config['org_name'] = org_name return token - + @staticmethod def _sso_get_token(): token = None @@ -513,7 +514,7 @@ def api_version(value=None): """Set or get the API version: 1 for 1.0 (deprecated), 3 for 2.0. Setting api=2 (protobuf) fully deprecated from the PyGraphistry client. Also set via environment variable GRAPHISTRY_API_VERSION.""" - + import re if value is None: #if set by env var, interpret @@ -571,7 +572,7 @@ def register( idp_name: Optional[str] = None, is_sso_login: Optional[bool] = False, sso_timeout: Optional[int] = SSO_GET_TOKEN_ELAPSE_SECONDS, - sso_opt_into_type: Optional[Literal["display", "browser"]] = None + sso_opt_into_type: Optional[Literal["display", "browser"]] = None ): """API key registration and server selection @@ -688,7 +689,7 @@ def register( PyGraphistry.set_bolt_driver(bolt) # Reset token creds PyGraphistry.__reset_token_creds_in_memory() - + if not (username is None) and not (password is None): PyGraphistry.login(username, password, org_name) PyGraphistry.api_token(token or PyGraphistry._config['api_token']) @@ -718,7 +719,7 @@ def __check_login_type_to_reset_token_creds( ): if origin_login_type != new_login_type: PyGraphistry.__reset_token_creds_in_memory() - + @staticmethod def privacy( mode: Optional[Mode] = None, @@ -1962,7 +1963,7 @@ def nodes(nodes: Union[Callable, Any], node=None, *args, **kwargs) -> Plottable: **Example** :: - + import graphistry def sample_nodes(g, n): @@ -2308,7 +2309,7 @@ def org_name(value=None): # setter, use switch_org instead if 'org_name' not in PyGraphistry._config or value is not PyGraphistry._config['org_name']: - try: + try: PyGraphistry.switch_org(value.strip()) # PyGraphistry._config['org_name'] = value.strip() except: @@ -2351,7 +2352,7 @@ def scene_settings( point_size: Optional[float] = None, edge_curvature: Optional[float] = None, edge_opacity: Optional[float] = None, - point_opacity: Optional[float] = None, + point_opacity: Optional[float] = None, ): return Plotter().scene_settings( menu, @@ -2422,7 +2423,7 @@ def _handle_api_response(response): logger.error('Error: %s', response, exc_info=True) raise Exception("Unknown Error") - + client_protocol_hostname = PyGraphistry.client_protocol_hostname From 778421b1203157951209508e001168fc8dad30b3 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Wed, 20 Dec 2023 14:41:18 -0600 Subject: [PATCH 074/104] fix syntax error --- graphistry/pygraphistry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphistry/pygraphistry.py b/graphistry/pygraphistry.py index 2346ab1ab0..d03d7afb36 100644 --- a/graphistry/pygraphistry.py +++ b/graphistry/pygraphistry.py @@ -271,7 +271,7 @@ def _handle_auth_url(auth_url, sso_timeout, sso_opt_into_type): # from IPython.core.display import HTML from IPython.display import display, HTML display(HTML(f'old: Login SSO')) - display(Markdown(f'[new: Login SSO]({auth_url})")) + display(Markdown(f'[new: Login SSO]({auth_url})')) print("Please click the above URL to open browser to login") print(f"If you cannot see the URL, please open browser, browse to this URL: {auth_url}") print("Please close browser tab after SSO login to back to notebook") From b8b005453696507958f27bab1fabc2da1da1c163 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Wed, 20 Dec 2023 18:54:32 -0800 Subject: [PATCH 075/104] fix(auth url): revert broken master commits --- graphistry/pygraphistry.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/graphistry/pygraphistry.py b/graphistry/pygraphistry.py index d03d7afb36..98916f6a01 100644 --- a/graphistry/pygraphistry.py +++ b/graphistry/pygraphistry.py @@ -270,8 +270,7 @@ def _handle_auth_url(auth_url, sso_timeout, sso_opt_into_type): if in_ipython() or in_databricks() or sso_opt_into_type == 'display': # If run in notebook, just display the HTML # from IPython.core.display import HTML from IPython.display import display, HTML - display(HTML(f'old: Login SSO')) - display(Markdown(f'[new: Login SSO]({auth_url})')) + display(HTML(f'Login SSO')) print("Please click the above URL to open browser to login") print(f"If you cannot see the URL, please open browser, browse to this URL: {auth_url}") print("Please close browser tab after SSO login to back to notebook") From eea5eeebdf28418178e2ef22c45f120cb713f157 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Wed, 20 Dec 2023 20:33:22 -0800 Subject: [PATCH 076/104] docs(gfql) --- CHANGELOG.md | 4 ++++ README.md | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6642a01b8c..68d03aaf56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +### Docs + +* GFQL in readme.md + ## [0.31.1 - 2023-12-05] ### Docs diff --git a/README.md b/README.md index 5818b142a2..c790ac7d79 100644 --- a/README.md +++ b/README.md @@ -147,9 +147,9 @@ It is easy to turn arbitrary data into insightful graphs. PyGraphistry comes wit g2.plot() ``` -* Cypher-style graph pattern mining queries on dataframes ([ipynb demo](demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb)) +* GFQL: Cypher-style graph pattern mining queries on dataframes ([ipynb demo](demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb)) - Run Cypher-style graph queries natively on dataframes without going to a database or Java: + Run Cypher-style graph queries natively on dataframes without going to a database or Java with GFQL: ```python from graphistry import n, e_undirected, is_in @@ -1133,7 +1133,7 @@ g2.plot() # nodes are values from cols s, d, k1 destination_node_match={"k2": 2}, destination_node_query='k2 == 2 or k2 == 4', ) - .chain([ # filter to subgraph + .chain([ # filter to subgraph with Cypher-style GFQL n(), n({'k2': 0, "m": 'ok'}), #specific values n({'type': is_in(["type1", "type2"])}), #multiple valid values @@ -1156,7 +1156,7 @@ g2.plot() # nodes are values from cols s, d, k1 .collapse(node='some_id', column='some_col', attribute='some val') ``` -Both `hop()` and `chain()` match dictionary expressions support dataframe series *predicates*. The above examples show `is_in([x, y, z, ...])`. Additional predicates include: +Both `hop()` and `chain()` (GFQL) match dictionary expressions support dataframe series *predicates*. The above examples show `is_in([x, y, z, ...])`. Additional predicates include: * categorical: is_in, duplicated * temporal: is_month_start, is_month_end, is_quarter_start, is_quarter_end, is_year_start, is_year_end @@ -1233,7 +1233,7 @@ assert 'pagerank' in g2._nodes.columns #### Graph pattern matching -PyGraphistry supports a PyData-native variant of the popular Cypher graph query language, meaning you can do graph pattern matching directly from Pandas dataframes without installing a database or Java +PyGraphistry supports GFQL, its PyData-native variant of the popular Cypher graph query language, meaning you can do graph pattern matching directly from Pandas dataframes without installing a database or Java See also [graph pattern matching tutorial](demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb) From 8ee50de062212adac3f60e39f0be6b55875bde0c Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Thu, 21 Dec 2023 01:27:16 -0800 Subject: [PATCH 077/104] feat(is_year_end): add missing predicate --- CHANGELOG.md | 3 +++ docs/source/conf.py | 1 + graphistry/__init__.py | 1 + graphistry/compute/__init__.py | 1 + graphistry/compute/ast.py | 1 + 5 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68d03aaf56..c3570d37e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +### Added +* GFQL predicate `is_year_end` + ### Docs * GFQL in readme.md diff --git a/docs/source/conf.py b/docs/source/conf.py index 5d182ca6d0..b3f8ad6cb7 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -81,6 +81,7 @@ ('py:class', 'graphistry.compute.predicates.temporal.IsQuarterEnd'), ('py:class', 'graphistry.compute.predicates.temporal.IsQuarterStart'), ('py:class', 'graphistry.compute.predicates.temporal.IsYearStart'), + ('py:class', 'graphistry.compute.predicates.temporal.IsYearEnd'), ('py:class', 'graphistry.Engine.Engine'), ('py:class', 'graphistry.gremlin.CosmosMixin'), ('py:class', 'graphistry.gremlin.GremlinMixin'), diff --git a/graphistry/__init__.py b/graphistry/__init__.py index 246fdf6cb7..c3e5f6610d 100644 --- a/graphistry/__init__.py +++ b/graphistry/__init__.py @@ -61,6 +61,7 @@ is_quarter_start, IsQuarterStart, is_quarter_end, IsQuarterEnd, is_year_start, IsYearStart, + is_year_end, IsYearEnd, is_leap_year, IsLeapYear, gt, GT, diff --git a/graphistry/compute/__init__.py b/graphistry/compute/__init__.py index d321b0915e..5065246bd9 100644 --- a/graphistry/compute/__init__.py +++ b/graphistry/compute/__init__.py @@ -14,6 +14,7 @@ is_quarter_start, IsQuarterStart, is_quarter_end, IsQuarterEnd, is_year_start, IsYearStart, + is_year_end, IsYearEnd, is_leap_year, IsLeapYear ) from .predicates.numeric import ( diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index bc45c7fa4b..93e3189f6b 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -17,6 +17,7 @@ is_quarter_start, IsQuarterStart, is_quarter_end, IsQuarterEnd, is_year_start, IsYearStart, + is_year_end, IsYearEnd, is_leap_year, IsLeapYear ) from .predicates.numeric import ( From f900bd154cfefdeb24e242337ced85bb7acdd0ff Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Thu, 21 Dec 2023 01:28:40 -0800 Subject: [PATCH 078/104] refactor(repr): remove unnecessary --- graphistry/compute/ast.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 93e3189f6b..c188c26d95 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -253,9 +253,6 @@ def __init__(self, edge_query=edge_query ) - def __repr__(self) -> str: - return f'ASTEdgeForward(edge_match={self._edge_match}, hops={self._hops}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, to_fixed_point={self._to_fixed_point}, name={self._name}, source_node_query={self._source_node_query}, destination_node_query={self._destination_node_query}, edge_query={self._edge_query})' - e_forward = ASTEdgeForward # noqa: E305 class ASTEdgeReverse(ASTEdge): @@ -285,9 +282,6 @@ def __init__(self, destination_node_query=destination_node_query, edge_query=edge_query ) - - def __repr__(self) -> str: - return f'ASTEdgeReverse(edge_match={self._edge_match}, hops={self._hops}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, to_fixed_point={self._to_fixed_point}, name={self._name}, source_node_query={self._source_node_query}, destination_node_query={self._destination_node_query}, edge_query={self._edge_query})' e_reverse = ASTEdgeReverse # noqa: E305 @@ -319,7 +313,4 @@ def __init__(self, edge_query=edge_query ) - def __repr__(self) -> str: - return f'ASTEdgeUndirected(edge_match={self._edge_match}, hops={self._hops}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, to_fixed_point={self._to_fixed_point}, name={self._name}, source_node_query={self._source_node_query}, destination_node_query={self._destination_node_query}, edge_query={self._edge_query})' - e_undirected = ASTEdgeUndirected # noqa: E305 From 4ad0233b39e969c05556c3c5d12bbbfb68fb5d7f Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Thu, 21 Dec 2023 01:29:28 -0800 Subject: [PATCH 079/104] refactor(gfql): e() now undirected edge --- CHANGELOG.md | 4 ++++ graphistry/compute/ast.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3570d37e2..f4930df0b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm * GFQL in readme.md +### Breaking 🔥 + +* GFQL `e()` now aliases `e_undirected` instead of the base class `ASTEdge` + ## [0.31.1 - 2023-12-05] ### Docs diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index c188c26d95..6727df437b 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -223,7 +223,6 @@ def reverse(self) -> 'ASTEdge': destination_node_query=self._source_node_query, edge_query=self._edge_query ) -e = ASTEdge # noqa: E305 class ASTEdgeForward(ASTEdge): """ @@ -314,3 +313,4 @@ def __init__(self, ) e_undirected = ASTEdgeUndirected # noqa: E305 +e = ASTEdgeUndirected # noqa: E305 From 8a943129b1ec7424817f9a6a24aacb042c197b22 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Thu, 21 Dec 2023 01:30:41 -0800 Subject: [PATCH 080/104] refactor(gfql): literal typed Direction --- graphistry/compute/ast.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 6727df437b..38461899a6 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -121,10 +121,11 @@ def reverse(self) -> 'ASTNode': ############################################################################### +Direction = Literal['forward', 'reverse', 'undirected'] DEFAULT_HOPS = 1 DEFAULT_FIXED_POINT = False -DEFAULT_DIRECTION = 'forward' +DEFAULT_DIRECTION: Direction = 'forward' DEFAULT_FILTER_DICT = None class ASTEdge(ASTObject): @@ -133,7 +134,7 @@ class ASTEdge(ASTObject): """ def __init__( self, - direction: Optional[str] = DEFAULT_DIRECTION, + direction: Optional[Direction] = DEFAULT_DIRECTION, edge_match: Optional[dict] = DEFAULT_FILTER_DICT, hops: Optional[int] = DEFAULT_HOPS, to_fixed_point: bool = DEFAULT_FIXED_POINT, @@ -158,7 +159,7 @@ def __init__( self._hops = hops self._to_fixed_point = to_fixed_point - self._direction = direction + self._direction : Direction = direction self._source_node_match = source_node_match self._edge_match = edge_match self._destination_node_match = destination_node_match @@ -206,6 +207,7 @@ def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], ta def reverse(self) -> 'ASTEdge': # updates both edges and nodes + direction : Direction if self._direction == 'reverse': direction = 'forward' elif self._direction == 'forward': From 90851d6ca42a1deebc55e8c7e9353300ea3c25d3 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Thu, 21 Dec 2023 01:30:56 -0800 Subject: [PATCH 081/104] refactor(gfql): literal typed Direction --- graphistry/compute/ast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 38461899a6..93ad93ef40 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -1,5 +1,5 @@ import logging -from typing import Optional, cast +from typing_extensions import Literal import pandas as pd from graphistry.Plottable import Plottable From ece0924ba0dcd4620a9f4907a8309d0291a8bf76 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Thu, 21 Dec 2023 01:31:48 -0800 Subject: [PATCH 082/104] feat(gfql): chain serialization --- CHANGELOG.md | 2 + graphistry/compute/ast.py | 144 ++++++++++++- graphistry/compute/chain.py | 19 +- graphistry/compute/predicates/ASTPredicate.py | 11 + graphistry/compute/predicates/categorical.py | 15 ++ graphistry/compute/predicates/from_json.py | 37 ++++ graphistry/compute/predicates/is_in.py | 21 ++ graphistry/compute/predicates/numeric.py | 129 +++++++++++- graphistry/compute/predicates/str.py | 191 +++++++++++++++++- graphistry/compute/predicates/temporal.py | 62 ++++++ .../compute/predicates/test_categorical.py | 15 ++ .../compute/predicates/test_from_json.py | 27 +++ .../tests/compute/predicates/test_is_in.py | 16 ++ .../tests/compute/predicates/test_numeric.py | 16 ++ .../tests/compute/predicates/test_str.py | 14 ++ .../tests/compute/predicates/test_temporal.py | 13 ++ graphistry/tests/compute/test_ast.py | 23 +++ graphistry/tests/compute/test_chain.py | 38 ++++ graphistry/tests/test_util.py | 63 ++++++ graphistry/util.py | 11 + 20 files changed, 856 insertions(+), 11 deletions(-) create mode 100644 graphistry/compute/predicates/from_json.py create mode 100644 graphistry/tests/compute/predicates/test_categorical.py create mode 100644 graphistry/tests/compute/predicates/test_from_json.py create mode 100644 graphistry/tests/compute/predicates/test_is_in.py create mode 100644 graphistry/tests/compute/predicates/test_numeric.py create mode 100644 graphistry/tests/compute/predicates/test_str.py create mode 100644 graphistry/tests/compute/predicates/test_temporal.py create mode 100644 graphistry/tests/compute/test_ast.py create mode 100644 graphistry/tests/compute/test_chain.py create mode 100644 graphistry/tests/test_util.py diff --git a/CHANGELOG.md b/CHANGELOG.md index f4930df0b9..e7761fba2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] ### Added + +* GFQL query serialization: `graphistry.compute.from_json(graphistry.compute.to_json([...]))` * GFQL predicate `is_year_end` ### Docs diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 93ad93ef40..7e892959e1 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -1,9 +1,11 @@ +from abc import abstractmethod import logging +from typing import Dict, Optional, Union, cast from typing_extensions import Literal import pandas as pd from graphistry.Plottable import Plottable -from graphistry.util import setup_logger +from graphistry.util import is_json_serializable, setup_logger from .predicates.ASTPredicate import ASTPredicate from .predicates.is_in import ( is_in, IsIn @@ -66,12 +68,43 @@ def __init__(self, name: Optional[str] = None): self._name = name pass + @abstractmethod def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], target_wave_front: Optional[pd.DataFrame]) -> Plottable: raise RuntimeError('__call__ not implemented') + @abstractmethod def reverse(self) -> 'ASTObject': raise RuntimeError('reverse not implemented') + + @abstractmethod + def to_json(self, validate=True) -> dict: + raise NotImplementedError() + def validate(self) -> None: + pass + + +############################################################################## + + +def assert_record_match(d: Dict) -> None: + assert isinstance(d, dict) + for k, v in d.items(): + assert isinstance(k, str) + assert isinstance(v, ASTPredicate) or is_json_serializable(v) + +def maybe_filter_dict_from_json(d: Dict, key: str) -> Optional[Dict]: + if key not in d: + return None + if key in d and isinstance(d[key], dict): + return { + k: ASTPredicate.from_json(v) if isinstance(v, dict) else v + for k, v in d[key].items() + } + elif key in d and d[key] is not None: + raise ValueError('filter_dict must be a dict or None') + else: + return None ############################################################################## @@ -91,6 +124,36 @@ def __init__(self, filter_dict: Optional[dict] = None, name: Optional[str] = Non def __repr__(self) -> str: return f'ASTNode(filter_dict={self._filter_dict}, name={self._name})' + + def validate(self) -> None: + if self._filter_dict is not None: + assert_record_match(self._filter_dict) + if self._name is not None: + assert isinstance(self._name, str) + if self._query is not None: + assert isinstance(self._query, str) + + def to_json(self, validate=True) -> dict: + return { + 'type': 'Node', + 'filter_dict': { + k: v.to_json() if isinstance(v, ASTPredicate) else v + for k, v in self._filter_dict.items() + if v is not None + } if self._filter_dict is not None else {}, + **({'name': self._name} if self._name is not None else {}), + **({'query': self._query } if self._query is not None else {}) + } + + @classmethod + def from_json(cls, d: dict) -> 'ASTNode': + out = ASTNode( + filter_dict=maybe_filter_dict_from_json(d, 'filter_dict'), + name=d['name'] if 'name' in d else None, + query=d['query'] if 'query' in d else None + ) + out.validate() + return out def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], target_wave_front: Optional[pd.DataFrame]) -> Plottable: out_g = (g @@ -170,6 +233,71 @@ def __init__( def __repr__(self) -> str: return f'ASTEdge(direction={self._direction}, edge_match={self._edge_match}, hops={self._hops}, to_fixed_point={self._to_fixed_point}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, name={self._name}, source_node_query={self._source_node_query}, destination_node_query={self._destination_node_query}, edge_query={self._edge_query})' + def validate(self) -> None: + assert self._hops is None or isinstance(self._hops, int) + assert isinstance(self._to_fixed_point, bool) + assert self._direction in ['forward', 'reverse', 'undirected'] + if self._source_node_match is not None: + assert_record_match(self._source_node_match) + if self._edge_match is not None: + assert_record_match(self._edge_match) + if self._destination_node_match is not None: + assert_record_match(self._destination_node_match) + if self._name is not None: + assert isinstance(self._name, str) + if self._source_node_query is not None: + assert isinstance(self._source_node_query, str) + if self._destination_node_query is not None: + assert isinstance(self._destination_node_query, str) + if self._edge_query is not None: + assert isinstance(self._edge_query, str) + + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return { + 'type': 'Edge', + 'hops': self._hops, + 'to_fixed_point': self._to_fixed_point, + 'direction': self._direction, + **({'source_node_match': { + k: v.to_json() if isinstance(v, ASTPredicate) else v + for k, v in self._source_node_match.items() + if v is not None + }} if self._source_node_match is not None else {}), + **({'edge_match': { + k: v.to_json() if isinstance(v, ASTPredicate) else v + for k, v in self._edge_match.items() + if v is not None + }} if self._edge_match is not None else {}), + **({'destination_node_match': { + k: v.to_json() if isinstance(v, ASTPredicate) else v + for k, v in self._destination_node_match.items() + if v is not None + }} if self._destination_node_match is not None else {}), + **({'name': self._name} if self._name is not None else {}), + **({'source_node_query': self._source_node_query} if self._source_node_query is not None else {}), + **({'destination_node_query': self._destination_node_query} if self._destination_node_query is not None else {}), + **({'edge_query': self._edge_query} if self._edge_query is not None else {}) + } + + @classmethod + def from_json(cls, d: dict) -> 'ASTEdge': + out = ASTEdge( + direction=d['direction'] if 'direction' in d else None, + edge_match=maybe_filter_dict_from_json(d, 'edge_match'), + hops=d['hops'] if 'hops' in d else None, + to_fixed_point=d['to_fixed_point'] if 'to_fixed_point' in d else None, + source_node_match=maybe_filter_dict_from_json(d, 'source_node_match'), + destination_node_match=maybe_filter_dict_from_json(d, 'destination_node_match'), + source_node_query=d['source_node_query'] if 'source_node_query' in d else None, + destination_node_query=d['destination_node_query'] if 'destination_node_query' in d else None, + edge_query=d['edge_query'] if 'edge_query' in d else None, + name=d['name'] if 'name' in d else None + ) + out.validate() + return out + def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], target_wave_front: Optional[pd.DataFrame]) -> Plottable: if logger.isEnabledFor(logging.DEBUG): @@ -316,3 +444,17 @@ def __init__(self, e_undirected = ASTEdgeUndirected # noqa: E305 e = ASTEdgeUndirected # noqa: E305 + +### + +def from_json(o: Dict) -> Union[ASTNode, ASTEdge]: + assert isinstance(o, dict) + assert 'type' in o + out : Union[ASTNode, ASTEdge] + if o['type'] == 'Node': + out = ASTNode.from_json(o) + elif o['type'] == 'Edge': + out = ASTEdge.from_json(o) + else: + raise ValueError(f'Unknown type {o["type"]}') + return out diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index a3132ca624..44e0133138 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -1,9 +1,9 @@ -from typing import cast, List, Tuple +from typing import Dict, cast, List, Tuple import pandas as pd from graphistry.Plottable import Plottable from graphistry.util import setup_logger -from .ast import ASTObject, ASTNode, ASTEdge +from .ast import ASTObject, ASTNode, ASTEdge, from_json as ASTObject_from_json logger = setup_logger(__name__) @@ -253,3 +253,18 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: g_out = g.nodes(final_nodes_df).edges(final_edges_df) return g_out + +### + +def from_json(d: Dict) -> List[ASTObject]: + """ + Convert a JSON AST into a list of ASTObjects + """ + assert isinstance(d, list) + return [ASTObject_from_json(op) for op in d] + +def to_json(ops: List[ASTObject]) -> List[Dict]: + """ + Convert a list of ASTObjects into a JSON AST + """ + return [op.to_json() for op in ops] diff --git a/graphistry/compute/predicates/ASTPredicate.py b/graphistry/compute/predicates/ASTPredicate.py index 24c2d08bc8..b5621b5a07 100644 --- a/graphistry/compute/predicates/ASTPredicate.py +++ b/graphistry/compute/predicates/ASTPredicate.py @@ -10,4 +10,15 @@ class ASTPredicate(): @abstractmethod def __call__(self, s: pd.Series) -> pd.Series: + raise NotImplementedError() + + @abstractmethod + def to_json(self, validate=True) -> dict: + raise NotImplementedError() + + @classmethod + def from_json(cls, d: dict) -> 'ASTPredicate': + raise NotImplementedError() + + def validate(self) -> None: pass diff --git a/graphistry/compute/predicates/categorical.py b/graphistry/compute/predicates/categorical.py index bcc08c7c84..6b98f5cfe5 100644 --- a/graphistry/compute/predicates/categorical.py +++ b/graphistry/compute/predicates/categorical.py @@ -10,6 +10,21 @@ def __init__(self, keep: Literal['first', 'last', False] = 'first') -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.duplicated(keep=self.keep) + def validate(self) -> None: + assert self.keep in ['first', 'last', False] + + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return {'type': 'Duplicated', 'keep': self.keep} + + @classmethod + def from_json(cls, d: dict) -> 'Duplicated': + assert 'keep' in d + out = Duplicated(keep=d['keep']) + out.validate() + return out + def duplicated(keep: Literal['first', 'last', False] = 'first') -> Duplicated: """ Return whether a given value is duplicated diff --git a/graphistry/compute/predicates/from_json.py b/graphistry/compute/predicates/from_json.py new file mode 100644 index 0000000000..cab27531b1 --- /dev/null +++ b/graphistry/compute/predicates/from_json.py @@ -0,0 +1,37 @@ +from typing import Dict, List, Type + +from graphistry.compute.predicates.ASTPredicate import ASTPredicate +from graphistry.compute.predicates.categorical import Duplicated +from graphistry.compute.predicates.is_in import IsIn +from graphistry.compute.predicates.numeric import GT, LT, GE, LE, EQ, NE, Between, IsNA, NotNA +from graphistry.compute.predicates.str import ( + Contains, Startswith, Endswith, Match, IsNumeric, IsAlpha, IsDecimal, IsDigit, IsLower, IsUpper, + IsSpace, IsAlnum, IsTitle, IsNull, NotNull +) +from graphistry.compute.predicates.temporal import ( + IsMonthStart, IsMonthEnd, IsQuarterStart, IsQuarterEnd, + IsYearStart, IsYearEnd, IsLeapYear +) + +predicates : List[Type[ASTPredicate]] = [ + Duplicated, + IsIn, + GT, LT, GE, LE, EQ, NE, Between, IsNA, NotNA, + Contains, Startswith, Endswith, Match, IsNumeric, IsAlpha, IsDecimal, IsDigit, IsLower, IsUpper, + IsSpace, IsAlnum, IsDecimal, IsTitle, IsNull, NotNull, + IsMonthStart, IsMonthEnd, IsQuarterStart, IsQuarterEnd, + IsYearStart, IsYearEnd, IsLeapYear +] + +type_to_predicate: Dict[str, Type[ASTPredicate]] = { + cls.__name__: cls + for cls in predicates +} + +def from_json(d: Dict) -> ASTPredicate: + assert isinstance(d, dict) + assert 'type' in d + assert d['type'] in type_to_predicate + out = type_to_predicate[d['type']].from_json(d) + out.validate() + return out diff --git a/graphistry/compute/predicates/is_in.py b/graphistry/compute/predicates/is_in.py index 77c9f2505a..64a2605f55 100644 --- a/graphistry/compute/predicates/is_in.py +++ b/graphistry/compute/predicates/is_in.py @@ -1,6 +1,8 @@ from typing import Any, List import pandas as pd +from graphistry.util import assert_json_serializable + from .ASTPredicate import ASTPredicate @@ -10,6 +12,25 @@ def __init__(self, options: List[Any]) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.isin(self.options) + + def validate(self) -> None: + assert isinstance(self.options, list) + assert_json_serializable(self.options) + + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return { + 'type': 'IsIn', + 'options': self.options + } + + @classmethod + def from_json(cls, d: dict) -> 'IsIn': + assert 'options' in d + out = IsIn(options=d['options']) + out.validate() + return out def is_in(options: List[Any]) -> IsIn: return IsIn(options) diff --git a/graphistry/compute/predicates/numeric.py b/graphistry/compute/predicates/numeric.py index d17b07bc0c..64fde67ee0 100644 --- a/graphistry/compute/predicates/numeric.py +++ b/graphistry/compute/predicates/numeric.py @@ -1,80 +1,162 @@ -from typing import Optional +from typing import Union import pandas as pd from .ASTPredicate import ASTPredicate -class GT(ASTPredicate): + +class NumericASTPredicate(ASTPredicate): + def __init__(self, val: Union[int, float]) -> None: + self.val = val + + def validate(self) -> None: + assert isinstance(self.val, (int, float)) + +### + +class GT(NumericASTPredicate): def __init__(self, val: float) -> None: self.val = val def __call__(self, s: pd.Series) -> pd.Series: return s > self.val + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return {'type': 'GT', 'val': self.val} + + @classmethod + def from_json(cls, d: dict) -> 'GT': + assert 'val' in d + out = GT(val=d['val']) + out.validate() + return out + def gt(val: float) -> GT: """ Return whether a given value is greater than a threshold """ return GT(val) -class LT(ASTPredicate): +class LT(NumericASTPredicate): def __init__(self, val: float) -> None: self.val = val def __call__(self, s: pd.Series) -> pd.Series: return s < self.val + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return {'type': 'LT', 'val': self.val} + + @classmethod + def from_json(cls, d: dict) -> 'LT': + assert 'val' in d + out = LT(val=d['val']) + out.validate() + return out + def lt(val: float) -> LT: """ Return whether a given value is less than a threshold """ return LT(val) -class GE(ASTPredicate): +class GE(NumericASTPredicate): def __init__(self, val: float) -> None: self.val = val def __call__(self, s: pd.Series) -> pd.Series: return s >= self.val + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return {'type': 'GE', 'val': self.val} + + @classmethod + def from_json(cls, d: dict) -> 'GE': + assert 'val' in d + out = GE(val=d['val']) + out.validate() + return out + def ge(val: float) -> GE: """ Return whether a given value is greater than or equal to a threshold """ return GE(val) -class LE(ASTPredicate): +class LE(NumericASTPredicate): def __init__(self, val: float) -> None: self.val = val def __call__(self, s: pd.Series) -> pd.Series: return s <= self.val + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return {'type': 'LE', 'val': self.val} + + @classmethod + def from_json(cls, d: dict) -> 'LE': + assert 'val' in d + out = LE(val=d['val']) + out.validate() + return out + def le(val: float) -> LE: """ Return whether a given value is less than or equal to a threshold """ return LE(val) -class EQ(ASTPredicate): +class EQ(NumericASTPredicate): def __init__(self, val: float) -> None: self.val = val def __call__(self, s: pd.Series) -> pd.Series: return s == self.val + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return {'type': 'EQ', 'val': self.val} + + @classmethod + def from_json(cls, d: dict) -> 'EQ': + assert 'val' in d + out = EQ(val=d['val']) + out.validate() + return out + def eq(val: float) -> EQ: """ Return whether a given value is equal to a threshold """ return EQ(val) -class NE(ASTPredicate): +class NE(NumericASTPredicate): def __init__(self, val: float) -> None: self.val = val def __call__(self, s: pd.Series) -> pd.Series: return s != self.val + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return {'type': 'NE', 'val': self.val} + + @classmethod + def from_json(cls, d: dict) -> 'NE': + assert 'val' in d + out = NE(val=d['val']) + out.validate() + return out + def ne(val: float) -> NE: """ Return whether a given value is not equal to a threshold @@ -92,6 +174,25 @@ def __call__(self, s: pd.Series) -> pd.Series: return (s >= self.lower) & (s <= self.upper) else: return (s > self.lower) & (s < self.upper) + + def validate(self) -> None: + assert isinstance(self.lower, (int, float)) + assert isinstance(self.upper, (int, float)) + assert isinstance(self.inclusive, bool) + + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return {'type': 'Between', 'lower': self.lower, 'upper': self.upper, 'inclusive': self.inclusive} + + @classmethod + def from_json(cls, d: dict) -> 'Between': + assert 'lower' in d + assert 'upper' in d + assert 'inclusive' in d + out = Between(lower=d['lower'], upper=d['upper'], inclusive=d['inclusive']) + out.validate() + return out def between(lower: float, upper: float, inclusive: bool = True) -> Between: """ @@ -102,6 +203,13 @@ def between(lower: float, upper: float, inclusive: bool = True) -> Between: class IsNA(ASTPredicate): def __call__(self, s: pd.Series) -> pd.Series: return s.isna() + + def to_json(self, validate=True) -> dict: + return {'type': 'IsNA'} + + @classmethod + def from_json(cls, d: dict) -> 'IsNA': + return IsNA() def isna() -> IsNA: """ @@ -113,6 +221,13 @@ def isna() -> IsNA: class NotNA(ASTPredicate): def __call__(self, s: pd.Series) -> pd.Series: return s.notna() + + def to_json(self, validate=True) -> dict: + return {'type': 'NotNA'} + + @classmethod + def from_json(cls, d: dict) -> 'NotNA': + return NotNA() def notna() -> NotNA: """ diff --git a/graphistry/compute/predicates/str.py b/graphistry/compute/predicates/str.py index 14a8ae2de5..fb81cc5ddf 100644 --- a/graphistry/compute/predicates/str.py +++ b/graphistry/compute/predicates/str.py @@ -14,6 +14,41 @@ def __init__(self, pat: str, case: bool = True, flags: int = 0, na: Optional[boo def __call__(self, s: pd.Series) -> pd.Series: return s.str.contains(self.pat, self.case, self.flags, self.na, self.regex) + + def validate(self) -> None: + assert isinstance(self.pat, str) + assert isinstance(self.case, bool) + assert isinstance(self.flags, int) + assert isinstance(self.na, (bool, type(None))) + assert isinstance(self.regex, bool) + + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return { + 'type': 'Contains', + 'pat': self.pat, + 'case': self.case, + 'flags': self.flags, + **({'na': self.na} if self.na is not None else {}), + 'regex': self.regex + } + + @classmethod + def from_json(cls, d: dict) -> 'Contains': + assert 'pat' in d + assert 'case' in d + assert 'flags' in d + assert 'regex' in d + out = Contains( + pat=d['pat'], + case=d['case'], + flags=d['flags'], + na=d['na'] if 'na' in d else None, + regex=d['regex'] + ) + out.validate() + return out def contains(pat: str, case: bool = True, flags: int = 0, na: Optional[bool] = None, regex: bool = True) -> Contains: """ @@ -30,6 +65,29 @@ def __init__(self, pat: str, na: Optional[str] = None) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.str.startswith(self.pat, self.na) + def validate(self) -> None: + assert isinstance(self.pat, str) + assert isinstance(self.na, (str, type(None))) + + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return { + 'type': 'Startswith', + 'pat': self.pat, + **({'na': self.na} if self.na is not None else {}) + } + + @classmethod + def from_json(cls, d: dict) -> 'Startswith': + assert 'pat' in d + out = Startswith( + pat=d['pat'], + na=d['na'] if 'na' in d else None + ) + out.validate() + return out + def startswith(pat: str, na: Optional[str] = None) -> Startswith: """ Return whether a given pattern is at the start of a string @@ -47,6 +105,29 @@ def __call__(self, s: pd.Series) -> pd.Series: """ return s.str.endswith(self.pat, self.na) + def validate(self) -> None: + assert isinstance(self.pat, str) + assert isinstance(self.na, (str, type(None))) + + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return { + 'type': 'Endswith', + 'pat': self.pat, + **({'na': self.na} if self.na is not None else {}) + } + + @classmethod + def from_json(cls, d: dict) -> 'Endswith': + assert 'pat' in d + out = Endswith( + pat=d['pat'], + na=d['na'] if 'na' in d else None + ) + out.validate() + return out + def endswith(pat: str, na: Optional[str] = None) -> Endswith: return Endswith(pat, na) @@ -59,6 +140,37 @@ def __init__(self, pat: str, case: bool = True, flags: int = 0, na: Optional[boo def __call__(self, s: pd.Series) -> pd.Series: return s.str.match(self.pat, self.case, self.flags, self.na) + + def validate(self) -> None: + assert isinstance(self.pat, str) + assert isinstance(self.case, bool) + assert isinstance(self.flags, int) + assert isinstance(self.na, (bool, type(None))) + + def to_json(self, validate=True) -> dict: + if validate: + self.validate() + return { + 'type': 'Match', + 'pat': self.pat, + 'case': self.case, + 'flags': self.flags, + **({'na': self.na} if self.na is not None else {}) + } + + @classmethod + def from_json(cls, d: dict) -> 'Match': + assert 'pat' in d + assert 'case' in d + assert 'flags' in d + out = Match( + pat=d['pat'], + case=d['case'], + flags=d['flags'], + na=d['na'] if 'na' in d else None + ) + out.validate() + return out def match(pat: str, case: bool = True, flags: int = 0, na: Optional[bool] = None) -> Match: """ @@ -72,7 +184,14 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.str.isnumeric() - + + def to_json(self, validate=True) -> dict: + return {'type': 'IsNumeric'} + + @classmethod + def from_json(cls, d: dict) -> 'IsNumeric': + return IsNumeric() + def isnumeric() -> IsNumeric: """ Return whether a given string is numeric @@ -85,6 +204,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.str.isalpha() + + def to_json(self, validate=True) -> dict: + return {'type': 'IsAlpha'} + + @classmethod + def from_json(cls, d: dict) -> 'IsAlpha': + return IsAlpha() def isalpha() -> IsAlpha: """ @@ -98,6 +224,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.str.isdigit() + + def to_json(self, validate=True) -> dict: + return {'type': 'IsDigit'} + + @classmethod + def from_json(cls, d: dict) -> 'IsDigit': + return IsDigit() def isdigit() -> IsDigit: """ @@ -111,6 +244,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.str.islower() + + def to_json(self, validate=True) -> dict: + return {'type': 'IsLower'} + + @classmethod + def from_json(cls, d: dict) -> 'IsLower': + return IsLower() def islower() -> IsLower: """ @@ -124,6 +264,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.str.isupper() + + def to_json(self, validate=True) -> dict: + return {'type': 'IsUpper'} + + @classmethod + def from_json(cls, d: dict) -> 'IsUpper': + return IsUpper() def isupper() -> IsUpper: """ @@ -138,6 +285,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.str.isspace() + def to_json(self, validate=True) -> dict: + return {'type': 'IsSpace'} + + @classmethod + def from_json(cls, d: dict) -> 'IsSpace': + return IsSpace() + def isspace() -> IsSpace: """ Return whether a given string is whitespace @@ -151,6 +305,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.str.isalnum() + def to_json(self, validate=True) -> dict: + return {'type': 'IsAlnum'} + + @classmethod + def from_json(cls, d: dict) -> 'IsAlnum': + return IsAlnum() + def isalnum() -> IsAlnum: """ Return whether a given string is alphanumeric @@ -163,6 +324,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.str.isdecimal() + + def to_json(self, validate=True) -> dict: + return {'type': 'IsDecimal'} + + @classmethod + def from_json(cls, d: dict) -> 'IsDecimal': + return IsDecimal() def isdecimal() -> IsDecimal: """ @@ -176,6 +344,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.str.istitle() + + def to_json(self, validate=True) -> dict: + return {'type': 'IsTitle'} + + @classmethod + def from_json(cls, d: dict) -> 'IsTitle': + return IsTitle() def istitle() -> IsTitle: """ @@ -189,6 +364,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.isnull() + + def to_json(self, validate=True) -> dict: + return {'type': 'IsNull'} + + @classmethod + def from_json(cls, d: dict) -> 'IsNull': + return IsNull() def isnull() -> IsNull: """ @@ -202,6 +384,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.notnull() + + def to_json(self, validate=True) -> dict: + return {'type': 'NotNull'} + + @classmethod + def from_json(cls, d: dict) -> 'NotNull': + return NotNull() def notnull() -> NotNull: """ diff --git a/graphistry/compute/predicates/temporal.py b/graphistry/compute/predicates/temporal.py index b18984fe97..329e95dcf2 100644 --- a/graphistry/compute/predicates/temporal.py +++ b/graphistry/compute/predicates/temporal.py @@ -9,6 +9,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_month_start + + def to_json(self, validate=True) -> dict: + return {'type': 'IsMonthStart'} + + @classmethod + def from_json(cls, d: dict) -> 'IsMonthStart': + return IsMonthStart() def is_month_start() -> IsMonthStart: """ @@ -22,6 +29,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_month_end + + def to_json(self, validate=True) -> dict: + return {'type': 'IsMonthEnd'} + + @classmethod + def from_json(cls, d: dict) -> 'IsMonthEnd': + return IsMonthEnd() def is_month_end() -> IsMonthEnd: """ @@ -35,6 +49,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_quarter_start + + def to_json(self, validate=True) -> dict: + return {'type': 'IsQuarterStart'} + + @classmethod + def from_json(cls, d: dict) -> 'IsQuarterStart': + return IsQuarterStart() def is_quarter_start() -> IsQuarterStart: """ @@ -48,6 +69,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_quarter_end + + def to_json(self, validate=True) -> dict: + return {'type': 'IsQuarterEnd'} + + @classmethod + def from_json(cls, d: dict) -> 'IsQuarterEnd': + return IsQuarterEnd() def is_quarter_end() -> IsQuarterEnd: """ @@ -61,6 +89,13 @@ def __init__(self) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_year_start + + def to_json(self, validate=True) -> dict: + return {'type': 'IsYearStart'} + + @classmethod + def from_json(cls, d: dict) -> 'IsYearStart': + return IsYearStart() def is_year_start() -> IsYearStart: """ @@ -68,12 +103,39 @@ def is_year_start() -> IsYearStart: """ return IsYearStart() +class IsYearEnd(ASTPredicate): + def __init__(self) -> None: + pass + + def __call__(self, s: pd.Series) -> pd.Series: + return s.dt.is_year_end + + def to_json(self, validate=True) -> dict: + return {'type': 'IsYearEnd'} + + @classmethod + def from_json(cls, d: dict) -> 'IsYearEnd': + return IsYearEnd() + +def is_year_end() -> IsYearEnd: + """ + Return whether a given value is a year end + """ + return IsYearEnd() + class IsLeapYear(ASTPredicate): def __init__(self) -> None: pass def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_leap_year + + def to_json(self, validate=True) -> dict: + return {'type': 'IsLeapYear'} + + @classmethod + def from_json(cls, d: dict) -> 'IsLeapYear': + return IsLeapYear() def is_leap_year() -> IsLeapYear: """ diff --git a/graphistry/tests/compute/predicates/test_categorical.py b/graphistry/tests/compute/predicates/test_categorical.py new file mode 100644 index 0000000000..afdc0393b8 --- /dev/null +++ b/graphistry/tests/compute/predicates/test_categorical.py @@ -0,0 +1,15 @@ +from graphistry.compute.predicates.categorical import Duplicated, duplicated + +def test_duplicated(): + + d = duplicated('last') + assert isinstance(d, Duplicated) + assert d.keep == 'last' + + o = d.to_json() + assert isinstance(o, dict) + assert o['type'] == 'Duplicated' + + d2 = Duplicated.from_json(o) + assert isinstance(d2, Duplicated) + assert d2.keep == 'last' diff --git a/graphistry/tests/compute/predicates/test_from_json.py b/graphistry/tests/compute/predicates/test_from_json.py new file mode 100644 index 0000000000..adafef1bbe --- /dev/null +++ b/graphistry/tests/compute/predicates/test_from_json.py @@ -0,0 +1,27 @@ +from graphistry.compute.predicates.categorical import Duplicated +from graphistry.compute.predicates.from_json import from_json + + +def test_from_json_good(): + d = from_json({'type': 'Duplicated', 'keep': 'last'}) + assert isinstance(d, Duplicated) + assert d.keep == 'last' + +def test_from_json_bad(): + try: + from_json({'type': 'zzz'}) + assert False + except AssertionError: + assert True + + try: + from_json({'type': 'Duplicated', 'keep': 'zzz'}) + assert False + except AssertionError: + assert True + + try: + from_json({'type': 'Duplicated'}) + assert False + except AssertionError: + assert True diff --git a/graphistry/tests/compute/predicates/test_is_in.py b/graphistry/tests/compute/predicates/test_is_in.py new file mode 100644 index 0000000000..8648f3487c --- /dev/null +++ b/graphistry/tests/compute/predicates/test_is_in.py @@ -0,0 +1,16 @@ +from graphistry.compute.predicates.is_in import IsIn, is_in + + +def test_is_in(): + + d = is_in([1, 2, 3]) + assert isinstance(d, IsIn) + assert d.options == [1, 2, 3] + + o = d.to_json() + assert isinstance(o, dict) + assert o['type'] == 'IsIn' + + d2 = IsIn.from_json(o) + assert isinstance(d2, IsIn) + assert d2.options == [1, 2, 3] diff --git a/graphistry/tests/compute/predicates/test_numeric.py b/graphistry/tests/compute/predicates/test_numeric.py new file mode 100644 index 0000000000..b6ce762c60 --- /dev/null +++ b/graphistry/tests/compute/predicates/test_numeric.py @@ -0,0 +1,16 @@ +from graphistry.compute.predicates.numeric import GT, gt + +def test_gt(): + + d = gt(1) + assert isinstance(d, GT) + assert d.val == 1 + + o = d.to_json() + assert isinstance(o, dict) + assert o['type'] == 'GT' + assert o['val'] == 1 + + d2 = GT.from_json(o) + assert isinstance(d2, GT) + assert d2.val == 1 diff --git a/graphistry/tests/compute/predicates/test_str.py b/graphistry/tests/compute/predicates/test_str.py new file mode 100644 index 0000000000..da6875157b --- /dev/null +++ b/graphistry/tests/compute/predicates/test_str.py @@ -0,0 +1,14 @@ +from graphistry.compute.predicates.str import IsUpper, isupper + + +def test_is_upper(): + + d = isupper() + assert isinstance(d, IsUpper) + + o = d.to_json() + assert isinstance(o, dict) + assert o['type'] == 'IsUpper' + + d2 = IsUpper.from_json(o) + assert isinstance(d2, IsUpper) diff --git a/graphistry/tests/compute/predicates/test_temporal.py b/graphistry/tests/compute/predicates/test_temporal.py new file mode 100644 index 0000000000..fe6101edc7 --- /dev/null +++ b/graphistry/tests/compute/predicates/test_temporal.py @@ -0,0 +1,13 @@ +from graphistry.compute.predicates.temporal import IsLeapYear, is_leap_year + +def test_is_leap_year(): + + d = is_leap_year() + assert isinstance(d, IsLeapYear) + + o = d.to_json() + assert isinstance(o, dict) + assert o['type'] == 'IsLeapYear' + + d2 = IsLeapYear.from_json(o) + assert isinstance(d2, IsLeapYear) diff --git a/graphistry/tests/compute/test_ast.py b/graphistry/tests/compute/test_ast.py new file mode 100644 index 0000000000..f08977223b --- /dev/null +++ b/graphistry/tests/compute/test_ast.py @@ -0,0 +1,23 @@ +from graphistry.compute.ast import from_json, ASTNode, ASTEdge, n, e, e_forward, e_reverse, e_undirected + +def test_serialization_node(): + + node = n(query='zzz', name='abc') + o = node.to_json() + node2 = from_json(o) + assert isinstance(node2, ASTNode) + assert node2._query == 'zzz' + assert node2._name == 'abc' + o2 = node2.to_json() + assert o == o2 + +def test_serialization_edge(): + + edge = e(edge_query='zzz', name='abc') + o = edge.to_json() + edge2 = from_json(o) + assert isinstance(edge2, ASTEdge) + assert edge2._edge_query == 'zzz' + assert edge2._name == 'abc' + o2 = edge2.to_json() + assert o == o2 diff --git a/graphistry/tests/compute/test_chain.py b/graphistry/tests/compute/test_chain.py new file mode 100644 index 0000000000..0f3d221c82 --- /dev/null +++ b/graphistry/tests/compute/test_chain.py @@ -0,0 +1,38 @@ +from graphistry.compute.ast import ASTNode, ASTEdge, n, e +from graphistry.compute.chain import to_json as chain_to_json, from_json as chain_from_json + +def test_chain_serialization_mt(): + o = chain_to_json([]) + d = chain_from_json(o) + assert d == [] + assert o == [] + +def test_chain_serialization_node(): + o = chain_to_json([n(query='zzz', name='abc')]) + d = chain_from_json(o) + assert isinstance(d[0], ASTNode) + assert d[0]._query == 'zzz' + assert d[0]._name == 'abc' + o2 = chain_to_json(d) + assert o == o2 + +def test_chain_serialization_edge(): + o = chain_to_json([e(edge_query='zzz', name='abc')]) + d = chain_from_json(o) + assert isinstance(d[0], ASTEdge) + assert d[0]._edge_query == 'zzz' + assert d[0]._name == 'abc' + o2 = chain_to_json(d) + assert o == o2 + +def test_chain_serialization_multi(): + o = chain_to_json([n(query='zzz', name='abc'), e(edge_query='zzz', name='abc')]) + d = chain_from_json(o) + assert isinstance(d[0], ASTNode) + assert d[0]._query == 'zzz' + assert d[0]._name == 'abc' + assert isinstance(d[1], ASTEdge) + assert d[1]._edge_query == 'zzz' + assert d[1]._name == 'abc' + o2 = chain_to_json(d) + assert o == o2 diff --git a/graphistry/tests/test_util.py b/graphistry/tests/test_util.py new file mode 100644 index 0000000000..6e36b44f0c --- /dev/null +++ b/graphistry/tests/test_util.py @@ -0,0 +1,63 @@ +from graphistry.util import assert_json_serializable + +class TestAssertJsonSerializable(): + + def test_primitives(self): + assert_json_serializable(1) + assert_json_serializable(1.0) + assert_json_serializable('a') + assert_json_serializable(True) + assert_json_serializable(None) + + def test_list(self): + assert_json_serializable([]) + assert_json_serializable([1]) + assert_json_serializable([1, 2]) + assert_json_serializable([1, 'a', True, None]) + + def test_dict(self): + assert_json_serializable({}) + assert_json_serializable({'a': 1}) + assert_json_serializable({'a': 1, 'b': 2}) + assert_json_serializable({'a': 1, 'b': 'b', 'c': True, 'd': None}) + + def test_nested(self): + assert_json_serializable({'a': [1]}) + assert_json_serializable({'a': {'b': 1}}) + assert_json_serializable({'a': [{'b': 1}]}) + assert_json_serializable({'a': [{'b': 1}, {'c': 2}]}) + + def test_unserializable(self): + + try: + assert_json_serializable(set()) + assert False, 'Expected exception on set' + except AssertionError: + pass + + try: + assert_json_serializable({'a': set()}) + assert False, 'Expected exception on nested set' + except AssertionError: + pass + + try: + assert_json_serializable({'a': [set()]}) + assert False, 'Expected exception on nested set' + except AssertionError: + pass + + try: + assert_json_serializable({'a': [{'b': set()}]}) + assert False, 'Expected exception on nested set' + except AssertionError: + pass + + class Unserializable: + pass + + try: + assert_json_serializable(Unserializable()) + assert False, 'Expected exception on class' + except AssertionError: + pass diff --git a/graphistry/util.py b/graphistry/util.py index 3498b33572..4bad6740d0 100644 --- a/graphistry/util.py +++ b/graphistry/util.py @@ -1,4 +1,5 @@ import hashlib +import json import logging import os import pandas as pd @@ -418,3 +419,13 @@ def printmd(string, color=None, size=20): # # # matches name of inner function # return wrapper + +def is_json_serializable(data): + try: + json.dumps(data) + return True + except TypeError: + return False + +def assert_json_serializable(data): + assert is_json_serializable(data), f"Data is not JSON-serializable: {data}" From eb3825c3fb80b614da7c77bd9913987482c9feb6 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Thu, 21 Dec 2023 01:55:51 -0800 Subject: [PATCH 083/104] docs(chain serialization) --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index c790ac7d79..8e6808c9dc 100644 --- a/README.md +++ b/README.md @@ -1316,6 +1316,17 @@ print('# end edges: ', len(g3._edges[ g3._edges.final_edge ])) See table above for more predicates like `is_in()` and `gt()` +Queries can be serialized and deserialized, such as for saving and remote execution: + +```python +from graphistry.compute.chain import from_json, to_json + +pattern = [n(), e(), n()] +pattern_json = to_json(pattern) +pattern2 = from_json(pattern_json) +g.chain(pattern2).plot() +``` + #### Pipelining ```python From ae5d7117979055875c5fd0d208226a4298c8a3dc Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Thu, 21 Dec 2023 01:56:01 -0800 Subject: [PATCH 084/104] fix(docs): new class --- docs/source/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index b3f8ad6cb7..c055149334 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -60,6 +60,7 @@ ('py:class', 'graphistry.compute.predicates.numeric.LT'), ('py:class', 'graphistry.compute.predicates.numeric.NE'), ('py:class', 'graphistry.compute.predicates.numeric.NotNA'), + ('py:class', 'graphistry.compute.predicates.numeric.NumericASTPredicate'), ('py:class', 'graphistry.compute.predicates.str.Contains'), ('py:class', 'graphistry.compute.predicates.str.Endswith'), ('py:class', 'graphistry.compute.predicates.str.IsAlnum'), From 78a8b3cbca3f059f48ba2a99939adbf2a8db05c4 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Fri, 22 Dec 2023 14:25:43 -0800 Subject: [PATCH 085/104] feat(json): explicit JSONVal type and helpers --- graphistry/compute/ast.py | 3 ++- graphistry/compute/predicates/is_in.py | 2 +- .../{test_util.py => utils/test_json.py} | 2 +- graphistry/util.py | 10 ------- graphistry/utils/json.py | 27 +++++++++++++++++++ 5 files changed, 31 insertions(+), 13 deletions(-) rename graphistry/tests/{test_util.py => utils/test_json.py} (96%) create mode 100644 graphistry/utils/json.py diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 7e892959e1..bbdc23933a 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -5,7 +5,8 @@ import pandas as pd from graphistry.Plottable import Plottable -from graphistry.util import is_json_serializable, setup_logger +from graphistry.util import setup_logger +from graphistry.utils.json import is_json_serializable from .predicates.ASTPredicate import ASTPredicate from .predicates.is_in import ( is_in, IsIn diff --git a/graphistry/compute/predicates/is_in.py b/graphistry/compute/predicates/is_in.py index 64a2605f55..698a13e7cd 100644 --- a/graphistry/compute/predicates/is_in.py +++ b/graphistry/compute/predicates/is_in.py @@ -1,7 +1,7 @@ from typing import Any, List import pandas as pd -from graphistry.util import assert_json_serializable +from graphistry.utils.json import assert_json_serializable from .ASTPredicate import ASTPredicate diff --git a/graphistry/tests/test_util.py b/graphistry/tests/utils/test_json.py similarity index 96% rename from graphistry/tests/test_util.py rename to graphistry/tests/utils/test_json.py index 6e36b44f0c..cbf8280187 100644 --- a/graphistry/tests/test_util.py +++ b/graphistry/tests/utils/test_json.py @@ -1,4 +1,4 @@ -from graphistry.util import assert_json_serializable +from graphistry.utils.json import assert_json_serializable class TestAssertJsonSerializable(): diff --git a/graphistry/util.py b/graphistry/util.py index 4bad6740d0..c2c47996f1 100644 --- a/graphistry/util.py +++ b/graphistry/util.py @@ -419,13 +419,3 @@ def printmd(string, color=None, size=20): # # # matches name of inner function # return wrapper - -def is_json_serializable(data): - try: - json.dumps(data) - return True - except TypeError: - return False - -def assert_json_serializable(data): - assert is_json_serializable(data), f"Data is not JSON-serializable: {data}" diff --git a/graphistry/utils/json.py b/graphistry/utils/json.py new file mode 100644 index 0000000000..9ddf068443 --- /dev/null +++ b/graphistry/utils/json.py @@ -0,0 +1,27 @@ + +import json +from typing import Any, Dict, List, Union + + +JSONVal = Union[None, bool, str, float, int, List['JSONVal'], Dict[str, 'JSONVal']] + + +def is_json_serializable(data): + try: + json.dumps(data) + return True + except TypeError: + return False + +def assert_json_serializable(data): + assert is_json_serializable(data), f"Data is not JSON-serializable: {data}" + +def serialize_to_json_val(obj: Any) -> JSONVal: + if isinstance(obj, (str, int, float, bool, type(None))): + return obj + elif isinstance(obj, list): + return [serialize_to_json_val(item) for item in obj] + elif isinstance(obj, dict): + return {key: serialize_to_json_val(value) for key, value in obj.items()} + else: + raise TypeError(f"Unsupported type for to_json: {type(obj)}") From 8323935b309f9a6c41d5d304a0b868a270e49837 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Fri, 22 Dec 2023 14:26:46 -0800 Subject: [PATCH 086/104] refactor(GFQL expr serialization): factor out json methods --- graphistry/compute/predicates/ASTPredicate.py | 40 +++- graphistry/compute/predicates/categorical.py | 12 -- graphistry/compute/predicates/from_json.py | 8 +- graphistry/compute/predicates/is_in.py | 15 -- graphistry/compute/predicates/numeric.py | 100 --------- graphistry/compute/predicates/str.py | 190 ------------------ graphistry/compute/predicates/temporal.py | 63 ------ 7 files changed, 36 insertions(+), 392 deletions(-) diff --git a/graphistry/compute/predicates/ASTPredicate.py b/graphistry/compute/predicates/ASTPredicate.py index b5621b5a07..cc6e25b966 100644 --- a/graphistry/compute/predicates/ASTPredicate.py +++ b/graphistry/compute/predicates/ASTPredicate.py @@ -1,24 +1,44 @@ -from abc import abstractmethod +from abc import ABC, abstractmethod +from typing import Any, Dict import pandas as pd +from graphistry.utils.json import JSONVal, serialize_to_json_val -class ASTPredicate(): + +class ASTPredicate(ABC): """ Internal, not intended for use outside of this module. These are fancy columnar predicates used in {k: v, ...} node/edge df matching when going beyond primitive equality """ - @abstractmethod - def __call__(self, s: pd.Series) -> pd.Series: - raise NotImplementedError() + reserved_fields = ['type'] @abstractmethod - def to_json(self, validate=True) -> dict: - raise NotImplementedError() - - @classmethod - def from_json(cls, d: dict) -> 'ASTPredicate': + def __call__(self, s: pd.Series) -> pd.Series: raise NotImplementedError() def validate(self) -> None: pass + + def to_json(self, validate=True) -> Dict[str, JSONVal]: + """ + Returns JSON-compatible dictionry {"type": "ClassName", "arg1": val1, ...} + Emits all non-reserved instance fields + """ + if validate: + self.validate() + data: Dict[str, JSONVal] = {'type': self.__class__.__name__} + for key, value in self.__dict__.items(): + if key not in self.reserved_fields: + data[key] = serialize_to_json_val(value) + return data + + @classmethod + def from_json(cls, d: Dict[str, JSONVal]) -> 'ASTPredicate': + """ + Given c.to_json(), hydrate back c + + Corresponding c.__class__.__init__ must accept all non-reserved instance fields + """ + constructor_args = {k: v for k, v in d.items() if k not in cls.reserved_fields} + return cls(**constructor_args) diff --git a/graphistry/compute/predicates/categorical.py b/graphistry/compute/predicates/categorical.py index 6b98f5cfe5..9d0d0ccb9d 100644 --- a/graphistry/compute/predicates/categorical.py +++ b/graphistry/compute/predicates/categorical.py @@ -13,18 +13,6 @@ def __call__(self, s: pd.Series) -> pd.Series: def validate(self) -> None: assert self.keep in ['first', 'last', False] - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return {'type': 'Duplicated', 'keep': self.keep} - - @classmethod - def from_json(cls, d: dict) -> 'Duplicated': - assert 'keep' in d - out = Duplicated(keep=d['keep']) - out.validate() - return out - def duplicated(keep: Literal['first', 'last', False] = 'first') -> Duplicated: """ Return whether a given value is duplicated diff --git a/graphistry/compute/predicates/from_json.py b/graphistry/compute/predicates/from_json.py index cab27531b1..544cc87cda 100644 --- a/graphistry/compute/predicates/from_json.py +++ b/graphistry/compute/predicates/from_json.py @@ -12,6 +12,8 @@ IsMonthStart, IsMonthEnd, IsQuarterStart, IsQuarterEnd, IsYearStart, IsYearEnd, IsLeapYear ) +from graphistry.utils.json import JSONVal + predicates : List[Type[ASTPredicate]] = [ Duplicated, @@ -28,10 +30,12 @@ for cls in predicates } -def from_json(d: Dict) -> ASTPredicate: +def from_json(d: Dict[str, JSONVal]) -> ASTPredicate: assert isinstance(d, dict) assert 'type' in d assert d['type'] in type_to_predicate - out = type_to_predicate[d['type']].from_json(d) + assert isinstance(d['type'], str) + pred = type_to_predicate[d['type']] + out = pred.from_json(d) out.validate() return out diff --git a/graphistry/compute/predicates/is_in.py b/graphistry/compute/predicates/is_in.py index 698a13e7cd..4803124d78 100644 --- a/graphistry/compute/predicates/is_in.py +++ b/graphistry/compute/predicates/is_in.py @@ -16,21 +16,6 @@ def __call__(self, s: pd.Series) -> pd.Series: def validate(self) -> None: assert isinstance(self.options, list) assert_json_serializable(self.options) - - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return { - 'type': 'IsIn', - 'options': self.options - } - - @classmethod - def from_json(cls, d: dict) -> 'IsIn': - assert 'options' in d - out = IsIn(options=d['options']) - out.validate() - return out def is_in(options: List[Any]) -> IsIn: return IsIn(options) diff --git a/graphistry/compute/predicates/numeric.py b/graphistry/compute/predicates/numeric.py index 64fde67ee0..826996214f 100644 --- a/graphistry/compute/predicates/numeric.py +++ b/graphistry/compute/predicates/numeric.py @@ -20,18 +20,6 @@ def __init__(self, val: float) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s > self.val - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return {'type': 'GT', 'val': self.val} - - @classmethod - def from_json(cls, d: dict) -> 'GT': - assert 'val' in d - out = GT(val=d['val']) - out.validate() - return out - def gt(val: float) -> GT: """ Return whether a given value is greater than a threshold @@ -45,18 +33,6 @@ def __init__(self, val: float) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s < self.val - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return {'type': 'LT', 'val': self.val} - - @classmethod - def from_json(cls, d: dict) -> 'LT': - assert 'val' in d - out = LT(val=d['val']) - out.validate() - return out - def lt(val: float) -> LT: """ Return whether a given value is less than a threshold @@ -70,18 +46,6 @@ def __init__(self, val: float) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s >= self.val - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return {'type': 'GE', 'val': self.val} - - @classmethod - def from_json(cls, d: dict) -> 'GE': - assert 'val' in d - out = GE(val=d['val']) - out.validate() - return out - def ge(val: float) -> GE: """ Return whether a given value is greater than or equal to a threshold @@ -95,18 +59,6 @@ def __init__(self, val: float) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s <= self.val - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return {'type': 'LE', 'val': self.val} - - @classmethod - def from_json(cls, d: dict) -> 'LE': - assert 'val' in d - out = LE(val=d['val']) - out.validate() - return out - def le(val: float) -> LE: """ Return whether a given value is less than or equal to a threshold @@ -120,18 +72,6 @@ def __init__(self, val: float) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s == self.val - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return {'type': 'EQ', 'val': self.val} - - @classmethod - def from_json(cls, d: dict) -> 'EQ': - assert 'val' in d - out = EQ(val=d['val']) - out.validate() - return out - def eq(val: float) -> EQ: """ Return whether a given value is equal to a threshold @@ -145,18 +85,6 @@ def __init__(self, val: float) -> None: def __call__(self, s: pd.Series) -> pd.Series: return s != self.val - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return {'type': 'NE', 'val': self.val} - - @classmethod - def from_json(cls, d: dict) -> 'NE': - assert 'val' in d - out = NE(val=d['val']) - out.validate() - return out - def ne(val: float) -> NE: """ Return whether a given value is not equal to a threshold @@ -180,20 +108,6 @@ def validate(self) -> None: assert isinstance(self.upper, (int, float)) assert isinstance(self.inclusive, bool) - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return {'type': 'Between', 'lower': self.lower, 'upper': self.upper, 'inclusive': self.inclusive} - - @classmethod - def from_json(cls, d: dict) -> 'Between': - assert 'lower' in d - assert 'upper' in d - assert 'inclusive' in d - out = Between(lower=d['lower'], upper=d['upper'], inclusive=d['inclusive']) - out.validate() - return out - def between(lower: float, upper: float, inclusive: bool = True) -> Between: """ Return whether a given value is between a lower and upper threshold @@ -203,13 +117,6 @@ def between(lower: float, upper: float, inclusive: bool = True) -> Between: class IsNA(ASTPredicate): def __call__(self, s: pd.Series) -> pd.Series: return s.isna() - - def to_json(self, validate=True) -> dict: - return {'type': 'IsNA'} - - @classmethod - def from_json(cls, d: dict) -> 'IsNA': - return IsNA() def isna() -> IsNA: """ @@ -221,13 +128,6 @@ def isna() -> IsNA: class NotNA(ASTPredicate): def __call__(self, s: pd.Series) -> pd.Series: return s.notna() - - def to_json(self, validate=True) -> dict: - return {'type': 'NotNA'} - - @classmethod - def from_json(cls, d: dict) -> 'NotNA': - return NotNA() def notna() -> NotNA: """ diff --git a/graphistry/compute/predicates/str.py b/graphistry/compute/predicates/str.py index fb81cc5ddf..43091d630a 100644 --- a/graphistry/compute/predicates/str.py +++ b/graphistry/compute/predicates/str.py @@ -21,34 +21,6 @@ def validate(self) -> None: assert isinstance(self.flags, int) assert isinstance(self.na, (bool, type(None))) assert isinstance(self.regex, bool) - - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return { - 'type': 'Contains', - 'pat': self.pat, - 'case': self.case, - 'flags': self.flags, - **({'na': self.na} if self.na is not None else {}), - 'regex': self.regex - } - - @classmethod - def from_json(cls, d: dict) -> 'Contains': - assert 'pat' in d - assert 'case' in d - assert 'flags' in d - assert 'regex' in d - out = Contains( - pat=d['pat'], - case=d['case'], - flags=d['flags'], - na=d['na'] if 'na' in d else None, - regex=d['regex'] - ) - out.validate() - return out def contains(pat: str, case: bool = True, flags: int = 0, na: Optional[bool] = None, regex: bool = True) -> Contains: """ @@ -68,25 +40,6 @@ def __call__(self, s: pd.Series) -> pd.Series: def validate(self) -> None: assert isinstance(self.pat, str) assert isinstance(self.na, (str, type(None))) - - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return { - 'type': 'Startswith', - 'pat': self.pat, - **({'na': self.na} if self.na is not None else {}) - } - - @classmethod - def from_json(cls, d: dict) -> 'Startswith': - assert 'pat' in d - out = Startswith( - pat=d['pat'], - na=d['na'] if 'na' in d else None - ) - out.validate() - return out def startswith(pat: str, na: Optional[str] = None) -> Startswith: """ @@ -108,25 +61,6 @@ def __call__(self, s: pd.Series) -> pd.Series: def validate(self) -> None: assert isinstance(self.pat, str) assert isinstance(self.na, (str, type(None))) - - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return { - 'type': 'Endswith', - 'pat': self.pat, - **({'na': self.na} if self.na is not None else {}) - } - - @classmethod - def from_json(cls, d: dict) -> 'Endswith': - assert 'pat' in d - out = Endswith( - pat=d['pat'], - na=d['na'] if 'na' in d else None - ) - out.validate() - return out def endswith(pat: str, na: Optional[str] = None) -> Endswith: return Endswith(pat, na) @@ -146,31 +80,6 @@ def validate(self) -> None: assert isinstance(self.case, bool) assert isinstance(self.flags, int) assert isinstance(self.na, (bool, type(None))) - - def to_json(self, validate=True) -> dict: - if validate: - self.validate() - return { - 'type': 'Match', - 'pat': self.pat, - 'case': self.case, - 'flags': self.flags, - **({'na': self.na} if self.na is not None else {}) - } - - @classmethod - def from_json(cls, d: dict) -> 'Match': - assert 'pat' in d - assert 'case' in d - assert 'flags' in d - out = Match( - pat=d['pat'], - case=d['case'], - flags=d['flags'], - na=d['na'] if 'na' in d else None - ) - out.validate() - return out def match(pat: str, case: bool = True, flags: int = 0, na: Optional[bool] = None) -> Match: """ @@ -179,19 +88,10 @@ def match(pat: str, case: bool = True, flags: int = 0, na: Optional[bool] = None return Match(pat, case, flags, na) class IsNumeric(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.str.isnumeric() - def to_json(self, validate=True) -> dict: - return {'type': 'IsNumeric'} - - @classmethod - def from_json(cls, d: dict) -> 'IsNumeric': - return IsNumeric() - def isnumeric() -> IsNumeric: """ Return whether a given string is numeric @@ -199,18 +99,9 @@ def isnumeric() -> IsNumeric: return IsNumeric() class IsAlpha(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.str.isalpha() - - def to_json(self, validate=True) -> dict: - return {'type': 'IsAlpha'} - - @classmethod - def from_json(cls, d: dict) -> 'IsAlpha': - return IsAlpha() def isalpha() -> IsAlpha: """ @@ -219,18 +110,9 @@ def isalpha() -> IsAlpha: return IsAlpha() class IsDigit(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.str.isdigit() - - def to_json(self, validate=True) -> dict: - return {'type': 'IsDigit'} - - @classmethod - def from_json(cls, d: dict) -> 'IsDigit': - return IsDigit() def isdigit() -> IsDigit: """ @@ -239,18 +121,9 @@ def isdigit() -> IsDigit: return IsDigit() class IsLower(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.str.islower() - - def to_json(self, validate=True) -> dict: - return {'type': 'IsLower'} - - @classmethod - def from_json(cls, d: dict) -> 'IsLower': - return IsLower() def islower() -> IsLower: """ @@ -259,18 +132,9 @@ def islower() -> IsLower: return IsLower() class IsUpper(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.str.isupper() - - def to_json(self, validate=True) -> dict: - return {'type': 'IsUpper'} - - @classmethod - def from_json(cls, d: dict) -> 'IsUpper': - return IsUpper() def isupper() -> IsUpper: """ @@ -279,19 +143,10 @@ def isupper() -> IsUpper: return IsUpper() class IsSpace(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.str.isspace() - def to_json(self, validate=True) -> dict: - return {'type': 'IsSpace'} - - @classmethod - def from_json(cls, d: dict) -> 'IsSpace': - return IsSpace() - def isspace() -> IsSpace: """ Return whether a given string is whitespace @@ -299,19 +154,10 @@ def isspace() -> IsSpace: return IsSpace() class IsAlnum(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.str.isalnum() - def to_json(self, validate=True) -> dict: - return {'type': 'IsAlnum'} - - @classmethod - def from_json(cls, d: dict) -> 'IsAlnum': - return IsAlnum() - def isalnum() -> IsAlnum: """ Return whether a given string is alphanumeric @@ -319,18 +165,9 @@ def isalnum() -> IsAlnum: return IsAlnum() class IsDecimal(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.str.isdecimal() - - def to_json(self, validate=True) -> dict: - return {'type': 'IsDecimal'} - - @classmethod - def from_json(cls, d: dict) -> 'IsDecimal': - return IsDecimal() def isdecimal() -> IsDecimal: """ @@ -339,18 +176,9 @@ def isdecimal() -> IsDecimal: return IsDecimal() class IsTitle(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.str.istitle() - - def to_json(self, validate=True) -> dict: - return {'type': 'IsTitle'} - - @classmethod - def from_json(cls, d: dict) -> 'IsTitle': - return IsTitle() def istitle() -> IsTitle: """ @@ -359,18 +187,9 @@ def istitle() -> IsTitle: return IsTitle() class IsNull(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.isnull() - - def to_json(self, validate=True) -> dict: - return {'type': 'IsNull'} - - @classmethod - def from_json(cls, d: dict) -> 'IsNull': - return IsNull() def isnull() -> IsNull: """ @@ -379,18 +198,9 @@ def isnull() -> IsNull: return IsNull() class NotNull(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.notnull() - - def to_json(self, validate=True) -> dict: - return {'type': 'NotNull'} - - @classmethod - def from_json(cls, d: dict) -> 'NotNull': - return NotNull() def notnull() -> NotNull: """ diff --git a/graphistry/compute/predicates/temporal.py b/graphistry/compute/predicates/temporal.py index 329e95dcf2..3858478ede 100644 --- a/graphistry/compute/predicates/temporal.py +++ b/graphistry/compute/predicates/temporal.py @@ -4,18 +4,9 @@ from .ASTPredicate import ASTPredicate class IsMonthStart(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_month_start - - def to_json(self, validate=True) -> dict: - return {'type': 'IsMonthStart'} - - @classmethod - def from_json(cls, d: dict) -> 'IsMonthStart': - return IsMonthStart() def is_month_start() -> IsMonthStart: """ @@ -24,18 +15,9 @@ def is_month_start() -> IsMonthStart: return IsMonthStart() class IsMonthEnd(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_month_end - - def to_json(self, validate=True) -> dict: - return {'type': 'IsMonthEnd'} - - @classmethod - def from_json(cls, d: dict) -> 'IsMonthEnd': - return IsMonthEnd() def is_month_end() -> IsMonthEnd: """ @@ -44,18 +26,9 @@ def is_month_end() -> IsMonthEnd: return IsMonthEnd() class IsQuarterStart(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_quarter_start - - def to_json(self, validate=True) -> dict: - return {'type': 'IsQuarterStart'} - - @classmethod - def from_json(cls, d: dict) -> 'IsQuarterStart': - return IsQuarterStart() def is_quarter_start() -> IsQuarterStart: """ @@ -64,18 +37,9 @@ def is_quarter_start() -> IsQuarterStart: return IsQuarterStart() class IsQuarterEnd(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_quarter_end - - def to_json(self, validate=True) -> dict: - return {'type': 'IsQuarterEnd'} - - @classmethod - def from_json(cls, d: dict) -> 'IsQuarterEnd': - return IsQuarterEnd() def is_quarter_end() -> IsQuarterEnd: """ @@ -84,18 +48,9 @@ def is_quarter_end() -> IsQuarterEnd: return IsQuarterEnd() class IsYearStart(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_year_start - - def to_json(self, validate=True) -> dict: - return {'type': 'IsYearStart'} - - @classmethod - def from_json(cls, d: dict) -> 'IsYearStart': - return IsYearStart() def is_year_start() -> IsYearStart: """ @@ -104,18 +59,9 @@ def is_year_start() -> IsYearStart: return IsYearStart() class IsYearEnd(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_year_end - - def to_json(self, validate=True) -> dict: - return {'type': 'IsYearEnd'} - - @classmethod - def from_json(cls, d: dict) -> 'IsYearEnd': - return IsYearEnd() def is_year_end() -> IsYearEnd: """ @@ -124,18 +70,9 @@ def is_year_end() -> IsYearEnd: return IsYearEnd() class IsLeapYear(ASTPredicate): - def __init__(self) -> None: - pass def __call__(self, s: pd.Series) -> pd.Series: return s.dt.is_leap_year - - def to_json(self, validate=True) -> dict: - return {'type': 'IsLeapYear'} - - @classmethod - def from_json(cls, d: dict) -> 'IsLeapYear': - return IsLeapYear() def is_leap_year() -> IsLeapYear: """ From b1541fa1654cee7e8bc4437b08892b9a9d1b7521 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Fri, 22 Dec 2023 16:03:51 -0800 Subject: [PATCH 087/104] refactor(gfql): chain json use, node/edge field name convention --- CHANGELOG.md | 8 +- README.md | 8 +- graphistry/compute/ASTSerializable.py | 40 +++++ graphistry/compute/__init__.py | 1 + graphistry/compute/ast.py | 152 +++++++++--------- graphistry/compute/chain.py | 66 +++++--- graphistry/compute/predicates/ASTPredicate.py | 35 +--- graphistry/compute/predicates/from_json.py | 1 + graphistry/tests/compute/test_ast.py | 4 +- graphistry/tests/compute/test_chain.py | 52 +++--- 10 files changed, 206 insertions(+), 161 deletions(-) create mode 100644 graphistry/compute/ASTSerializable.py diff --git a/CHANGELOG.md b/CHANGELOG.md index e7761fba2a..b58dc37994 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,19 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ### Added -* GFQL query serialization: `graphistry.compute.from_json(graphistry.compute.to_json([...]))` +* GFQL `Chain` AST object +* GFQL query serialization - `Chain`, `ASTObject`, and `ASTPredict` implement `ASTSerializable` + - Ex:`Chain.from_json(Chain([n(), e(), n()]).to_json())` * GFQL predicate `is_year_end` ### Docs * GFQL in readme.md +### Changes + +* Refactor `ASTEdge`, `ASTNode` field naming convention to match other `ASTSerializable`s + ### Breaking 🔥 * GFQL `e()` now aliases `e_undirected` instead of the base class `ASTEdge` diff --git a/README.md b/README.md index 8e6808c9dc..be64eccaa9 100644 --- a/README.md +++ b/README.md @@ -1319,11 +1319,11 @@ See table above for more predicates like `is_in()` and `gt()` Queries can be serialized and deserialized, such as for saving and remote execution: ```python -from graphistry.compute.chain import from_json, to_json +from graphistry.compute.chain import Chain -pattern = [n(), e(), n()] -pattern_json = to_json(pattern) -pattern2 = from_json(pattern_json) +pattern = Chain([n(), e(), n()]) +pattern_json = pattern.to_json() +pattern2 = Chain.from_json(pattern_json) g.chain(pattern2).plot() ``` diff --git a/graphistry/compute/ASTSerializable.py b/graphistry/compute/ASTSerializable.py new file mode 100644 index 0000000000..4122f189b0 --- /dev/null +++ b/graphistry/compute/ASTSerializable.py @@ -0,0 +1,40 @@ +from abc import ABC, abstractmethod +from typing import Dict +import pandas as pd + +from graphistry.utils.json import JSONVal, serialize_to_json_val + + +class ASTSerializable(ABC): + """ + Internal, not intended for use outside of this module. + Class name becomes o['type'], and all non reserved_fields become JSON-typed key + """ + + reserved_fields = ['type'] + + def validate(self) -> None: + pass + + def to_json(self, validate=True) -> Dict[str, JSONVal]: + """ + Returns JSON-compatible dictionry {"type": "ClassName", "arg1": val1, ...} + Emits all non-reserved instance fields + """ + if validate: + self.validate() + data: Dict[str, JSONVal] = {'type': self.__class__.__name__} + for key, value in self.__dict__.items(): + if key not in self.reserved_fields: + data[key] = serialize_to_json_val(value) + return data + + @classmethod + def from_json(cls, d: Dict[str, JSONVal]) -> 'ASTSerializable': + """ + Given c.to_json(), hydrate back c + + Corresponding c.__class__.__init__ must accept all non-reserved instance fields + """ + constructor_args = {k: v for k, v in d.items() if k not in cls.reserved_fields} + return cls(**constructor_args) diff --git a/graphistry/compute/__init__.py b/graphistry/compute/__init__.py index 5065246bd9..0bed507004 100644 --- a/graphistry/compute/__init__.py +++ b/graphistry/compute/__init__.py @@ -2,6 +2,7 @@ from .ast import ( n, e_forward, e_reverse, e_undirected ) +from .chain import Chain from .predicates.is_in import ( is_in, IsIn ) diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index bbdc23933a..06359e0c8d 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -5,8 +5,9 @@ import pandas as pd from graphistry.Plottable import Plottable +from graphistry.compute.ASTSerializable import ASTSerializable from graphistry.util import setup_logger -from graphistry.utils.json import is_json_serializable +from graphistry.utils.json import JSONVal, is_json_serializable from .predicates.ASTPredicate import ASTPredicate from .predicates.is_in import ( is_in, IsIn @@ -60,7 +61,7 @@ ############################################################################## -class ASTObject(object): +class ASTObject(ASTSerializable): """ Internal, not intended for use outside of this module. These are operator-level expressions used as g.chain(List) @@ -76,13 +77,6 @@ def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], ta @abstractmethod def reverse(self) -> 'ASTObject': raise RuntimeError('reverse not implemented') - - @abstractmethod - def to_json(self, validate=True) -> dict: - raise NotImplementedError() - - def validate(self) -> None: - pass ############################################################################## @@ -120,30 +114,32 @@ def __init__(self, filter_dict: Optional[dict] = None, name: Optional[str] = Non if filter_dict == {}: filter_dict = None - self._filter_dict = filter_dict - self._query = query + self.filter_dict = filter_dict + self.query = query def __repr__(self) -> str: - return f'ASTNode(filter_dict={self._filter_dict}, name={self._name})' + return f'ASTNode(filter_dict={self.filter_dict}, name={self._name})' def validate(self) -> None: - if self._filter_dict is not None: - assert_record_match(self._filter_dict) + if self.filter_dict is not None: + assert_record_match(self.filter_dict) if self._name is not None: assert isinstance(self._name, str) - if self._query is not None: - assert isinstance(self._query, str) + if self.query is not None: + assert isinstance(self.query, str) def to_json(self, validate=True) -> dict: + if validate: + self.validate() return { 'type': 'Node', 'filter_dict': { k: v.to_json() if isinstance(v, ASTPredicate) else v - for k, v in self._filter_dict.items() + for k, v in self.filter_dict.items() if v is not None - } if self._filter_dict is not None else {}, + } if self.filter_dict is not None else {}, **({'name': self._name} if self._name is not None else {}), - **({'query': self._query } if self._query is not None else {}) + **({'query': self.query } if self.query is not None else {}) } @classmethod @@ -159,8 +155,8 @@ def from_json(cls, d: dict) -> 'ASTNode': def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], target_wave_front: Optional[pd.DataFrame]) -> Plottable: out_g = (g .nodes(prev_node_wavefront if prev_node_wavefront is not None else g._nodes) - .filter_nodes_by_dict(self._filter_dict) - .nodes(lambda g_dynamic: g_dynamic._nodes.query(self._query) if self._query is not None else g_dynamic._nodes) + .filter_nodes_by_dict(self.filter_dict) + .nodes(lambda g_dynamic: g_dynamic._nodes.query(self.query) if self.query is not None else g_dynamic._nodes) .edges(g._edges[:0]) ) if target_wave_front is not None: @@ -221,65 +217,65 @@ def __init__( if destination_node_match == {}: destination_node_match = None - self._hops = hops - self._to_fixed_point = to_fixed_point - self._direction : Direction = direction - self._source_node_match = source_node_match - self._edge_match = edge_match - self._destination_node_match = destination_node_match - self._source_node_query = source_node_query - self._destination_node_query = destination_node_query - self._edge_query = edge_query + self.hops = hops + self.to_fixed_point = to_fixed_point + self.direction : Direction = direction + self.source_node_match = source_node_match + self.edge_match = edge_match + self.destination_node_match = destination_node_match + self.source_node_query = source_node_query + self.destination_node_query = destination_node_query + self.edge_query = edge_query def __repr__(self) -> str: - return f'ASTEdge(direction={self._direction}, edge_match={self._edge_match}, hops={self._hops}, to_fixed_point={self._to_fixed_point}, source_node_match={self._source_node_match}, destination_node_match={self._destination_node_match}, name={self._name}, source_node_query={self._source_node_query}, destination_node_query={self._destination_node_query}, edge_query={self._edge_query})' + return f'ASTEdge(direction={self.direction}, edge_match={self.edge_match}, hops={self.hops}, to_fixed_point={self.to_fixed_point}, source_node_match={self.source_node_match}, destination_node_match={self.destination_node_match}, name={self._name}, source_node_query={self.source_node_query}, destination_node_query={self.destination_node_query}, edge_query={self.edge_query})' def validate(self) -> None: - assert self._hops is None or isinstance(self._hops, int) - assert isinstance(self._to_fixed_point, bool) - assert self._direction in ['forward', 'reverse', 'undirected'] - if self._source_node_match is not None: - assert_record_match(self._source_node_match) - if self._edge_match is not None: - assert_record_match(self._edge_match) - if self._destination_node_match is not None: - assert_record_match(self._destination_node_match) + assert self.hops is None or isinstance(self.hops, int) + assert isinstance(self.to_fixed_point, bool) + assert self.direction in ['forward', 'reverse', 'undirected'] + if self.source_node_match is not None: + assert_record_match(self.source_node_match) + if self.edge_match is not None: + assert_record_match(self.edge_match) + if self.destination_node_match is not None: + assert_record_match(self.destination_node_match) if self._name is not None: assert isinstance(self._name, str) - if self._source_node_query is not None: - assert isinstance(self._source_node_query, str) - if self._destination_node_query is not None: - assert isinstance(self._destination_node_query, str) - if self._edge_query is not None: - assert isinstance(self._edge_query, str) + if self.source_node_query is not None: + assert isinstance(self.source_node_query, str) + if self.destination_node_query is not None: + assert isinstance(self.destination_node_query, str) + if self.edge_query is not None: + assert isinstance(self.edge_query, str) def to_json(self, validate=True) -> dict: if validate: self.validate() return { 'type': 'Edge', - 'hops': self._hops, - 'to_fixed_point': self._to_fixed_point, - 'direction': self._direction, + 'hops': self.hops, + 'to_fixed_point': self.to_fixed_point, + 'direction': self.direction, **({'source_node_match': { k: v.to_json() if isinstance(v, ASTPredicate) else v - for k, v in self._source_node_match.items() + for k, v in self.source_node_match.items() if v is not None - }} if self._source_node_match is not None else {}), + }} if self.source_node_match is not None else {}), **({'edge_match': { k: v.to_json() if isinstance(v, ASTPredicate) else v - for k, v in self._edge_match.items() + for k, v in self.edge_match.items() if v is not None - }} if self._edge_match is not None else {}), + }} if self.edge_match is not None else {}), **({'destination_node_match': { k: v.to_json() if isinstance(v, ASTPredicate) else v - for k, v in self._destination_node_match.items() + for k, v in self.destination_node_match.items() if v is not None - }} if self._destination_node_match is not None else {}), + }} if self.destination_node_match is not None else {}), **({'name': self._name} if self._name is not None else {}), - **({'source_node_query': self._source_node_query} if self._source_node_query is not None else {}), - **({'destination_node_query': self._destination_node_query} if self._destination_node_query is not None else {}), - **({'edge_query': self._edge_query} if self._edge_query is not None else {}) + **({'source_node_query': self.source_node_query} if self.source_node_query is not None else {}), + **({'destination_node_query': self.destination_node_query} if self.destination_node_query is not None else {}), + **({'edge_query': self.edge_query} if self.edge_query is not None else {}) } @classmethod @@ -312,17 +308,17 @@ def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], ta out_g = g.hop( nodes=prev_node_wavefront, - hops=self._hops, - to_fixed_point=self._to_fixed_point, - direction=self._direction, - source_node_match=self._source_node_match, - edge_match=self._edge_match, - destination_node_match=self._destination_node_match, + hops=self.hops, + to_fixed_point=self.to_fixed_point, + direction=self.direction, + source_node_match=self.source_node_match, + edge_match=self.edge_match, + destination_node_match=self.destination_node_match, return_as_wave_front=True, target_wave_front=target_wave_front, - source_node_query=self._source_node_query, - destination_node_query=self._destination_node_query, - edge_query=self._edge_query + source_node_query=self.source_node_query, + destination_node_query=self.destination_node_query, + edge_query=self.edge_query ) if self._name is not None: @@ -337,22 +333,22 @@ def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], ta def reverse(self) -> 'ASTEdge': # updates both edges and nodes direction : Direction - if self._direction == 'reverse': + if self.direction == 'reverse': direction = 'forward' - elif self._direction == 'forward': + elif self.direction == 'forward': direction = 'reverse' else: direction = 'undirected' return ASTEdge( direction=direction, - edge_match=self._edge_match, - hops=self._hops, - to_fixed_point=self._to_fixed_point, - source_node_match=self._destination_node_match, - destination_node_match=self._source_node_match, - source_node_query=self._destination_node_query, - destination_node_query=self._source_node_query, - edge_query=self._edge_query + edge_match=self.edge_match, + hops=self.hops, + to_fixed_point=self.to_fixed_point, + source_node_match=self.destination_node_match, + destination_node_match=self.source_node_match, + source_node_query=self.destination_node_query, + destination_node_query=self.source_node_query, + edge_query=self.edge_query ) class ASTEdgeForward(ASTEdge): @@ -448,7 +444,7 @@ def __init__(self, ### -def from_json(o: Dict) -> Union[ASTNode, ASTEdge]: +def from_json(o: JSONVal) -> Union[ASTNode, ASTEdge]: assert isinstance(o, dict) assert 'type' in o out : Union[ASTNode, ASTEdge] diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index 44e0133138..d8536ea1df 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -1,8 +1,10 @@ -from typing import Dict, cast, List, Tuple +from typing import Dict, Union, cast, List, Tuple import pandas as pd from graphistry.Plottable import Plottable +from graphistry.compute.ASTSerializable import ASTSerializable from graphistry.util import setup_logger +from graphistry.utils.json import JSONVal from .ast import ASTObject, ASTNode, ASTEdge, from_json as ASTObject_from_json logger = setup_logger(__name__) @@ -11,6 +13,44 @@ ############################################################################### +class Chain(ASTSerializable): + + def __init__(self, chain: List[ASTObject]) -> None: + self.chain = chain + + def validate(self) -> None: + assert isinstance(self.chain, list) + for op in self.chain: + assert isinstance(op, ASTObject) + op.validate() + + @classmethod + def from_json(cls, d: Dict[str, JSONVal]) -> 'Chain': + """ + Convert a JSON AST into a list of ASTObjects + """ + assert isinstance(d, dict) + assert 'chain' in d + assert isinstance(d['chain'], list) + out = cls([ASTObject_from_json(op) for op in d['chain']]) + out.validate() + return out + + def to_json(self, validate=True) -> Dict[str, JSONVal]: + """ + Convert a list of ASTObjects into a JSON AST + """ + if validate: + self.validate() + return { + 'type': self.__class__.__name__, + 'chain': [op.to_json() for op in self.chain] + } + + +############################################################################### + + def combine_steps(g: Plottable, kind: str, steps: List[Tuple[ASTObject,Plottable]]) -> pd.DataFrame: """ Collect nodes and edges, taking care to deduplicate and tag any names @@ -92,13 +132,15 @@ def combine_steps(g: Plottable, kind: str, steps: List[Tuple[ASTObject,Plottable # ############################################################################### -def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: +def chain(self: Plottable, ops: Union[List[ASTObject], Chain]) -> Plottable: """ - Experimental: Chain a list of operations + Chain a list of ASTObject (node/edge) traversal operations Return subgraph of matches according to the list of node & edge matchers If any matchers are named, add a correspondingly named boolean-valued column to the output + For direct calls, exposes convenience `List[ASTObject]`. Internal operational should prefer `Chain`. + :param ops: List[ASTObject] Various node and edge matchers :returns: Plotter @@ -162,6 +204,9 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: """ + if isinstance(ops, Chain): + ops = ops.chain + if len(ops) == 0: return self @@ -253,18 +298,3 @@ def chain(self: Plottable, ops: List[ASTObject]) -> Plottable: g_out = g.nodes(final_nodes_df).edges(final_edges_df) return g_out - -### - -def from_json(d: Dict) -> List[ASTObject]: - """ - Convert a JSON AST into a list of ASTObjects - """ - assert isinstance(d, list) - return [ASTObject_from_json(op) for op in d] - -def to_json(ops: List[ASTObject]) -> List[Dict]: - """ - Convert a list of ASTObjects into a JSON AST - """ - return [op.to_json() for op in ops] diff --git a/graphistry/compute/predicates/ASTPredicate.py b/graphistry/compute/predicates/ASTPredicate.py index cc6e25b966..0d9b300223 100644 --- a/graphistry/compute/predicates/ASTPredicate.py +++ b/graphistry/compute/predicates/ASTPredicate.py @@ -1,44 +1,15 @@ -from abc import ABC, abstractmethod -from typing import Any, Dict +from abc import abstractmethod import pandas as pd -from graphistry.utils.json import JSONVal, serialize_to_json_val +from graphistry.compute.ASTSerializable import ASTSerializable -class ASTPredicate(ABC): +class ASTPredicate(ASTSerializable): """ Internal, not intended for use outside of this module. These are fancy columnar predicates used in {k: v, ...} node/edge df matching when going beyond primitive equality """ - reserved_fields = ['type'] - @abstractmethod def __call__(self, s: pd.Series) -> pd.Series: raise NotImplementedError() - - def validate(self) -> None: - pass - - def to_json(self, validate=True) -> Dict[str, JSONVal]: - """ - Returns JSON-compatible dictionry {"type": "ClassName", "arg1": val1, ...} - Emits all non-reserved instance fields - """ - if validate: - self.validate() - data: Dict[str, JSONVal] = {'type': self.__class__.__name__} - for key, value in self.__dict__.items(): - if key not in self.reserved_fields: - data[key] = serialize_to_json_val(value) - return data - - @classmethod - def from_json(cls, d: Dict[str, JSONVal]) -> 'ASTPredicate': - """ - Given c.to_json(), hydrate back c - - Corresponding c.__class__.__init__ must accept all non-reserved instance fields - """ - constructor_args = {k: v for k, v in d.items() if k not in cls.reserved_fields} - return cls(**constructor_args) diff --git a/graphistry/compute/predicates/from_json.py b/graphistry/compute/predicates/from_json.py index 544cc87cda..fd248ec73e 100644 --- a/graphistry/compute/predicates/from_json.py +++ b/graphistry/compute/predicates/from_json.py @@ -37,5 +37,6 @@ def from_json(d: Dict[str, JSONVal]) -> ASTPredicate: assert isinstance(d['type'], str) pred = type_to_predicate[d['type']] out = pred.from_json(d) + assert isinstance(out, ASTPredicate) out.validate() return out diff --git a/graphistry/tests/compute/test_ast.py b/graphistry/tests/compute/test_ast.py index f08977223b..61f082d21c 100644 --- a/graphistry/tests/compute/test_ast.py +++ b/graphistry/tests/compute/test_ast.py @@ -6,7 +6,7 @@ def test_serialization_node(): o = node.to_json() node2 = from_json(o) assert isinstance(node2, ASTNode) - assert node2._query == 'zzz' + assert node2.query == 'zzz' assert node2._name == 'abc' o2 = node2.to_json() assert o == o2 @@ -17,7 +17,7 @@ def test_serialization_edge(): o = edge.to_json() edge2 = from_json(o) assert isinstance(edge2, ASTEdge) - assert edge2._edge_query == 'zzz' + assert edge2.edge_query == 'zzz' assert edge2._name == 'abc' o2 = edge2.to_json() assert o == o2 diff --git a/graphistry/tests/compute/test_chain.py b/graphistry/tests/compute/test_chain.py index 0f3d221c82..30760d6b29 100644 --- a/graphistry/tests/compute/test_chain.py +++ b/graphistry/tests/compute/test_chain.py @@ -1,38 +1,38 @@ from graphistry.compute.ast import ASTNode, ASTEdge, n, e -from graphistry.compute.chain import to_json as chain_to_json, from_json as chain_from_json +from graphistry.compute.chain import Chain def test_chain_serialization_mt(): - o = chain_to_json([]) - d = chain_from_json(o) - assert d == [] - assert o == [] + o = Chain([]).to_json() + d = Chain.from_json(o) + assert d.chain == [] + assert o['chain'] == [] def test_chain_serialization_node(): - o = chain_to_json([n(query='zzz', name='abc')]) - d = chain_from_json(o) - assert isinstance(d[0], ASTNode) - assert d[0]._query == 'zzz' - assert d[0]._name == 'abc' - o2 = chain_to_json(d) + o = Chain([n(query='zzz', name='abc')]).to_json() + d = Chain.from_json(o) + assert isinstance(d.chain[0], ASTNode) + assert d.chain[0].query == 'zzz' + assert d.chain[0]._name == 'abc' + o2 = d.to_json() assert o == o2 def test_chain_serialization_edge(): - o = chain_to_json([e(edge_query='zzz', name='abc')]) - d = chain_from_json(o) - assert isinstance(d[0], ASTEdge) - assert d[0]._edge_query == 'zzz' - assert d[0]._name == 'abc' - o2 = chain_to_json(d) + o = Chain([e(edge_query='zzz', name='abc')]).to_json() + d = Chain.from_json(o) + assert isinstance(d.chain[0], ASTEdge) + assert d.chain[0].edge_query == 'zzz' + assert d.chain[0]._name == 'abc' + o2 = d.to_json() assert o == o2 def test_chain_serialization_multi(): - o = chain_to_json([n(query='zzz', name='abc'), e(edge_query='zzz', name='abc')]) - d = chain_from_json(o) - assert isinstance(d[0], ASTNode) - assert d[0]._query == 'zzz' - assert d[0]._name == 'abc' - assert isinstance(d[1], ASTEdge) - assert d[1]._edge_query == 'zzz' - assert d[1]._name == 'abc' - o2 = chain_to_json(d) + o = Chain([n(query='zzz', name='abc'), e(edge_query='zzz', name='abc')]).to_json() + d = Chain.from_json(o) + assert isinstance(d.chain[0], ASTNode) + assert d.chain[0].query == 'zzz' + assert d.chain[0]._name == 'abc' + assert isinstance(d.chain[1], ASTEdge) + assert d.chain[1].edge_query == 'zzz' + assert d.chain[1]._name == 'abc' + o2 = d.to_json() assert o == o2 From 060c0e87540fca59951b1e1a4f7d8035389a608f Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Fri, 22 Dec 2023 16:07:16 -0800 Subject: [PATCH 088/104] refactor(gfql): export Chain --- graphistry/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/graphistry/__init__.py b/graphistry/__init__.py index c3e5f6610d..43bcc8660e 100644 --- a/graphistry/__init__.py +++ b/graphistry/__init__.py @@ -51,6 +51,7 @@ from graphistry.compute import ( n, e_forward, e_reverse, e_undirected, + Chain, is_in, IsIn, From fe2e342afb759bcbe3e3c9dfdc8c4246fadaa905 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Fri, 22 Dec 2023 16:09:12 -0800 Subject: [PATCH 089/104] fix(docs): new types --- docs/source/conf.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index c055149334..50ff684da7 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -48,6 +48,8 @@ ('py:class', '3'), ('py:class', ""), ('py:class', ""), + ('py:class', "graphistry.compute.ASTSerializable.ASTSerializable"), + ('py:class', "graphistry.compute.chain.Chain"), ('py:class', "graphistry.compute.predicates.ASTPredicate.ASTPredicate"), ('py:class', 'graphistry.compute.predicates.categorical.Duplicated'), ('py:class', 'graphistry.compute.predicates.is_in.IsIn'), From 87cb5c7ed996e37fc0ff998320749048a4727e28 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Fri, 22 Dec 2023 16:15:04 -0800 Subject: [PATCH 090/104] docs(changelog): 0.32.0 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b58dc37994..c4a9c837d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +## [0.32.0 - 2023-12-22] + ### Added * GFQL `Chain` AST object From a18800cb50bb8155bc0312c5eb4a6c90764fb173 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 24 Dec 2023 19:20:50 -0800 Subject: [PATCH 091/104] infra(gpu tester): thread LOG_LEVEL --- docker/test-gpu-local.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docker/test-gpu-local.sh b/docker/test-gpu-local.sh index d0d239d023..14d4c27791 100755 --- a/docker/test-gpu-local.sh +++ b/docker/test-gpu-local.sh @@ -10,6 +10,7 @@ WITH_TYPECHECK=${WITH_TYPECHECK:-1} WITH_TEST=${WITH_TEST:-1} WITH_BUILD=${WITH_BUILD:-1} TEST_CPU_VERSION=${TEST_CPU_VERSION:-latest} +LOG_LEVEL=${LOG_LEVEL:-DEBUG} NETWORK="" if [ "$WITH_NEO4J" == "1" ] @@ -39,6 +40,7 @@ docker run \ -e WITH_TYPECHECK=$WITH_TYPECHECK \ -e WITH_TEST=$WITH_TEST \ -e WITH_BUILD=$WITH_BUILD \ + -e LOG_LEVEL=$LOG_LEVEL \ -v "`pwd`/../graphistry:/opt/pygraphistry/graphistry:ro" \ --security-opt seccomp=unconfined \ --rm \ From 5f29bde66d3551fd238add25f722aaa4963c46aa Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 24 Dec 2023 19:21:33 -0800 Subject: [PATCH 092/104] refactor(Engine): Add EngineAbstract --- graphistry/Engine.py | 67 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/graphistry/Engine.py b/graphistry/Engine.py index 5b0eb7e2ff..8753aa71f7 100644 --- a/graphistry/Engine.py +++ b/graphistry/Engine.py @@ -1,5 +1,5 @@ import pandas as pd -from typing import Any +from typing import Any, Optional from enum import Enum @@ -9,11 +9,63 @@ class Engine(Enum): DASK : str = 'dask' DASK_CUDF : str = 'dask_cudf' +class EngineAbstract(Enum): + PANDAS = Engine.PANDAS.value + CUDF = Engine.CUDF.value + DASK = Engine.DASK.value + DASK_CUDF = Engine.DASK_CUDF.value + AUTO = 'auto' + DataframeLike = Any # pdf, cudf, ddf, dgdf DataframeLocalLike = Any # pdf, cudf GraphistryLke = Any +#TODO use new importer when it lands (this is copied from umap_utils) +def lazy_cudf_import_has_dependancy(): + try: + import warnings + + warnings.filterwarnings("ignore") + import cudf # type: ignore + + return True, "ok", cudf + except ModuleNotFoundError as e: + return False, e, None + +def resolve_engine( + engine: EngineAbstract, + g_or_df: Optional[Any] = None, +) -> Engine: + # if an Engine (concrete), just use that + if engine != EngineAbstract.AUTO: + return Engine(engine.value) + + if g_or_df is not None: + # work around circular dependency + from graphistry.Plottable import Plottable + if isinstance(g_or_df, Plottable): + if g_or_df._nodes is not None and g_or_df._edges is not None: + if not isinstance(g_or_df._nodes, type(g_or_df._edges)): + raise ValueError(f'Edges and nodes must be same type for auto engine selection, got: {type(g_or_df._edges)} and {type(g_or_df._nodes)}') + g_or_df = g_or_df._edges if g_or_df._edges is not None else g_or_df._nodes + + if g_or_df is not None: + if isinstance(g_or_df, pd.DataFrame): + return Engine.PANDAS + + has_cudf_dependancy_, _, _ = lazy_cudf_import_has_dependancy() + if has_cudf_dependancy_: + import cudf + if isinstance(g_or_df, cudf.DataFrame): + return Engine.CUDF + raise ValueError(f'Expected cudf dataframe, got: {type(g_or_df)}') + + has_cudf_dependancy_, _, _ = lazy_cudf_import_has_dependancy() + if has_cudf_dependancy_: + return Engine.CUDF + return Engine.PANDAS + def df_to_pdf(df, engine: Engine): if engine == Engine.PANDAS: return df @@ -35,8 +87,7 @@ def df_to_engine(df, engine: Engine): return df else: return cudf.DataFrame.from_pandas(df) - else: - raise ValueError('Only engines pandas/cudf supported') + raise ValueError('Only engines pandas/cudf supported') def df_concat(engine: Engine): if engine == Engine.PANDAS: @@ -44,6 +95,7 @@ def df_concat(engine: Engine): elif engine == Engine.CUDF: import cudf return cudf.concat + raise NotImplementedError("Only pandas/cudf supported") def df_cons(engine: Engine): if engine == Engine.PANDAS: @@ -51,3 +103,12 @@ def df_cons(engine: Engine): elif engine == Engine.CUDF: import cudf return cudf.DataFrame + raise NotImplementedError("Only pandas/cudf supported") + +def s_cons(engine: Engine): + if engine == Engine.PANDAS: + return pd.Series + elif engine == Engine.CUDF: + import cudf + return cudf.Series + raise NotImplementedError("Only pandas/cudf supported") From 25aab669e55d6dfcd4bce727acd8f4e8e7f12a7d Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 24 Dec 2023 19:25:12 -0800 Subject: [PATCH 093/104] refactor(pd): abstract out --- graphistry/Plottable.py | 4 +- graphistry/compute/ComputeMixin.py | 24 +++++----- graphistry/compute/ast.py | 35 ++++++++++++--- graphistry/compute/chain.py | 36 ++++++++++----- graphistry/compute/filter_by_dict.py | 29 +++++++++--- graphistry/compute/hop.py | 45 +++++++++++++------ graphistry/compute/predicates/ASTPredicate.py | 9 +++- graphistry/compute/predicates/categorical.py | 9 +++- graphistry/compute/predicates/is_in.py | 11 +++-- graphistry/compute/predicates/numeric.py | 26 ++++++----- graphistry/compute/predicates/str.py | 38 +++++++++------- graphistry/compute/predicates/temporal.py | 23 ++++++---- graphistry/plugins/cugraph.py | 6 +-- 13 files changed, 206 insertions(+), 89 deletions(-) diff --git a/graphistry/Plottable.py b/graphistry/Plottable.py index 56cb22cc9b..e7589e92ea 100644 --- a/graphistry/Plottable.py +++ b/graphistry/Plottable.py @@ -3,7 +3,7 @@ import pandas as pd from graphistry.plugins_types.cugraph_types import CuGraphKind -from graphistry.Engine import Engine +from graphistry.Engine import Engine, EngineAbstract if TYPE_CHECKING: @@ -149,7 +149,7 @@ def get_degrees( raise RuntimeError('should not happen') return self - def materialize_nodes(self, reuse: bool = True, engine: Union[Engine, Literal['auto']] = 'auto') -> 'Plottable': + def materialize_nodes(self, reuse: bool = True, engine:EngineAbstract = EngineAbstract.AUTO) -> 'Plottable': if 1 + 1: raise RuntimeError('should not happen') return self diff --git a/graphistry/compute/ComputeMixin.py b/graphistry/compute/ComputeMixin.py index a5a0431f00..ea00ad34c8 100644 --- a/graphistry/compute/ComputeMixin.py +++ b/graphistry/compute/ComputeMixin.py @@ -2,7 +2,7 @@ from typing import Any, List, Union, TYPE_CHECKING from typing_extensions import Literal -from graphistry.Engine import Engine +from graphistry.Engine import Engine, EngineAbstract from graphistry.Plottable import Plottable from graphistry.util import setup_logger from .chain import chain as chain_base @@ -28,7 +28,7 @@ def __init__(self, *args, **kwargs): def materialize_nodes( self, reuse: bool = True, - engine: Union[Engine, Literal['auto']] = "auto" + engine: EngineAbstract = EngineAbstract.AUTO ) -> "Plottable": """ Generate g._nodes based on g._edges @@ -72,21 +72,25 @@ def materialize_nodes( return g node_id = g._node if g._node is not None else "id" - if engine == 'auto': + engine_concrete : Engine + if engine == EngineAbstract.AUTO: if isinstance(g._edges, pd.DataFrame): - engine = Engine.PANDAS + engine_concrete = Engine.PANDAS else: try: import cudf if isinstance(g._edges, cudf.DataFrame): - engine = Engine.CUDF + engine_concrete = Engine.CUDF except ImportError: pass - if engine == 'auto': - raise ValueError('Could not determine engine for edges, expected pandas or cudf dataframe, got: {}'.format(type(g._edges))) - if engine == Engine.PANDAS: + if engine == EngineAbstract.AUTO: + raise ValueError('Could not determine engine for edges, expected pandas or cudf dataframe, got: {}'.format(type(g._edges))) + else: + engine_concrete = Engine(engine.value) + + if engine_concrete == Engine.PANDAS: concat_df = pd.concat([g._edges[g._source], g._edges[g._destination]]) - elif engine == Engine.CUDF: + elif engine_concrete == Engine.CUDF: import cudf if isinstance(g._edges, cudf.DataFrame): edges_gdf = g._edges @@ -96,7 +100,7 @@ def materialize_nodes( raise ValueError('Unexpected edges type; convert edges to cudf.DataFrame') concat_df = cudf.concat([edges_gdf[g._source].rename(node_id), edges_gdf[g._destination].rename(node_id)]) else: - raise ValueError('Expected engine to be pandas or cudf, got: {}'.format(engine)) + raise ValueError('Expected engine to be pandas or cudf, got: {}'.format(engine_concrete)) nodes_df = concat_df.rename(node_id).drop_duplicates().to_frame().reset_index(drop=True) return g.nodes(nodes_df, node_id) diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 06359e0c8d..83c1d9020a 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -1,8 +1,9 @@ from abc import abstractmethod import logging -from typing import Dict, Optional, Union, cast +from typing import Any, TYPE_CHECKING, Dict, Optional, Union, cast from typing_extensions import Literal import pandas as pd +from graphistry.Engine import Engine from graphistry.Plottable import Plottable from graphistry.compute.ASTSerializable import ASTSerializable @@ -58,6 +59,12 @@ logger = setup_logger(__name__) +if TYPE_CHECKING: + DataFrameT = pd.DataFrame +else: + DataFrameT = Any + + ############################################################################## @@ -71,7 +78,13 @@ def __init__(self, name: Optional[str] = None): pass @abstractmethod - def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], target_wave_front: Optional[pd.DataFrame]) -> Plottable: + def __call__( + self, + g: Plottable, + prev_node_wavefront: Optional[DataFrameT], + target_wave_front: Optional[DataFrameT], + engine: Engine + ) -> Plottable: raise RuntimeError('__call__ not implemented') @abstractmethod @@ -152,7 +165,13 @@ def from_json(cls, d: dict) -> 'ASTNode': out.validate() return out - def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], target_wave_front: Optional[pd.DataFrame]) -> Plottable: + def __call__( + self, + g: Plottable, + prev_node_wavefront: Optional[DataFrameT], + target_wave_front: Optional[DataFrameT], + engine: Engine + ) -> Plottable: out_g = (g .nodes(prev_node_wavefront if prev_node_wavefront is not None else g._nodes) .filter_nodes_by_dict(self.filter_dict) @@ -161,7 +180,7 @@ def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], ta ) if target_wave_front is not None: assert g._node is not None - reduced_nodes = cast(pd.DataFrame, out_g._nodes).merge(target_wave_front[[g._node]], on=g._node, how='inner') + reduced_nodes = cast(DataFrameT, out_g._nodes).merge(target_wave_front[[g._node]], on=g._node, how='inner') out_g = out_g.nodes(reduced_nodes) if self._name is not None: @@ -295,7 +314,13 @@ def from_json(cls, d: dict) -> 'ASTEdge': out.validate() return out - def __call__(self, g: Plottable, prev_node_wavefront: Optional[pd.DataFrame], target_wave_front: Optional[pd.DataFrame]) -> Plottable: + def __call__( + self, + g: Plottable, + prev_node_wavefront: Optional[DataFrameT], + target_wave_front: Optional[DataFrameT], + engine: Engine + ) -> Plottable: if logger.isEnabledFor(logging.DEBUG): logger.debug('----------------------------------------') diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index d8536ea1df..de55955212 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -1,5 +1,6 @@ -from typing import Dict, Union, cast, List, Tuple +from typing import Any, Dict, Union, cast, List, Tuple, TYPE_CHECKING import pandas as pd +from graphistry.Engine import Engine, EngineAbstract, df_concat, resolve_engine from graphistry.Plottable import Plottable from graphistry.compute.ASTSerializable import ASTSerializable @@ -13,6 +14,12 @@ ############################################################################### +if TYPE_CHECKING: + DataFrameT = pd.DataFrame +else: + DataFrameT = Any + + class Chain(ASTSerializable): def __init__(self, chain: List[ASTObject]) -> None: @@ -51,7 +58,7 @@ def to_json(self, validate=True) -> Dict[str, JSONVal]: ############################################################################### -def combine_steps(g: Plottable, kind: str, steps: List[Tuple[ASTObject,Plottable]]) -> pd.DataFrame: +def combine_steps(g: Plottable, kind: str, steps: List[Tuple[ASTObject,Plottable]], engine: Engine) -> DataFrameT: """ Collect nodes and edges, taking care to deduplicate and tag any names """ @@ -74,14 +81,17 @@ def combine_steps(g: Plottable, kind: str, steps: List[Tuple[ASTObject,Plottable prev_node_wavefront=g_step._nodes, # start from where backwards step says is reachable #target_wave_front=steps[i+1][1]._nodes # end at where next backwards step says is reachable - target_wave_front=None # ^^^ optimization: valid transitions already limit to known-good ones + target_wave_front=None, # ^^^ optimization: valid transitions already limit to known-good ones + engine=engine ) ) for (op, g_step) in steps ] + concat = df_concat(engine) + # df[[id]] - out_df = pd.concat([ + out_df = concat([ getattr(g_step, df_fld)[[id]] for (_, g_step) in steps ]).drop_duplicates(subset=[id]) @@ -132,7 +142,7 @@ def combine_steps(g: Plottable, kind: str, steps: List[Tuple[ASTObject,Plottable # ############################################################################### -def chain(self: Plottable, ops: Union[List[ASTObject], Chain]) -> Plottable: +def chain(self: Plottable, ops: Union[List[ASTObject], Chain], engine: EngineAbstract = EngineAbstract.AUTO) -> Plottable: """ Chain a list of ASTObject (node/edge) traversal operations @@ -212,6 +222,9 @@ def chain(self: Plottable, ops: Union[List[ASTObject], Chain]) -> Plottable: logger.debug('orig chain >> %s', ops) + engine_concrete = resolve_engine(engine, self) + logger.debug('chain engine: %s => %s', engine, engine_concrete) + if isinstance(ops[0], ASTEdge): logger.debug('adding initial node to ensure initial link has needed reversals') ops = cast(List[ASTObject], [ ASTNode() ]) + ops @@ -222,7 +235,7 @@ def chain(self: Plottable, ops: Union[List[ASTObject], Chain]) -> Plottable: logger.debug('final chain >> %s', ops) - g = self.materialize_nodes() + g = self.materialize_nodes(engine=EngineAbstract(engine_concrete.value)) if g._edge is None: if 'index' in g._edges.columns: @@ -252,7 +265,8 @@ def chain(self: Plottable, ops: Union[List[ASTObject], Chain]) -> Plottable: op( g=g, # transition via any original edge prev_node_wavefront=prev_step_nodes, - target_wave_front=None # implicit any + target_wave_front=None, # implicit any + engine=engine_concrete ) ) g_stack.append(g_step) @@ -282,16 +296,18 @@ def chain(self: Plottable, ops: Union[List[ASTObject], Chain]) -> Plottable: prev_node_wavefront=prev_loop_step._nodes, # only allow transitions to these nodes (vs prev_node_wavefront) - target_wave_front=prev_orig_step._nodes if prev_orig_step is not None else None + target_wave_front=prev_orig_step._nodes if prev_orig_step is not None else None, + + engine=engine_concrete ) ) g_stack_reverse.append(g_step_reverse) logger.debug('============ COMBINE NODES ============') - final_nodes_df = combine_steps(g, 'nodes', list(zip(ops, reversed(g_stack_reverse)))) + final_nodes_df = combine_steps(g, 'nodes', list(zip(ops, reversed(g_stack_reverse))), engine_concrete) logger.debug('============ COMBINE EDGES ============') - final_edges_df = combine_steps(g, 'edges', list(zip(ops, reversed(g_stack_reverse)))) + final_edges_df = combine_steps(g, 'edges', list(zip(ops, reversed(g_stack_reverse))), engine_concrete) if added_edge_index: final_edges_df = final_edges_df.drop(columns=['index']) diff --git a/graphistry/compute/filter_by_dict.py b/graphistry/compute/filter_by_dict.py index db59d2605d..ebcda6bf51 100644 --- a/graphistry/compute/filter_by_dict.py +++ b/graphistry/compute/filter_by_dict.py @@ -1,17 +1,31 @@ -from typing import Dict, Optional +from typing import Any, Dict, Optional, TYPE_CHECKING import pandas as pd +from graphistry.Engine import EngineAbstract, df_to_engine, resolve_engine, s_cons +from graphistry.util import setup_logger from graphistry.Plottable import Plottable from .predicates.ASTPredicate import ASTPredicate -def filter_by_dict(df, filter_dict: Optional[dict] = None) -> pd.DataFrame: +logger = setup_logger(__name__) + + +if TYPE_CHECKING: + DataFrameT = pd.DataFrame +else: + DataFrameT = Any + +def filter_by_dict(df: DataFrameT, filter_dict: Optional[dict] = None, engine: EngineAbstract = EngineAbstract.AUTO) -> DataFrameT: """ return df where rows match all values in filter_dict """ if filter_dict is None or filter_dict == {}: return df + + engine_concrete = resolve_engine(engine, df) + df = df_to_engine(df, engine_concrete) + logger.debug('filter_by_dict engine: %s => %s', engine, engine_concrete) predicates: Dict[str, ASTPredicate] = {} for col, val in filter_dict.items(): @@ -26,7 +40,8 @@ def filter_by_dict(df, filter_dict: Optional[dict] = None) -> pd.DataFrame: } if filter_dict_concrete: - hits = (df[list(filter_dict_concrete)] == pd.Series(filter_dict_concrete)).all(axis=1) + S = s_cons(engine_concrete) + hits = (df[list(filter_dict_concrete)] == S(filter_dict_concrete)).all(axis=1) else: hits = df[[]].assign(x=True).x if predicates: @@ -35,17 +50,17 @@ def filter_by_dict(df, filter_dict: Optional[dict] = None) -> pd.DataFrame: return df[hits] -def filter_nodes_by_dict(self: Plottable, filter_dict: dict) -> Plottable: +def filter_nodes_by_dict(self: Plottable, filter_dict: dict, engine: EngineAbstract = EngineAbstract.AUTO) -> Plottable: """ filter nodes to those that match all values in filter_dict """ - nodes2 = filter_by_dict(self._nodes, filter_dict) + nodes2 = filter_by_dict(self._nodes, filter_dict, engine) return self.nodes(nodes2) -def filter_edges_by_dict(self: Plottable, filter_dict: dict) -> Plottable: +def filter_edges_by_dict(self: Plottable, filter_dict: dict, engine: EngineAbstract = EngineAbstract.AUTO) -> Plottable: """ filter edges to those that match all values in filter_dict """ - edges2 = filter_by_dict(self._edges, filter_dict) + edges2 = filter_by_dict(self._edges, filter_dict, engine) return self.edges(edges2) diff --git a/graphistry/compute/hop.py b/graphistry/compute/hop.py index a94725b9bf..4289fda6cc 100644 --- a/graphistry/compute/hop.py +++ b/graphistry/compute/hop.py @@ -1,22 +1,28 @@ import logging -from typing import List, Optional +from typing import Any, List, Optional, TYPE_CHECKING import pandas as pd +from graphistry.Engine import Engine, EngineAbstract, df_concat, df_cons, df_to_engine, resolve_engine from graphistry.Plottable import Plottable from graphistry.util import setup_logger from .filter_by_dict import filter_by_dict + logger = setup_logger(__name__) +if TYPE_CHECKING: + DataFrameT = pd.DataFrame +else: + DataFrameT = Any -def query_if_not_none(query: Optional[str], df: pd.DataFrame) -> pd.DataFrame: +def query_if_not_none(query: Optional[str], df: DataFrameT) -> DataFrameT: if query is None: return df return df.query(query) def hop(self: Plottable, - nodes: Optional[pd.DataFrame] = None, # chain: incoming wavefront + nodes: Optional[DataFrameT] = None, # chain: incoming wavefront hops: Optional[int] = 1, to_fixed_point: bool = False, direction: str = 'forward', @@ -27,7 +33,8 @@ def hop(self: Plottable, destination_node_query: Optional[str] = None, edge_query: Optional[str] = None, return_as_wave_front = False, - target_wave_front: Optional[pd.DataFrame] = None # chain: limit hits to these for reverse pass + target_wave_front: Optional[DataFrameT] = None, # chain: limit hits to these for reverse pass + engine: EngineAbstract = EngineAbstract.AUTO ) -> Plottable: """ Given a graph and some source nodes, return subgraph of all paths within k-hops from the sources @@ -45,6 +52,7 @@ def hop(self: Plottable, edge_query: dataframe query to match edges before hopping (including intermediate) return_as_wave_front: Only return the nodes/edges reached, ignoring past ones (primarily for internal use) target_wave_front: Only consider these nodes for reachability, and for intermediate hops, also consider nodes (primarily for internal use by reverse pass) + engine: 'auto', 'pandas', 'cudf' """ """ @@ -55,6 +63,14 @@ def hop(self: Plottable, """ + engine_concrete = resolve_engine(engine, self) + if not TYPE_CHECKING: + DataFrameT = df_cons(engine_concrete) + concat = df_concat(engine_concrete) + + nodes = df_to_engine(nodes, engine_concrete) if nodes is not None else None + target_wave_front = df_to_engine(target_wave_front, engine_concrete) if target_wave_front is not None else None + #TODO target_wave_front code also includes nodes for handling intermediate hops # ... better to make an explicit param of allowed intermediates? (vs recording each intermediate hop) @@ -77,6 +93,8 @@ def hop(self: Plottable, logger.debug('edge_query: %s', edge_query) logger.debug('return_as_wave_front: %s', return_as_wave_front) logger.debug('target_wave_front:\n%s', target_wave_front) + logger.debug('engine: %s', engine) + logger.debug('engine_concrete: %s', engine_concrete) logger.debug('---------------------') if not to_fixed_point and not isinstance(hops, int): @@ -91,7 +109,8 @@ def hop(self: Plottable, if destination_node_match == {}: destination_node_match = None - g2 = self.materialize_nodes() + g2 = self.materialize_nodes(engine=EngineAbstract(engine_concrete.value)) + logger.debug('materialized node/eddge types: %s, %s', type(g2._nodes), type(g2._edges)) starting_nodes = nodes if nodes is not None else g2._nodes @@ -145,7 +164,7 @@ def hop(self: Plottable, hops_remaining = hops_remaining - 1 assert len(wave_front.columns) == 1, "just indexes" - wave_front_iter : pd.DataFrame = query_if_not_none( + wave_front_iter : DataFrameT = query_if_not_none( source_node_query, filter_by_dict( starting_nodes @@ -173,7 +192,7 @@ def hop(self: Plottable, if target_wave_front is not None: assert nodes is not None, "target_wave_front indicates nodes" if hops_remaining: - intermediate_target_wave_front = pd.concat([ + intermediate_target_wave_front = concat([ target_wave_front[[g2._node]], nodes[[g2._node]] ], sort=False, ignore_index=True @@ -222,7 +241,7 @@ def hop(self: Plottable, if target_wave_front is not None: assert nodes is not None, "target_wave_front indicates nodes" if hops_remaining: - intermediate_target_wave_front = pd.concat([ + intermediate_target_wave_front = concat([ target_wave_front[[g2._node]], nodes[[g2._node]] ], sort=False, ignore_index=True @@ -258,15 +277,15 @@ def hop(self: Plottable, logger.debug('hop_edges_reverse:\n%s', hop_edges_reverse) logger.debug('new_node_ids_reverse:\n%s', new_node_ids_reverse) - mt : List[pd.DataFrame] = [] # help mypy + mt : List[DataFrameT] = [] # help mypy - matches_edges = pd.concat( + matches_edges = concat( [ matches_edges ] + ([ hop_edges_forward[[ EDGE_ID ]] ] if hop_edges_forward is not None else mt) # noqa: W503 + ([ hop_edges_reverse[[ EDGE_ID ]] ] if hop_edges_reverse is not None else mt), # noqa: W503 ignore_index=True, sort=False).drop_duplicates(subset=[EDGE_ID]) - new_node_ids = pd.concat( + new_node_ids = concat( mt + ( [ new_node_ids_forward ] if new_node_ids_forward is not None else mt ) # noqa: W503 + ( [ new_node_ids_reverse] if new_node_ids_reverse is not None else mt ), # noqa: W503 @@ -284,7 +303,7 @@ def hop(self: Plottable, if return_as_wave_front: matches_nodes = new_node_ids[:0] else: - matches_nodes = pd.concat( + matches_nodes = concat( mt + ( [hop_edges_forward[[g2._source]].rename(columns={g2._source: g2._node}).drop_duplicates()] # noqa: W503 if hop_edges_forward is not None @@ -298,7 +317,7 @@ def hop(self: Plottable, logger.debug('~~~~~~~~~~ LOOP STEP MERGES 2 ~~~~~~~~~~~') logger.debug('matches_edges:\n%s', matches_edges) - combined_node_ids = pd.concat([matches_nodes, new_node_ids], ignore_index=True, sort=False).drop_duplicates() + combined_node_ids = concat([matches_nodes, new_node_ids], ignore_index=True, sort=False).drop_duplicates() if len(combined_node_ids) == len(matches_nodes): #fixedpoint, exit early: future will come to same spot! diff --git a/graphistry/compute/predicates/ASTPredicate.py b/graphistry/compute/predicates/ASTPredicate.py index 0d9b300223..5c03121150 100644 --- a/graphistry/compute/predicates/ASTPredicate.py +++ b/graphistry/compute/predicates/ASTPredicate.py @@ -1,9 +1,16 @@ from abc import abstractmethod import pandas as pd +from typing import Any, TYPE_CHECKING from graphistry.compute.ASTSerializable import ASTSerializable +if TYPE_CHECKING: + SeriesT = pd.Series +else: + SeriesT = Any + + class ASTPredicate(ASTSerializable): """ Internal, not intended for use outside of this module. @@ -11,5 +18,5 @@ class ASTPredicate(ASTSerializable): """ @abstractmethod - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: raise NotImplementedError() diff --git a/graphistry/compute/predicates/categorical.py b/graphistry/compute/predicates/categorical.py index 9d0d0ccb9d..840daa0d9a 100644 --- a/graphistry/compute/predicates/categorical.py +++ b/graphistry/compute/predicates/categorical.py @@ -1,13 +1,20 @@ +from typing import Any, TYPE_CHECKING from typing_extensions import Literal import pandas as pd from .ASTPredicate import ASTPredicate + +if TYPE_CHECKING: + SeriesT = pd.Series +else: + SeriesT = Any + class Duplicated(ASTPredicate): def __init__(self, keep: Literal['first', 'last', False] = 'first') -> None: self.keep = keep - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.duplicated(keep=self.keep) def validate(self) -> None: diff --git a/graphistry/compute/predicates/is_in.py b/graphistry/compute/predicates/is_in.py index 4803124d78..9735a11dfe 100644 --- a/graphistry/compute/predicates/is_in.py +++ b/graphistry/compute/predicates/is_in.py @@ -1,16 +1,21 @@ -from typing import Any, List +from typing import TYPE_CHECKING, Any, List import pandas as pd from graphistry.utils.json import assert_json_serializable - from .ASTPredicate import ASTPredicate +if TYPE_CHECKING: + SeriesT = pd.Series +else: + SeriesT = Any + + class IsIn(ASTPredicate): def __init__(self, options: List[Any]) -> None: self.options = options - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.isin(self.options) def validate(self) -> None: diff --git a/graphistry/compute/predicates/numeric.py b/graphistry/compute/predicates/numeric.py index 826996214f..e558e5111a 100644 --- a/graphistry/compute/predicates/numeric.py +++ b/graphistry/compute/predicates/numeric.py @@ -1,9 +1,15 @@ -from typing import Union +from typing import Any, TYPE_CHECKING, Union import pandas as pd from .ASTPredicate import ASTPredicate +if TYPE_CHECKING: + SeriesT = pd.Series +else: + SeriesT = Any + + class NumericASTPredicate(ASTPredicate): def __init__(self, val: Union[int, float]) -> None: self.val = val @@ -17,7 +23,7 @@ class GT(NumericASTPredicate): def __init__(self, val: float) -> None: self.val = val - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s > self.val def gt(val: float) -> GT: @@ -30,7 +36,7 @@ class LT(NumericASTPredicate): def __init__(self, val: float) -> None: self.val = val - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s < self.val def lt(val: float) -> LT: @@ -43,7 +49,7 @@ class GE(NumericASTPredicate): def __init__(self, val: float) -> None: self.val = val - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s >= self.val def ge(val: float) -> GE: @@ -56,7 +62,7 @@ class LE(NumericASTPredicate): def __init__(self, val: float) -> None: self.val = val - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s <= self.val def le(val: float) -> LE: @@ -69,7 +75,7 @@ class EQ(NumericASTPredicate): def __init__(self, val: float) -> None: self.val = val - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s == self.val def eq(val: float) -> EQ: @@ -82,7 +88,7 @@ class NE(NumericASTPredicate): def __init__(self, val: float) -> None: self.val = val - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s != self.val def ne(val: float) -> NE: @@ -97,7 +103,7 @@ def __init__(self, lower: float, upper: float, inclusive: bool = True) -> None: self.upper = upper self.inclusive = inclusive - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: if self.inclusive: return (s >= self.lower) & (s <= self.upper) else: @@ -115,7 +121,7 @@ def between(lower: float, upper: float, inclusive: bool = True) -> Between: return Between(lower, upper, inclusive) class IsNA(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.isna() def isna() -> IsNA: @@ -126,7 +132,7 @@ def isna() -> IsNA: class NotNA(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.notna() def notna() -> NotNA: diff --git a/graphistry/compute/predicates/str.py b/graphistry/compute/predicates/str.py index 43091d630a..af5e0501d3 100644 --- a/graphistry/compute/predicates/str.py +++ b/graphistry/compute/predicates/str.py @@ -1,9 +1,15 @@ -from typing import Optional +from typing import Any, TYPE_CHECKING, Optional import pandas as pd from .ASTPredicate import ASTPredicate +if TYPE_CHECKING: + SeriesT = pd.Series +else: + SeriesT = Any + + class Contains(ASTPredicate): def __init__(self, pat: str, case: bool = True, flags: int = 0, na: Optional[bool] = None, regex: bool = True) -> None: self.pat = pat @@ -12,7 +18,7 @@ def __init__(self, pat: str, case: bool = True, flags: int = 0, na: Optional[boo self.na = na self.regex = regex - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.str.contains(self.pat, self.case, self.flags, self.na, self.regex) def validate(self) -> None: @@ -34,7 +40,7 @@ def __init__(self, pat: str, na: Optional[str] = None) -> None: self.pat = pat self.na = na - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.str.startswith(self.pat, self.na) def validate(self) -> None: @@ -52,7 +58,7 @@ def __init__(self, pat: str, na: Optional[str] = None) -> None: self.pat = pat self.na = na - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: """ Return whether a given pattern is at the end of a string """ @@ -72,7 +78,7 @@ def __init__(self, pat: str, case: bool = True, flags: int = 0, na: Optional[boo self.flags = flags self.na = na - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.str.match(self.pat, self.case, self.flags, self.na) def validate(self) -> None: @@ -89,7 +95,7 @@ def match(pat: str, case: bool = True, flags: int = 0, na: Optional[bool] = None class IsNumeric(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.str.isnumeric() def isnumeric() -> IsNumeric: @@ -100,7 +106,7 @@ def isnumeric() -> IsNumeric: class IsAlpha(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.str.isalpha() def isalpha() -> IsAlpha: @@ -111,7 +117,7 @@ def isalpha() -> IsAlpha: class IsDigit(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.str.isdigit() def isdigit() -> IsDigit: @@ -122,7 +128,7 @@ def isdigit() -> IsDigit: class IsLower(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.str.islower() def islower() -> IsLower: @@ -133,7 +139,7 @@ def islower() -> IsLower: class IsUpper(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.str.isupper() def isupper() -> IsUpper: @@ -144,7 +150,7 @@ def isupper() -> IsUpper: class IsSpace(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.str.isspace() def isspace() -> IsSpace: @@ -155,7 +161,7 @@ def isspace() -> IsSpace: class IsAlnum(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.str.isalnum() def isalnum() -> IsAlnum: @@ -166,7 +172,7 @@ def isalnum() -> IsAlnum: class IsDecimal(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.str.isdecimal() def isdecimal() -> IsDecimal: @@ -177,7 +183,7 @@ def isdecimal() -> IsDecimal: class IsTitle(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.str.istitle() def istitle() -> IsTitle: @@ -188,7 +194,7 @@ def istitle() -> IsTitle: class IsNull(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.isnull() def isnull() -> IsNull: @@ -199,7 +205,7 @@ def isnull() -> IsNull: class NotNull(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.notnull() def notnull() -> NotNull: diff --git a/graphistry/compute/predicates/temporal.py b/graphistry/compute/predicates/temporal.py index 3858478ede..740baf2e27 100644 --- a/graphistry/compute/predicates/temporal.py +++ b/graphistry/compute/predicates/temporal.py @@ -1,11 +1,18 @@ -from typing import Optional +from typing import Any, TYPE_CHECKING, Optional import pandas as pd from .ASTPredicate import ASTPredicate + +if TYPE_CHECKING: + SeriesT = pd.Series +else: + SeriesT = Any + + class IsMonthStart(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.dt.is_month_start def is_month_start() -> IsMonthStart: @@ -16,7 +23,7 @@ def is_month_start() -> IsMonthStart: class IsMonthEnd(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.dt.is_month_end def is_month_end() -> IsMonthEnd: @@ -27,7 +34,7 @@ def is_month_end() -> IsMonthEnd: class IsQuarterStart(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.dt.is_quarter_start def is_quarter_start() -> IsQuarterStart: @@ -38,7 +45,7 @@ def is_quarter_start() -> IsQuarterStart: class IsQuarterEnd(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.dt.is_quarter_end def is_quarter_end() -> IsQuarterEnd: @@ -49,7 +56,7 @@ def is_quarter_end() -> IsQuarterEnd: class IsYearStart(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.dt.is_year_start def is_year_start() -> IsYearStart: @@ -60,7 +67,7 @@ def is_year_start() -> IsYearStart: class IsYearEnd(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.dt.is_year_end def is_year_end() -> IsYearEnd: @@ -71,7 +78,7 @@ def is_year_end() -> IsYearEnd: class IsLeapYear(ASTPredicate): - def __call__(self, s: pd.Series) -> pd.Series: + def __call__(self, s: SeriesT) -> SeriesT: return s.dt.is_leap_year def is_leap_year() -> IsLeapYear: diff --git a/graphistry/plugins/cugraph.py b/graphistry/plugins/cugraph.py index f1e3857fb2..ed9bb08515 100644 --- a/graphistry/plugins/cugraph.py +++ b/graphistry/plugins/cugraph.py @@ -1,7 +1,7 @@ import pandas as pd from typing import Any, Dict, List, Optional, Union from graphistry.constants import NODE -from graphistry.Engine import Engine +from graphistry.Engine import EngineAbstract from graphistry.Plottable import Plottable from graphistry.plugins_types import CuGraphKind from graphistry.util import setup_logger @@ -270,7 +270,7 @@ def compute_cugraph( out = getattr(cugraph, alg)(G, **params) if isinstance(out, tuple): out = out[0] - g = self.materialize_nodes(engine=Engine.CUDF) + g = self.materialize_nodes(engine=EngineAbstract.CUDF) if g._node != 'vertex': out = out.rename(columns={'vertex': g._node}) expected_cols = node_compute_algs_to_attr[alg] @@ -396,7 +396,7 @@ def layout_cugraph( import cugraph - g = self.materialize_nodes(engine=Engine.CUDF) + g = self.materialize_nodes(engine=EngineAbstract.CUDF) if layout not in layout_algs: raise ValueError('Unsupported algorithm: %s', layout) From 34aaacfea4ed0d782b8aed9d1bf443439e5abc1e Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 24 Dec 2023 19:25:30 -0800 Subject: [PATCH 094/104] test(gfql cudf) --- graphistry/tests/compute/test_chain.py | 89 +++++++++++++++++++++++++ graphistry/tests/compute/test_hop.py | 90 ++++++++++++++++++++++++++ graphistry/tests/test_compute.py | 2 +- 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 graphistry/tests/compute/test_hop.py diff --git a/graphistry/tests/compute/test_chain.py b/graphistry/tests/compute/test_chain.py index 30760d6b29..c685edb84c 100644 --- a/graphistry/tests/compute/test_chain.py +++ b/graphistry/tests/compute/test_chain.py @@ -1,5 +1,12 @@ +import os +import pandas as pd +from graphistry.compute.predicates.is_in import is_in +import pytest + from graphistry.compute.ast import ASTNode, ASTEdge, n, e from graphistry.compute.chain import Chain +from graphistry.tests.test_compute import CGFull + def test_chain_serialization_mt(): o = Chain([]).to_json() @@ -36,3 +43,85 @@ def test_chain_serialization_multi(): assert d.chain[1]._name == 'abc' o2 = d.to_json() assert o == o2 + +def test_chain_simple_cudf_pd(): + nodes_df = pd.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']}) + edges_df = pd.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]}) + g = CGFull().nodes(nodes_df, 'id').edges(edges_df, 'src', 'dst') + #g_nodes = g.chain([n()]) + #assert isinstance(g_nodes._nodes, pd.DataFrame) + #assert len(g_nodes._nodes) == 3 + g_edges = g.chain([e()]) + assert isinstance(g_edges._edges, pd.DataFrame) + assert len(g_edges._edges) == 3 + + +@pytest.mark.skipif( + not ("TEST_CUDF" in os.environ and os.environ["TEST_CUDF"] == "1"), + reason="cudf tests need TEST_CUDF=1", +) +def test_chain_simple_cudf(): + import cudf + nodes_gdf = cudf.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']}) + edges_gdf = cudf.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]}) + g = CGFull().nodes(nodes_gdf, 'id').edges(edges_gdf, 'src', 'dst') + g_nodes = g.chain([n()]) + assert isinstance(g_nodes._nodes, cudf.DataFrame) + assert len(g_nodes._nodes) == 3 + g_edges = g.chain([e()]) + assert isinstance(g_edges._edges, cudf.DataFrame) + assert len(g_edges._edges) == 3 + +def test_chain_kv_cudf_pd(): + nodes_df = pd.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']}) + edges_df = pd.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]}) + g = CGFull().nodes(nodes_df, 'id').edges(edges_df, 'src', 'dst') + g_nodes = g.chain([n({'id': 0})]) + assert isinstance(g_nodes._nodes, pd.DataFrame) + assert len(g_nodes._nodes) == 1 + g_edges = g.chain([e({'src': 0})]) + assert isinstance(g_edges._edges, pd.DataFrame) + assert len(g_edges._edges) == 1 + +@pytest.mark.skipif( + not ("TEST_CUDF" in os.environ and os.environ["TEST_CUDF"] == "1"), + reason="cudf tests need TEST_CUDF=1", +) +def test_chain_kv_cudf(): + import cudf + nodes_gdf = cudf.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']}) + edges_gdf = cudf.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]}) + g = CGFull().nodes(nodes_gdf, 'id').edges(edges_gdf, 'src', 'dst') + g_nodes = g.chain([n({'id': 0})]) + assert isinstance(g_nodes._nodes, cudf.DataFrame) + assert len(g_nodes._nodes) == 1 + g_edges = g.chain([e({'src': 0})]) + assert isinstance(g_edges._edges, cudf.DataFrame) + assert len(g_edges._edges) == 1 + +def test_chain_pred_cudf_pd(): + nodes_df = pd.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']}) + edges_df = pd.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]}) + g = CGFull().nodes(nodes_df, 'id').edges(edges_df, 'src', 'dst') + g_nodes = g.chain([n({'id': is_in([0])})]) + assert isinstance(g_nodes._nodes, pd.DataFrame) + assert len(g_nodes._nodes) == 1 + g_edges = g.chain([e({'src': is_in([0])})]) + assert isinstance(g_edges._edges, pd.DataFrame) + assert len(g_edges._edges) == 1 + +@pytest.mark.skipif( + not ("TEST_CUDF" in os.environ and os.environ["TEST_CUDF"] == "1"), + reason="cudf tests need TEST_CUDF=1", +) +def test_chain_pred_cudf(): + import cudf + nodes_gdf = cudf.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']}) + edges_gdf = cudf.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]}) + g = CGFull().nodes(nodes_gdf, 'id').edges(edges_gdf, 'src', 'dst') + g_nodes = g.chain([n({'id': is_in([0])})]) + assert isinstance(g_nodes._nodes, cudf.DataFrame) + assert len(g_nodes._nodes) == 1 + g_edges = g.chain([e({'src': is_in([0])})]) + assert isinstance(g_edges._edges, cudf.DataFrame) + assert len(g_edges._edges) == 1 diff --git a/graphistry/tests/compute/test_hop.py b/graphistry/tests/compute/test_hop.py new file mode 100644 index 0000000000..2960c58325 --- /dev/null +++ b/graphistry/tests/compute/test_hop.py @@ -0,0 +1,90 @@ +import os +import pandas as pd +from graphistry.compute.predicates.is_in import is_in +import pytest + +from graphistry.compute.ast import ASTNode, ASTEdge, n, e +from graphistry.tests.test_compute import CGFull + + +def test_hop_simple_cudf_pd(): + nodes_df = pd.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']}) + edges_df = pd.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]}) + g = CGFull().nodes(nodes_df, 'id').edges(edges_df, 'src', 'dst') + g_nodes = g.hop() + assert isinstance(g_nodes._nodes, pd.DataFrame) + assert len(g_nodes._nodes) == 3 + g_edges = g.hop() + assert isinstance(g_edges._edges, pd.DataFrame) + assert len(g_edges._edges) == 3 + + +@pytest.mark.skipif( + not ("TEST_CUDF" in os.environ and os.environ["TEST_CUDF"] == "1"), + reason="cudf tests need TEST_CUDF=1", +) +def test_hop_simple_cudf(): + import cudf + nodes_gdf = cudf.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']}) + edges_gdf = cudf.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]}) + g = CGFull().nodes(nodes_gdf, 'id').edges(edges_gdf, 'src', 'dst') + g_nodes = g.hop() + assert isinstance(g_nodes._nodes, cudf.DataFrame) + assert len(g_nodes._nodes) == 3 + g_edges = g.hop() + assert isinstance(g_edges._edges, cudf.DataFrame) + assert len(g_edges._edges) == 3 + +def test_hop_kv_cudf_pd(): + nodes_df = pd.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']}) + edges_df = pd.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]}) + g = CGFull().nodes(nodes_df, 'id').edges(edges_df, 'src', 'dst') + g_nodes = g.hop(source_node_match=({'id': 0})) + assert isinstance(g_nodes._nodes, pd.DataFrame) + assert len(g_nodes._nodes) == 2 + g_edges = g.hop(edge_match={'src': 0}) + assert isinstance(g_edges._edges, pd.DataFrame) + assert len(g_edges._edges) == 1 + +@pytest.mark.skipif( + not ("TEST_CUDF" in os.environ and os.environ["TEST_CUDF"] == "1"), + reason="cudf tests need TEST_CUDF=1", +) +def test_hop_kv_cudf(): + import cudf + nodes_gdf = cudf.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']}) + edges_gdf = cudf.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]}) + g = CGFull().nodes(nodes_gdf, 'id').edges(edges_gdf, 'src', 'dst') + g_nodes = g.hop(source_node_match={'id': 0}) + assert isinstance(g_nodes._nodes, cudf.DataFrame) + assert len(g_nodes._nodes) == 2 + g_edges = g.hop(edge_match={'src': 0}) + assert isinstance(g_edges._edges, cudf.DataFrame) + assert len(g_edges._edges) == 1 + +def test_hop_pred_cudf_pd(): + nodes_df = pd.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']}) + edges_df = pd.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]}) + g = CGFull().nodes(nodes_df, 'id').edges(edges_df, 'src', 'dst') + g_nodes = g.hop(source_node_match={'id': is_in([0])}) + assert isinstance(g_nodes._nodes, pd.DataFrame) + assert len(g_nodes._nodes) == 2 + g_edges = g.hop(edge_match={'src': is_in([0])}) + assert isinstance(g_edges._edges, pd.DataFrame) + assert len(g_edges._edges) == 1 + +@pytest.mark.skipif( + not ("TEST_CUDF" in os.environ and os.environ["TEST_CUDF"] == "1"), + reason="cudf tests need TEST_CUDF=1", +) +def test_hop_pred_cudf(): + import cudf + nodes_gdf = cudf.DataFrame({'id': [0, 1, 2], 'label': ['a', 'b', 'c']}) + edges_gdf = cudf.DataFrame({'src': [0, 1, 2], 'dst': [1, 2, 0]}) + g = CGFull().nodes(nodes_gdf, 'id').edges(edges_gdf, 'src', 'dst') + g_nodes = g.hop(source_node_match={'id': is_in([0])}) + assert isinstance(g_nodes._nodes, cudf.DataFrame) + assert len(g_nodes._nodes) == 2 + g_edges = g.hop(edge_match={'src': is_in([0])}) + assert isinstance(g_edges._edges, cudf.DataFrame) + assert len(g_edges._edges) == 1 diff --git a/graphistry/tests/test_compute.py b/graphistry/tests/test_compute.py index 735f0c68a9..49d72d3b61 100644 --- a/graphistry/tests/test_compute.py +++ b/graphistry/tests/test_compute.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- import os, pandas as pd, pytest, unittest -from common import NoAuthTestCase from graphistry.compute import ComputeMixin from graphistry.plotter import PlotterBase +from .common import NoAuthTestCase class CG(ComputeMixin): From 0a5fb728ad48cc08102f4abbe9ac117d1166fa78 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 24 Dec 2023 19:32:28 -0800 Subject: [PATCH 095/104] fix(test): paths --- graphistry/tests/test_compute.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphistry/tests/test_compute.py b/graphistry/tests/test_compute.py index 49d72d3b61..b328804a46 100644 --- a/graphistry/tests/test_compute.py +++ b/graphistry/tests/test_compute.py @@ -3,7 +3,7 @@ from graphistry.compute import ComputeMixin from graphistry.plotter import PlotterBase -from .common import NoAuthTestCase +from graphistry.tests.common import NoAuthTestCase class CG(ComputeMixin): From ead3316ea87ba5a6af01d4341f9ae209b5a02151 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 24 Dec 2023 19:50:30 -0800 Subject: [PATCH 096/104] fix(graphistry): utils as module --- graphistry/utils/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 graphistry/utils/__init__.py diff --git a/graphistry/utils/__init__.py b/graphistry/utils/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 42a727aa2055c72d4661d1407991ea0b82273b13 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 24 Dec 2023 21:43:59 -0800 Subject: [PATCH 097/104] fix(engineabstract): support str specs --- graphistry/Engine.py | 8 ++++++-- graphistry/Plottable.py | 2 +- graphistry/compute/ComputeMixin.py | 6 +++++- graphistry/compute/chain.py | 5 ++++- graphistry/compute/filter_by_dict.py | 11 +++++++---- graphistry/compute/hop.py | 7 +++++-- 6 files changed, 28 insertions(+), 11 deletions(-) diff --git a/graphistry/Engine.py b/graphistry/Engine.py index 8753aa71f7..8bc2bc2b1d 100644 --- a/graphistry/Engine.py +++ b/graphistry/Engine.py @@ -1,5 +1,5 @@ import pandas as pd -from typing import Any, Optional +from typing import Any, Optional, Union from enum import Enum @@ -34,9 +34,13 @@ def lazy_cudf_import_has_dependancy(): return False, e, None def resolve_engine( - engine: EngineAbstract, + engine: Union[EngineAbstract, str], g_or_df: Optional[Any] = None, ) -> Engine: + + if isinstance(engine, str): + engine = EngineAbstract(engine) + # if an Engine (concrete), just use that if engine != EngineAbstract.AUTO: return Engine(engine.value) diff --git a/graphistry/Plottable.py b/graphistry/Plottable.py index e7589e92ea..72ac7d8831 100644 --- a/graphistry/Plottable.py +++ b/graphistry/Plottable.py @@ -149,7 +149,7 @@ def get_degrees( raise RuntimeError('should not happen') return self - def materialize_nodes(self, reuse: bool = True, engine:EngineAbstract = EngineAbstract.AUTO) -> 'Plottable': + def materialize_nodes(self, reuse: bool = True, engine: Union[EngineAbstract, str] = EngineAbstract.AUTO) -> 'Plottable': if 1 + 1: raise RuntimeError('should not happen') return self diff --git a/graphistry/compute/ComputeMixin.py b/graphistry/compute/ComputeMixin.py index ea00ad34c8..6148b66c27 100644 --- a/graphistry/compute/ComputeMixin.py +++ b/graphistry/compute/ComputeMixin.py @@ -28,7 +28,7 @@ def __init__(self, *args, **kwargs): def materialize_nodes( self, reuse: bool = True, - engine: EngineAbstract = EngineAbstract.AUTO + engine: Union[EngineAbstract, str] = EngineAbstract.AUTO ) -> "Plottable": """ Generate g._nodes based on g._edges @@ -50,6 +50,10 @@ def materialize_nodes( print(g2._nodes) # pd.DataFrame """ + + if isinstance(engine, str): + engine = EngineAbstract(engine) + g = self if g._edges is None: raise ValueError("Missing edges") diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index de55955212..52a86957a3 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -142,7 +142,7 @@ def combine_steps(g: Plottable, kind: str, steps: List[Tuple[ASTObject,Plottable # ############################################################################### -def chain(self: Plottable, ops: Union[List[ASTObject], Chain], engine: EngineAbstract = EngineAbstract.AUTO) -> Plottable: +def chain(self: Plottable, ops: Union[List[ASTObject], Chain], engine: Union[EngineAbstract, str] = EngineAbstract.AUTO) -> Plottable: """ Chain a list of ASTObject (node/edge) traversal operations @@ -214,6 +214,9 @@ def chain(self: Plottable, ops: Union[List[ASTObject], Chain], engine: EngineAbs """ + if isinstance(engine, str): + engine = EngineAbstract(engine) + if isinstance(ops, Chain): ops = ops.chain diff --git a/graphistry/compute/filter_by_dict.py b/graphistry/compute/filter_by_dict.py index ebcda6bf51..1fbbbf9477 100644 --- a/graphistry/compute/filter_by_dict.py +++ b/graphistry/compute/filter_by_dict.py @@ -1,4 +1,4 @@ -from typing import Any, Dict, Optional, TYPE_CHECKING +from typing import Any, Dict, Optional, TYPE_CHECKING, Union import pandas as pd from graphistry.Engine import EngineAbstract, df_to_engine, resolve_engine, s_cons from graphistry.util import setup_logger @@ -15,11 +15,14 @@ else: DataFrameT = Any -def filter_by_dict(df: DataFrameT, filter_dict: Optional[dict] = None, engine: EngineAbstract = EngineAbstract.AUTO) -> DataFrameT: +def filter_by_dict(df: DataFrameT, filter_dict: Optional[dict] = None, engine: Union[EngineAbstract, str] = EngineAbstract.AUTO) -> DataFrameT: """ return df where rows match all values in filter_dict """ + if isinstance(engine, str): + engine = EngineAbstract(engine) + if filter_dict is None or filter_dict == {}: return df @@ -50,7 +53,7 @@ def filter_by_dict(df: DataFrameT, filter_dict: Optional[dict] = None, engine: E return df[hits] -def filter_nodes_by_dict(self: Plottable, filter_dict: dict, engine: EngineAbstract = EngineAbstract.AUTO) -> Plottable: +def filter_nodes_by_dict(self: Plottable, filter_dict: dict, engine: Union[EngineAbstract, str] = EngineAbstract.AUTO) -> Plottable: """ filter nodes to those that match all values in filter_dict """ @@ -58,7 +61,7 @@ def filter_nodes_by_dict(self: Plottable, filter_dict: dict, engine: EngineAbstr return self.nodes(nodes2) -def filter_edges_by_dict(self: Plottable, filter_dict: dict, engine: EngineAbstract = EngineAbstract.AUTO) -> Plottable: +def filter_edges_by_dict(self: Plottable, filter_dict: dict, engine: Union[EngineAbstract, str] = EngineAbstract.AUTO) -> Plottable: """ filter edges to those that match all values in filter_dict """ diff --git a/graphistry/compute/hop.py b/graphistry/compute/hop.py index 4289fda6cc..66c5414270 100644 --- a/graphistry/compute/hop.py +++ b/graphistry/compute/hop.py @@ -1,5 +1,5 @@ import logging -from typing import Any, List, Optional, TYPE_CHECKING +from typing import Any, List, Optional, TYPE_CHECKING, Union import pandas as pd from graphistry.Engine import Engine, EngineAbstract, df_concat, df_cons, df_to_engine, resolve_engine @@ -34,7 +34,7 @@ def hop(self: Plottable, edge_query: Optional[str] = None, return_as_wave_front = False, target_wave_front: Optional[DataFrameT] = None, # chain: limit hits to these for reverse pass - engine: EngineAbstract = EngineAbstract.AUTO + engine: Union[EngineAbstract, str] = EngineAbstract.AUTO ) -> Plottable: """ Given a graph and some source nodes, return subgraph of all paths within k-hops from the sources @@ -63,6 +63,9 @@ def hop(self: Plottable, """ + if isinstance(engine, str): + engine = EngineAbstract(engine) + engine_concrete = resolve_engine(engine, self) if not TYPE_CHECKING: DataFrameT = df_cons(engine_concrete) From dcdda9210e696722e7c2f53efd286154c52450ff Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 24 Dec 2023 23:31:51 -0800 Subject: [PATCH 098/104] fix(docs) --- docs/source/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/conf.py b/docs/source/conf.py index 50ff684da7..c166e8ccee 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -86,6 +86,7 @@ ('py:class', 'graphistry.compute.predicates.temporal.IsYearStart'), ('py:class', 'graphistry.compute.predicates.temporal.IsYearEnd'), ('py:class', 'graphistry.Engine.Engine'), + ('py:class', 'graphistry.Engine.EngineAbstract'), ('py:class', 'graphistry.gremlin.CosmosMixin'), ('py:class', 'graphistry.gremlin.GremlinMixin'), ('py:class', 'graphistry.gremlin.NeptuneMixin'), From be7fe97d27ac51d66e0a80c39534892e1a13d200 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Sun, 24 Dec 2023 23:39:59 -0800 Subject: [PATCH 099/104] fix(docs): missing module --- docs/source/graphistry.rst | 9 ++++++++- docs/source/graphistry.utils.rst | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 docs/source/graphistry.utils.rst diff --git a/docs/source/graphistry.rst b/docs/source/graphistry.rst index 2fd55094a2..a6f87bc9db 100644 --- a/docs/source/graphistry.rst +++ b/docs/source/graphistry.rst @@ -15,7 +15,6 @@ Plugins graphistry.plugins - Compute ================== .. toctree:: @@ -33,6 +32,14 @@ Layouts graphistry.layout +Utilities +================== +.. toctree:: + :maxdepth: 3 + + graphistry.utils + + Featurize ================== .. automodule:: graphistry.feature_utils diff --git a/docs/source/graphistry.utils.rst b/docs/source/graphistry.utils.rst new file mode 100644 index 0000000000..037dd78676 --- /dev/null +++ b/docs/source/graphistry.utils.rst @@ -0,0 +1,8 @@ +Module contents +--------------- + +.. automodule:: graphistry.utils + :members: + :undoc-members: + :show-inheritance: + :noindex: From d409f5587a450626ea4a5a839e773ca65f8f02d4 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Mon, 25 Dec 2023 12:55:43 -0800 Subject: [PATCH 100/104] refactor(gfql): use DataFrameT --- graphistry/compute/__init__.py | 1 + graphistry/compute/ast.py | 8 +------- graphistry/compute/chain.py | 7 +------ graphistry/compute/filter_by_dict.py | 6 +----- graphistry/compute/hop.py | 5 +---- graphistry/compute/typing.py | 8 ++++++++ 6 files changed, 13 insertions(+), 22 deletions(-) create mode 100644 graphistry/compute/typing.py diff --git a/graphistry/compute/__init__.py b/graphistry/compute/__init__.py index 0bed507004..360b038992 100644 --- a/graphistry/compute/__init__.py +++ b/graphistry/compute/__init__.py @@ -46,3 +46,4 @@ isnull, IsNull, notnull, NotNull, ) +from .typing import DataFrameT diff --git a/graphistry/compute/ast.py b/graphistry/compute/ast.py index 83c1d9020a..c1e7b4e046 100644 --- a/graphistry/compute/ast.py +++ b/graphistry/compute/ast.py @@ -54,17 +54,11 @@ notnull, NotNull ) from .filter_by_dict import filter_by_dict +from .typing import DataFrameT logger = setup_logger(__name__) - -if TYPE_CHECKING: - DataFrameT = pd.DataFrame -else: - DataFrameT = Any - - ############################################################################## diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index 52a86957a3..ad5fb13bab 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -7,6 +7,7 @@ from graphistry.util import setup_logger from graphistry.utils.json import JSONVal from .ast import ASTObject, ASTNode, ASTEdge, from_json as ASTObject_from_json +from .typing import DataFrameT logger = setup_logger(__name__) @@ -14,12 +15,6 @@ ############################################################################### -if TYPE_CHECKING: - DataFrameT = pd.DataFrame -else: - DataFrameT = Any - - class Chain(ASTSerializable): def __init__(self, chain: List[ASTObject]) -> None: diff --git a/graphistry/compute/filter_by_dict.py b/graphistry/compute/filter_by_dict.py index 1fbbbf9477..ea6350d086 100644 --- a/graphistry/compute/filter_by_dict.py +++ b/graphistry/compute/filter_by_dict.py @@ -5,16 +5,12 @@ from graphistry.Plottable import Plottable from .predicates.ASTPredicate import ASTPredicate +from .typing import DataFrameT logger = setup_logger(__name__) -if TYPE_CHECKING: - DataFrameT = pd.DataFrame -else: - DataFrameT = Any - def filter_by_dict(df: DataFrameT, filter_dict: Optional[dict] = None, engine: Union[EngineAbstract, str] = EngineAbstract.AUTO) -> DataFrameT: """ return df where rows match all values in filter_dict diff --git a/graphistry/compute/hop.py b/graphistry/compute/hop.py index 66c5414270..a171dcca1b 100644 --- a/graphistry/compute/hop.py +++ b/graphistry/compute/hop.py @@ -6,14 +6,11 @@ from graphistry.Plottable import Plottable from graphistry.util import setup_logger from .filter_by_dict import filter_by_dict +from .typing import DataFrameT logger = setup_logger(__name__) -if TYPE_CHECKING: - DataFrameT = pd.DataFrame -else: - DataFrameT = Any def query_if_not_none(query: Optional[str], df: DataFrameT) -> DataFrameT: if query is None: diff --git a/graphistry/compute/typing.py b/graphistry/compute/typing.py new file mode 100644 index 0000000000..8ad1a80969 --- /dev/null +++ b/graphistry/compute/typing.py @@ -0,0 +1,8 @@ +import pandas as pd +from typing import Any, TYPE_CHECKING + +# TODO stubs for Union[cudf.DataFrame, dask.DataFrame, ..] at checking time +if TYPE_CHECKING: + DataFrameT = pd.DataFrame +else: + DataFrameT = Any From 547b968ddd1cad7bc896bb92ed9c4ae9c9c19423 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Mon, 25 Dec 2023 12:55:53 -0800 Subject: [PATCH 101/104] docs(changelog) --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4a9c837d5..fd1fcab40b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,20 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +### Added + +* `AbstractEngine` to `engine.py::Engine` enum +* `compute.typing.DataFrameT` to centralize df-lib-agnostic type checking +* `chain`, `hop`, `filter_by_dict` variants support GPU execution + +### Refactor + +* GFQL and more of compute uses generic dataframe methods and threads through engine + +### Infra + +* GPU tester threads through LOG_LEVEL + ## [0.32.0 - 2023-12-22] ### Added From 72e778c5be6ac8a7b9edd02d6df89215db6e5b95 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Mon, 25 Dec 2023 13:14:01 -0800 Subject: [PATCH 102/104] docs(gfql): GPU --- README.md | 32 ++++++++++++++++++++++++++++---- graphistry/compute/chain.py | 24 ++++++++++++++++++++++++ graphistry/compute/hop.py | 6 +++++- 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index be64eccaa9..eaca3805b5 100644 --- a/README.md +++ b/README.md @@ -11,9 +11,9 @@ [![Uptime Robot status](https://img.shields.io/uptimerobot/status/m787548531-e9c7b7508fc76fea927e2313?label=hub.graphistry.com)](https://status.graphistry.com/) [](https://join.slack.com/t/graphistry-community/shared_invite/zt-53ik36w2-fpP0Ibjbk7IJuVFIRSnr6g) [![Twitter Follow](https://img.shields.io/twitter/follow/graphistry)](https://twitter.com/graphistry) -**PyGraphistry is a Python visual graph AI library to extract, transform, analyze, model, and visualize big graphs, and especially alongside [Graphistry](https://www.graphistry.com) end-to-end GPU server sessions.** Installing with optional `graphistry[ai]` dependencies adds **graph autoML**, including automatic feature engineering, UMAP, and graph neural net support. Combined, PyGraphistry reduces your `time to graph` for going from raw data to visualizations and AI models down to three lines of code. +**PyGraphistry is a dataframe-native Python visual graph AI library to extract, query, transform, analyze, model, and visualize big graphs, and especially alongside [Graphistry](https://www.graphistry.com) end-to-end GPU server sessions.** The GFQL query language supports running a large subset of the Cypher property graph query language without requiring external software and adds optional GPU acceleration. Installing PyGraphistry with the optional `graphistry[ai]` dependencies adds **graph autoML**, including automatic feature engineering, UMAP, and graph neural net support. Combined, PyGraphistry reduces your **time to graph** for going from raw data to visualizations and AI models down to three lines of code. -Graphistry gets used on problems like visually mapping the behavior of devices and users, investigating fraud, analyzing machine learning results, and starting in graph AI. It provides point-and-click features like timebars, search, filtering, clustering, coloring, sharing, and more. Graphistry is the only tool built ground-up for large graphs. The client's custom WebGL rendering engine renders up to 8MM nodes + edges at a time, and most older client GPUs smoothly support somewhere between 100K and 2MM elements. The serverside GPU analytics engine supports even bigger graphs. It smoothes graph workflows over the PyData ecosystem including Pandas/Spark/Dask dataframes, Nvidia RAPIDS GPU dataframes & GPU graphs, DGL/PyTorch graph neural networks, and various data connectors. +The optional visual engine, Graphistry, gets used on problems like visually mapping the behavior of devices and users, investigating fraud, analyzing machine learning results, and starting in graph AI. It provides point-and-click features like timebars, search, filtering, clustering, coloring, sharing, and more. Graphistry is the only tool built ground-up for large graphs. The client's custom WebGL rendering engine renders up to 8MM nodes + edges at a time, and most older client GPUs smoothly support somewhere between 100K and 2MM elements. The serverside GPU analytics engine supports even bigger graphs. It smoothes graph workflows over the PyData ecosystem including Pandas/Spark/Dask dataframes, Nvidia RAPIDS GPU dataframes & GPU graphs, DGL/PyTorch graph neural networks, and various data connectors. The PyGraphistry Python client helps several kinds of usage modes: @@ -147,14 +147,14 @@ It is easy to turn arbitrary data into insightful graphs. PyGraphistry comes wit g2.plot() ``` -* GFQL: Cypher-style graph pattern mining queries on dataframes ([ipynb demo](demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb)) +* GFQL: Cypher-style graph pattern mining queries on dataframes with optional GPU acceleration ([ipynb demo](demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb)) Run Cypher-style graph queries natively on dataframes without going to a database or Java with GFQL: ```python from graphistry import n, e_undirected, is_in - g2 = g.chain([ + g2 = g1.chain([ n({'user': 'Biden'}), e_undirected(), n(name='bridge'), @@ -166,6 +166,17 @@ It is easy to turn arbitrary data into insightful graphs. PyGraphistry comes wit g2.plot() ``` + Enable GFQL's optional automatic GPU acceleration for 43X+ speedups: + + ```python + # Switch from Pandas CPU dataframes to RAPIDS GPU dataframes + import cudf + g2 = g1.edges(lambda g: cudf.DataFrame(g._edges)) + # GFQL will automaticallly run on a GPU + g3 = g2.chain([n(), e(hops=3), n()]) + g3.plot() + ``` + * [Spark](https://spark.apache.org/)/[Databricks](https://databricks.com/) ([ipynb demo](demos/demos_databases_apis/databricks_pyspark/graphistry-notebook-dashboard.ipynb), [dbc demo](demos/demos_databases_apis/databricks_pyspark/graphistry-notebook-dashboard.dbc)) ```python @@ -1163,6 +1174,8 @@ Both `hop()` and `chain()` (GFQL) match dictionary expressions support dataframe * numeric: gt, lt, ge, le, eq, ne, between, isna, notna * string: contains, startswith, endswith, match, isnumeric, isalpha, isdigit, islower, isupper, isspace, isalnum, isdecimal, istitle, isnull, notnull +Both `hop()` and `chain()` will run on GPUs when passing in RAPIDS dataframes. Specify parameter `engine='cudf'` to be sure. + #### Table to graph ```python @@ -1327,6 +1340,17 @@ pattern2 = Chain.from_json(pattern_json) g.chain(pattern2).plot() ``` +Benefit from automatic GPU acceleration by passing in GPU dataframes: + +```python +import cudf + +g1 = graphistry.edges(cudf.read_csv('data.csv'), 's', 'd') +g2 = g1.chain(..., engine='cudf') +``` + +The parameter `engine` is optional, defaulting to `'auto'`. + #### Pipelining ```python diff --git a/graphistry/compute/chain.py b/graphistry/compute/chain.py index ad5fb13bab..20de5e83f7 100644 --- a/graphistry/compute/chain.py +++ b/graphistry/compute/chain.py @@ -146,6 +146,8 @@ def chain(self: Plottable, ops: Union[List[ASTObject], Chain], engine: Union[Eng For direct calls, exposes convenience `List[ASTObject]`. Internal operational should prefer `Chain`. + Use `engine='cudf'` to force automatic GPU acceleration mode + :param ops: List[ASTObject] Various node and edge matchers :returns: Plotter @@ -206,6 +208,28 @@ def chain(self: Plottable, ops: Union[List[ASTObject], Chain], engine: Union[Eng n({"risk2": True}) ]) print('# hits:', len(g_risky._nodes[ g_risky._nodes.hit ])) + + **Example: Run with automatic GPU acceleration** + + :: + + import cudf + import graphistry + + e_gdf = cudf.from_pandas(df) + g1 = graphistry.edges(e_gdf, 's', 'd') + g2 = g1.chain([ ... ]) + + **Example: Run with automatic GPU acceleration, and force GPU mode** + + :: + + import cudf + import graphistry + + e_gdf = cudf.from_pandas(df) + g1 = graphistry.edges(e_gdf, 's', 'd') + g2 = g1.chain([ ... ], engine='cudf') """ diff --git a/graphistry/compute/hop.py b/graphistry/compute/hop.py index a171dcca1b..7d8425a690 100644 --- a/graphistry/compute/hop.py +++ b/graphistry/compute/hop.py @@ -36,6 +36,10 @@ def hop(self: Plottable, """ Given a graph and some source nodes, return subgraph of all paths within k-hops from the sources + This can be faster than the equivalent chain([...]) call that wraps it with additional steps + + See chain() examples for examples of many of the parameters + g: Plotter nodes: dataframe with id column matching g._node. None signifies all nodes (default). hops: consider paths of length 1 to 'hops' steps, if any (default 1). @@ -49,7 +53,7 @@ def hop(self: Plottable, edge_query: dataframe query to match edges before hopping (including intermediate) return_as_wave_front: Only return the nodes/edges reached, ignoring past ones (primarily for internal use) target_wave_front: Only consider these nodes for reachability, and for intermediate hops, also consider nodes (primarily for internal use by reverse pass) - engine: 'auto', 'pandas', 'cudf' + engine: 'auto', 'pandas', 'cudf' (GPU) """ """ From 5e818246d2ff24ad33e271e6fbdae2c1856464a6 Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Tue, 26 Dec 2023 12:56:20 -0800 Subject: [PATCH 103/104] docs(benchmark) --- README.md | 4 +- demos/gfql/benchmark_hops_cpu_gpu.ipynb | 4825 +++++++++++++++++++++++ 2 files changed, 4827 insertions(+), 2 deletions(-) create mode 100644 demos/gfql/benchmark_hops_cpu_gpu.ipynb diff --git a/README.md b/README.md index eaca3805b5..b4dcae3167 100644 --- a/README.md +++ b/README.md @@ -147,7 +147,7 @@ It is easy to turn arbitrary data into insightful graphs. PyGraphistry comes wit g2.plot() ``` -* GFQL: Cypher-style graph pattern mining queries on dataframes with optional GPU acceleration ([ipynb demo](demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb)) +* GFQL: Cypher-style graph pattern mining queries on dataframes with optional GPU acceleration ([ipynb demo](demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb), [benchmark](demos/gfql/benchmark_hops_cpu_gpu.ipynb)) Run Cypher-style graph queries natively on dataframes without going to a database or Java with GFQL: @@ -1248,7 +1248,7 @@ assert 'pagerank' in g2._nodes.columns PyGraphistry supports GFQL, its PyData-native variant of the popular Cypher graph query language, meaning you can do graph pattern matching directly from Pandas dataframes without installing a database or Java -See also [graph pattern matching tutorial](demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb) +See also [graph pattern matching tutorial](demos/more_examples/graphistry_features/hop_and_chain_graph_pattern_mining.ipynb) and the CPU/GPU [benchmark](demos/gfql/benchmark_hops_cpu_gpu.ipynb) Traverse within a graph, or expand one graph against another diff --git a/demos/gfql/benchmark_hops_cpu_gpu.ipynb b/demos/gfql/benchmark_hops_cpu_gpu.ipynb new file mode 100644 index 0000000000..bf17b630e7 --- /dev/null +++ b/demos/gfql/benchmark_hops_cpu_gpu.ipynb @@ -0,0 +1,4825 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "provenance": [], + "gpuType": "T4" + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + }, + "accelerator": "GPU" + }, + "cells": [ + { + "cell_type": "markdown", + "source": [ + "# GFQL CPU, GPU Benchmark\n", + "\n", + "This notebook examines GFQL progerty graph query performance on 1-8 hop queries using CPU + GPU modes on various real-world 100K - 100M edge graphs. The data comes from a variety of popular social networks. The single-threaded CPU mode benefits from GFQL's novel dataframe engine, and the GPU mode further adds single-GPU acceleration. Both the `chain()` and `hop()` methods are examined.\n", + "\n", + "The benchmark does not examine bigger-than-memory and distributed scenarios. The provided results here are from running on a free Google Colab T4 runtime, with a 2.2GHz Intel CPU (12 GB CPU RAM) and T4 Nvidia GPU (16 GB GPU RAM).\n", + "\n", + "## Data\n", + "From [SNAP](https://snap.stanford.edu/data/)\n", + "\n", + "| Network | Nodes | Edges |\n", + "|-------------|-----------|--------------|\n", + "| **Facebook**| 4,039 | 88,234 |\n", + "| **Twitter** | 81,306 | 2,420,766 |\n", + "| **GPlus** | 107,614 | 30,494,866 |\n", + "| **Orkut** | 3,072,441 | 117,185,082 |\n", + "\n", + "## Results\n", + "\n", + "Definitions:\n", + "\n", + "* GTEPS: Giga (billion) edges traversed per second\n", + "\n", + "* T edges / \\$: Estimated trillion edges traversed for 1\\$ USD based on observed GTEPS and a 3yr AWS reservation (as of 12/2023)\n", + "\n", + "Tasks:\n", + "\n", + "1. `chain()` - includes complex pre/post processing\n", + "\n", + " **Task**: `g.chain([n({'id': some_id}), e_forward(hops=some_n)])`\n", + "\n", + "\n", + "| **Dataset** | Max GPU Speedup | CPU GTEPS | GPU GTEPS | T CPU edges / \\$ (t3.l) | T GPU edges / \\$ (g4dn.xl) |\n", + "|-------------|--------------|-------------|-------------|----------------------------|--------------------------------|\n", + "| **Facebook**| 1.1X | 0.66 | 0.61 | 65.7 | 10.4 |\n", + "| **Twitter** | 17.4X | 0.17 | 2.81 | 16.7 | 48.1 |\n", + "| **GPlus** | 43.8X | 0.09 | 2.87 | 8.5 | 49.2 |\n", + "| **Orkut** | N/A | N/A | 12.15 | N/A | 208.3 |\n", + "| **AVG** | 20.7X | 0.30 | 4.61 | 30.3 | 79.0\n", + "| **MAX** | 43.8X | 0.66 | 12.15 | 65.7 | 208.3\n", + "\n", + "\n", + "2. `hop()` - core property search primitive similar to BFS\n", + "\n", + " **Task**: `g.hop(nodes=[some_id], direction='forward', hops=some_n)`\n", + "\n", + "\n", + "| **Dataset** | Max GPU Speedup | CPU GTEPS | GPU GTEPS | T CPU edges / \\$ (t3.l) | T GPU edges / \\$ (g4dn.xl) |\n", + "|-------------|-------------|-----------|-----------|--------------------|--------------------------------|\n", + "| **Facebook**| 3X | 0.47 | 1.47 | 47.0 | 25.2 |\n", + "| **Twitter** | 42X | 0.50 | 10.51 | 50.2 | 180.2 |\n", + "| **GPlus** | 21X | 0.26 | 4.11 | 26.2 | 70.4 |\n", + "| **Orkut** | N/A | N/A | 41.50 | N/A | 711.4 |\n", + "| **AVG** | 22X | 0.41 | 14.4 | 41.1 | 246.8\n", + "| **MAX** | 42X | 0.50 | 41.50 | 50.2 | 711.4\n" + ], + "metadata": { + "id": "GZxoiU8sQDk_" + } + }, + { + "cell_type": "markdown", + "source": [ + "## Optional: GPU setup - Google Colab" + ], + "metadata": { + "id": "SAj8lhREEOwS" + } + }, + { + "cell_type": "markdown", + "source": [], + "metadata": { + "id": "4hrEEAAm7DTO" + } + }, + { + "cell_type": "code", + "source": [ + "# Report GPU used when GPU benchmarking\n", + "! nvidia-smi" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "W2MF6ZsjDv3B", + "outputId": "46088cbc-2db9-4529-f724-dc57ed85dfb7" + }, + "execution_count": 1, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Tue Dec 26 00:50:30 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 54C P8 10W / 70W | 0MiB / 15360MiB | 0% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "| No running processes found |\n", + "+---------------------------------------------------------------------------------------+\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "# if in google colab\n", + "!git clone https://github.com/rapidsai/rapidsai-csp-utils.git\n", + "!python rapidsai-csp-utils/colab/pip-install.py" + ], + "metadata": { + "id": "Aikh0x4ID_wK" + }, + "execution_count": 8, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "import cudf\n", + "cudf.__version__" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "id": "Lwekdei1dH3N", + "outputId": "71f5b01d-7917-4283-8338-969167d6e1e8" + }, + "execution_count": 3, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'23.12.01'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 3 + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "# 1. Install & configure" + ], + "metadata": { + "id": "QQpsrtwBT7sa" + } + }, + { + "cell_type": "code", + "source": [ + "#! pip install graphistry[igraph]\n", + "\n", + "!pip install -q igraph\n", + "#!pip install -q git+https://github.com/graphistry/pygraphistry.git@dev/cugfql\n", + "!pip install -q graphistry\n" + ], + "metadata": { + "id": "cYjRbgkU9Sx8", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "2cf25531-9b8b-4715-ccc7-e79094d84ebd" + }, + "execution_count": 2, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Imports" + ], + "metadata": { + "id": "Ff6Tt9DhkePl" + } + }, + { + "cell_type": "code", + "source": [ + "import pandas as pd\n", + "\n", + "import graphistry\n", + "\n", + "from graphistry import (\n", + "\n", + " # graph operators\n", + " n, e_undirected, e_forward, e_reverse,\n", + "\n", + " # attribute predicates\n", + " is_in, ge, startswith, contains, match as match_re\n", + ")\n", + "graphistry.__version__" + ], + "metadata": { + "id": "S5_y0CbLkjft", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 35 + }, + "outputId": "a68a9c4b-c9c5-4b8b-ea4f-7bf1e4ddf315" + }, + "execution_count": 3, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "'0.32.0+12.g72e778c'" + ], + "application/vnd.google.colaboratory.intrinsic+json": { + "type": "string" + } + }, + "metadata": {}, + "execution_count": 3 + } + ] + }, + { + "cell_type": "code", + "source": [ + "import cudf" + ], + "metadata": { + "id": "I7Fg75jsG4co" + }, + "execution_count": 6, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "#work around google colab shell encoding bugs\n", + "\n", + "import locale\n", + "locale.getpreferredencoding = lambda: \"UTF-8\"" + ], + "metadata": { + "id": "uLZKph2-a5M4" + }, + "execution_count": 7, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# 2. Perf benchmarks" + ], + "metadata": { + "id": "eU9SyauNUHtR" + } + }, + { + "cell_type": "markdown", + "source": [ + "### Facebook: 88K edges" + ], + "metadata": { + "id": "NA0Ym11fkB8j" + } + }, + { + "cell_type": "code", + "source": [ + "df = pd.read_csv('https://raw.githubusercontent.com/graphistry/pygraphistry/master/demos/data/facebook_combined.txt', sep=' ', names=['s', 'd'])\n", + "print(df.shape)\n", + "df.head(5)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 224 + }, + "id": "vXuQogHekClJ", + "outputId": "64db92c0-2704-438b-d0e4-25865acbb5e9" + }, + "execution_count": 10, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(88234, 2)\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " s d\n", + "0 0 1\n", + "1 0 2\n", + "2 0 3\n", + "3 0 4\n", + "4 0 5" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sd
001
102
203
304
405
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 10 + } + ] + }, + { + "cell_type": "code", + "source": [ + "fg = graphistry.edges(df, 's', 'd').materialize_nodes()\n", + "print(fg._nodes.shape, fg._edges.shape)\n", + "fg._nodes.head(5)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 224 + }, + "id": "jEma7hvvkzkN", + "outputId": "dbf21342-6b80-429c-bd3f-b1494c6854c7" + }, + "execution_count": 11, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(4039, 1) (88234, 2)\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " id\n", + "0 0\n", + "1 1\n", + "2 2\n", + "3 3\n", + "4 4" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
id
00
11
22
33
44
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 11 + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "for i in range(100):\n", + " fg2 = fg.chain([n({'id': 0}), e_forward(hops=2)])" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5lEdCBw9lzd7", + "outputId": "ed7451e0-401e-4edc-c8de-79c5afd0c95b" + }, + "execution_count": 12, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 13.6 s, sys: 2.08 s, total: 15.7 s\n", + "Wall time: 18 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "fg_gdf = fg.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "for i in range(100):\n", + " fg2 = fg_gdf.chain([n({'id': 0}), e_forward(hops=2)])\n", + "print(fg._nodes.shape, fg._edges.shape)\n", + "print(fg2._nodes.shape, fg2._edges.shape)\n", + "del fg_gdf\n", + "del fg2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JFKIBa8mJCvJ", + "outputId": "c22022f0-b33d-483a-db64-29992c5161e8" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(4039, 1) (88234, 2)\n", + "(1519, 1) (4060, 2)\n", + "CPU times: user 11.8 s, sys: 28.1 ms, total: 11.8 s\n", + "Wall time: 11.9 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "for i in range(50):\n", + " fg2 = fg.chain([n({'id': 0}), e_forward(hops=5)])\n", + "print(fg._nodes.shape, fg._edges.shape)\n", + "print(fg2._nodes.shape, fg2._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-KBGLexek5tS", + "outputId": "2f462e6c-578a-4fa1-ec29-91bae753f4c5" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(4039, 1) (88234, 2)\n", + "(3829, 1) (86074, 2)\n", + "CPU times: user 15.4 s, sys: 808 ms, total: 16.2 s\n", + "Wall time: 16.2 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "fg_gdf = fg.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "for i in range(50):\n", + " fg2 = fg_gdf.chain([n({'id': 0}), e_forward(hops=5)])\n", + "print(fg._nodes.shape, fg._edges.shape)\n", + "print(fg2._nodes.shape, fg2._edges.shape)\n", + "del fg_gdf\n", + "del fg2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CVpcbhpdHFEF", + "outputId": "aba04ee1-781e-4226-b593-b42415a55fc4" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(4039, 1) (88234, 2)\n", + "(3829, 1) (86074, 2)\n", + "CPU times: user 9.82 s, sys: 133 ms, total: 9.95 s\n", + "Wall time: 10.1 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "for i in range(100):\n", + " fg2 = fg.chain([e_forward(source_node_match={'id': 0}, hops=5)])" + ], + "metadata": { + "id": "1cFIyJF9pLjE", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "107329af-8e4b-428c-8b03-77ed00bdf5bf" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 11.8 s, sys: 377 ms, total: 12.1 s\n", + "Wall time: 13.1 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "fg_gdf = fg.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "for i in range(100):\n", + " fg2 = fg_gdf.chain([e_forward(source_node_match={'id': 0}, hops=5)])\n", + "print(fg._nodes.shape, fg._edges.shape)\n", + "print(fg2._nodes.shape, fg2._edges.shape)\n", + "del fg_gdf\n", + "del fg2\n", + "\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "M5uRiD6uJVNW", + "outputId": "5e938a19-2992-4280-80c2-784382d40113" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(4039, 1) (88234, 2)\n", + "(348, 1) (347, 2)\n", + "CPU times: user 14.1 s, sys: 48.5 ms, total: 14.2 s\n", + "Wall time: 14.2 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = pd.DataFrame({fg._node: [0]})\n", + "for i in range(100):\n", + " fg2 = fg.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=2)\n", + "print(fg2._nodes.shape, fg2._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Y9vgzfT69x41", + "outputId": "6882c1ce-0df8-4087-dda4-0a105a8617e1" + }, + "execution_count": 17, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(1519, 1) (4060, 2)\n", + "CPU times: user 4.5 s, sys: 1.35 s, total: 5.85 s\n", + "Wall time: 6.09 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({fg._node: [0]})\n", + "fg_gdf = fg.nodes(cudf.from_pandas(fg._nodes)).edges(cudf.from_pandas(fg._edges))\n", + "for i in range(100):\n", + " fg2 = fg_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=2)\n", + "print(fg2._nodes.shape, fg2._edges.shape)\n", + "del start_nodes\n", + "del fg_gdf\n", + "del fg2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "c7ybJqjc-T31", + "outputId": "37ccc1fb-6460-4193-8aa7-22837ff06d0a" + }, + "execution_count": 18, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(1519, 1) (4060, 2)\n", + "CPU times: user 2.58 s, sys: 6.75 ms, total: 2.59 s\n", + "Wall time: 2.58 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = pd.DataFrame({fg._node: [0]})\n", + "for i in range(100):\n", + " fg2 = fg.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=5)\n", + "print(fg2._nodes.shape, fg2._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Dy7a4zDZ-7_G", + "outputId": "077b5d9c-c9ae-411a-8228-3c026b07a910" + }, + "execution_count": 19, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(3829, 1) (86074, 2)\n", + "CPU times: user 13.2 s, sys: 2 s, total: 15.2 s\n", + "Wall time: 18.3 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({fg._node: [0]})\n", + "fg_gdf = fg.nodes(cudf.from_pandas(fg._nodes)).edges(cudf.from_pandas(fg._edges))\n", + "for i in range(100):\n", + " fg2 = fg_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=5)\n", + "print(fg2._nodes.shape, fg2._edges.shape)\n", + "del start_nodes\n", + "del fg_gdf\n", + "del fg2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "N5aUtF1a--ML", + "outputId": "0c2b67b8-fac6-45b3-dfbe-8002b5506e91" + }, + "execution_count": 20, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(3829, 1) (86074, 2)\n", + "CPU times: user 5.72 s, sys: 159 ms, total: 5.88 s\n", + "Wall time: 5.86 s\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "## Twitter\n", + "\n", + "- edges: 2420766\n", + "- nodes: 81306" + ], + "metadata": { + "id": "KrJKjXy2KLos" + } + }, + { + "cell_type": "code", + "source": [ + "! wget 'https://snap.stanford.edu/data/twitter_combined.txt.gz'\n", + "#! curl -L 'https://snap.stanford.edu/data/twitter_combined.txt.gz' -o twitter_combined.txt.gz" + ], + "metadata": { + "id": "fO2qasGqpubr", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "d41a110e-9f7c-4710-9ce3-3f4906ab02ae" + }, + "execution_count": 21, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "--2023-12-25 21:58:27-- https://snap.stanford.edu/data/twitter_combined.txt.gz\n", + "Resolving snap.stanford.edu (snap.stanford.edu)... 171.64.75.80\n", + "Connecting to snap.stanford.edu (snap.stanford.edu)|171.64.75.80|:443... connected.\n", + "HTTP request sent, awaiting response... 200 OK\n", + "Length: 10621918 (10M) [application/x-gzip]\n", + "Saving to: ‘twitter_combined.txt.gz’\n", + "\n", + "twitter_combined.tx 100%[===================>] 10.13M 3.00MB/s in 4.0s \n", + "\n", + "2023-12-25 21:58:32 (2.52 MB/s) - ‘twitter_combined.txt.gz’ saved [10621918/10621918]\n", + "\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "! gunzip twitter_combined.txt.gz" + ], + "metadata": { + "id": "fn7zeA3SGlEo" + }, + "execution_count": 22, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "! head -n 5 twitter_combined.txt" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "68TAZkhLGz9g", + "outputId": "8ba7c23d-267f-4b59-d6c6-b3f66caec9cf" + }, + "execution_count": 24, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "214328887 34428380\n", + "17116707 28465635\n", + "380580781 18996905\n", + "221036078 153460275\n", + "107830991 17868918\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "te_df = pd.read_csv('twitter_combined.txt', sep=' ', names=['s', 'd'])\n", + "te_df.shape" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QU2wNeGXG2GC", + "outputId": "349ac9c0-6f6c-4ce6-fec0-8bae75fca635" + }, + "execution_count": 25, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 474 ms, sys: 61.9 ms, total: 536 ms\n", + "Wall time: 534 ms\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(2420766, 2)" + ] + }, + "metadata": {}, + "execution_count": 25 + } + ] + }, + { + "cell_type": "code", + "source": [ + "import graphistry" + ], + "metadata": { + "id": "EK5gQH2iG5UU" + }, + "execution_count": 26, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "g = graphistry.edges(te_df, 's', 'd').materialize_nodes()\n", + "g._nodes.shape" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ZtIW-eFGG_R4", + "outputId": "0686e9b3-b684-4b93-da03-289244394338" + }, + "execution_count": 27, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 86.4 ms, sys: 106 ms, total: 193 ms\n", + "Wall time: 191 ms\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(81306, 1)" + ] + }, + "metadata": {}, + "execution_count": 27 + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "for i in range(10):\n", + " g2 = g.chain([n({'id': 17116707}), e_forward(hops=1)])\n", + "g2._nodes.shape, g2._edges.shape" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "yUaRfw4FHGMb", + "outputId": "3945cc5a-c36c-451b-ac95-8af992a3546f" + }, + "execution_count": 29, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 11.8 s, sys: 8.4 s, total: 20.2 s\n", + "Wall time: 23 s\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "((140, 1), (615, 2))" + ] + }, + "metadata": {}, + "execution_count": 29 + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "g_gdf = g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "for i in range(10):\n", + " out = g_gdf.chain([n({'id': 17116707}), e_forward(hops=1)])._nodes\n", + "print(out.shape)\n", + "del g_gdf\n", + "del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5hM4NBu2_eks", + "outputId": "54505262-4871-44ee-e5e4-ad7ab32c13c2" + }, + "execution_count": 30, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(140, 1)\n", + "CPU times: user 1.33 s, sys: 46.6 ms, total: 1.38 s\n", + "Wall time: 1.63 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "for i in range(10):\n", + " out = g.chain([n({'id': 17116707}), e_forward(hops=2)])\n", + "print(out._nodes.shape, out._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "m2-MxD5lHX6u", + "outputId": "e89b9d4b-6c04-45c7-9e7f-cbdbbe0a4730" + }, + "execution_count": 31, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(2345, 1) (68536, 2)\n", + "CPU times: user 13.3 s, sys: 8.05 s, total: 21.4 s\n", + "Wall time: 21.6 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "g_gdf = g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "for i in range(10):\n", + " out = g_gdf.chain([n({'id': 17116707}), e_forward(hops=2)])._nodes\n", + "print(out.shape)\n", + "del g_gdf\n", + "del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7EQSRbIqLaGw", + "outputId": "60c00a03-9e7b-46b5-fce3-f4f567a09430" + }, + "execution_count": 36, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(2345, 1)\n", + "CPU times: user 1.67 s, sys: 55.8 ms, total: 1.72 s\n", + "Wall time: 1.75 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "for i in range(10):\n", + " out = g.chain([n({'id': 17116707}), e_forward(hops=8)])\n", + "print(out._nodes.shape, out._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hh6WnjI3ITpB", + "outputId": "33138efe-a581-49ed-b2b4-247f8e9bdc09" + }, + "execution_count": 37, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(81304, 1) (2417796, 2)\n", + "CPU times: user 1min 56s, sys: 17.1 s, total: 2min 13s\n", + "Wall time: 2min 22s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "g_gdf = g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "for i in range(10):\n", + " out = g_gdf.chain([n({'id': 17116707}), e_forward(hops=8)])._nodes\n", + "print(out.shape)\n", + "del g_gdf\n", + "del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7jFFVUenM87j", + "outputId": "2cceb720-9de3-488e-8b74-b820fd06e6c1" + }, + "execution_count": 38, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(81304, 1)\n", + "CPU times: user 5.3 s, sys: 1.48 s, total: 6.78 s\n", + "Wall time: 7.89 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = pd.DataFrame({g._node: [17116707]})\n", + "for i in range(10):\n", + " g2 = g.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=1)\n", + "print(g2._nodes.shape, g2._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "_5LD0bZB_lU4", + "outputId": "bc31bd03-e79f-46d2-ea8f-3b01d9ef39a2" + }, + "execution_count": 39, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(0, 1) (0, 2)\n", + "CPU times: user 2.58 s, sys: 1.59 s, total: 4.17 s\n", + "Wall time: 6.02 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({g._node: [17116707]})\n", + "g_gdf = g.nodes(cudf.from_pandas(g._nodes)).edges(cudf.from_pandas(g._edges))\n", + "for i in range(10):\n", + " g2 = g_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=5)\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del g_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "M_rHjqtvACQw", + "outputId": "8d3e308e-b1e2-452b-f402-573be0dd5b58" + }, + "execution_count": 44, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(61827, 1) (1473599, 2)\n", + "CPU times: user 822 ms, sys: 179 ms, total: 1 s\n", + "Wall time: 997 ms\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = pd.DataFrame({g._node: [17116707]})\n", + "for i in range(10):\n", + " g2 = g.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=2)\n", + "print(g2._nodes.shape, g2._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "0zEIucaCAbj_", + "outputId": "83e64b0f-2b3a-4e4b-d189-3e6a8ef78f53" + }, + "execution_count": 40, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(2345, 1) (68536, 2)\n", + "CPU times: user 8.93 s, sys: 5.92 s, total: 14.9 s\n", + "Wall time: 15.8 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({g._node: [17116707]})\n", + "g_gdf = g.nodes(cudf.from_pandas(g._nodes)).edges(cudf.from_pandas(g._edges))\n", + "for i in range(10):\n", + " g2 = g_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=2)\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del g_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "LKJh5gRtAdIj", + "outputId": "e3c7883d-74c0-4d55-b238-88457296c6bc" + }, + "execution_count": 41, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(2345, 1) (68536, 2)\n", + "CPU times: user 374 ms, sys: 6.92 ms, total: 381 ms\n", + "Wall time: 379 ms\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = pd.DataFrame({g._node: [17116707]})\n", + "for i in range(10):\n", + " g2 = g.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=8)\n", + "print(g2._nodes.shape, g2._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "JZwxdofNAfmb", + "outputId": "2731be4c-75d9-47f4-8602-4f2d6cb2ddac" + }, + "execution_count": 42, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(81304, 1) (2417796, 2)\n", + "CPU times: user 38.8 s, sys: 8.7 s, total: 47.5 s\n", + "Wall time: 48.2 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({g._node: [17116707]})\n", + "g_gdf = g.nodes(cudf.from_pandas(g._nodes)).edges(cudf.from_pandas(g._edges))\n", + "for i in range(10):\n", + " g2 = g_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=8)\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del g_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9o_og8bSAhe3", + "outputId": "dd3e4f8f-f426-4705-98c4-60f1912ba28a" + }, + "execution_count": 43, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(81304, 1) (2417796, 2)\n", + "CPU times: user 1.8 s, sys: 506 ms, total: 2.3 s\n", + "Wall time: 2.3 s\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### GPlus\n", + "\n", + "- edges: 30494866\n", + "- nodes: 107614" + ], + "metadata": { + "id": "9dZzAAVONCD2" + } + }, + { + "cell_type": "code", + "source": [ + "! wget https://snap.stanford.edu/data/gplus_combined.txt.gz" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-nhWGNekKpcZ", + "outputId": "e2175290-337c-4faa-e5d8-4bc401583326" + }, + "execution_count": 4, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "--2023-12-26 18:36:29-- https://snap.stanford.edu/data/gplus_combined.txt.gz\n", + "Resolving snap.stanford.edu (snap.stanford.edu)... 171.64.75.80\n", + "Connecting to snap.stanford.edu (snap.stanford.edu)|171.64.75.80|:443... connected.\n", + "HTTP request sent, awaiting response... 200 OK\n", + "Length: 398930514 (380M) [application/x-gzip]\n", + "Saving to: ‘gplus_combined.txt.gz’\n", + "\n", + "gplus_combined.txt. 100%[===================>] 380.45M 34.7MB/s in 9.6s \n", + "\n", + "2023-12-26 18:36:39 (39.7 MB/s) - ‘gplus_combined.txt.gz’ saved [398930514/398930514]\n", + "\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "! gunzip gplus_combined.txt.gz" + ], + "metadata": { + "id": "g5wgA_c2KqwJ" + }, + "execution_count": 5, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "ge_df = pd.read_csv('gplus_combined.txt', sep=' ', names=['s', 'd'])\n", + "print(ge_df.shape)\n", + "ge_df.head(5)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 258 + }, + "id": "52hgDbr0Kti6", + "outputId": "217203fc-7095-4784-c4c4-d46ee9c78808" + }, + "execution_count": 6, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(30494866, 2)\n", + "CPU times: user 16 s, sys: 1.45 s, total: 17.5 s\n", + "Wall time: 22.5 s\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " s d\n", + "0 116374117927631468606 101765416973555767821\n", + "1 112188647432305746617 107727150903234299458\n", + "2 116719211656774388392 100432456209427807893\n", + "3 117421021456205115327 101096322838605097368\n", + "4 116407635616074189669 113556266482860931616" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sd
0116374117927631468606101765416973555767821
1112188647432305746617107727150903234299458
2116719211656774388392100432456209427807893
3117421021456205115327101096322838605097368
4116407635616074189669113556266482860931616
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 6 + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "gg = graphistry.edges(ge_df, 's', 'd').materialize_nodes()\n", + "gg = graphistry.edges(ge_df, 's', 'd').nodes(gg._nodes, 'id')\n", + "print(gg._edges.shape, gg._nodes.shape)\n", + "gg._nodes.head(5)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 258 + }, + "id": "w5YkN-nLK6UV", + "outputId": "dc98380d-54c2-4b36-c56e-5e8401c4ffa4" + }, + "execution_count": 7, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(30494866, 2) (107614, 1)\n", + "CPU times: user 4.49 s, sys: 1.25 s, total: 5.74 s\n", + "Wall time: 5.97 s\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " id\n", + "0 116374117927631468606\n", + "1 112188647432305746617\n", + "2 116719211656774388392\n", + "3 117421021456205115327\n", + "4 116407635616074189669" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
id
0116374117927631468606
1112188647432305746617
2116719211656774388392
3117421021456205115327
4116407635616074189669
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 7 + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "gg.chain([ n({'id': '116374117927631468606'})])._nodes" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 115 + }, + "id": "NKtz54uELX-8", + "outputId": "5d8f3eef-893d-47cc-e7a9-c5cbfec8270c" + }, + "execution_count": 49, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 534 ms, sys: 598 ms, total: 1.13 s\n", + "Wall time: 1.65 s\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " id\n", + "0 116374117927631468606" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
id
0116374117927631468606
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 49 + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "out = gg.chain([ n({'id': '116374117927631468606'}), e_forward(hops=1)])._nodes\n", + "out.shape" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "iNWdi00VLmZG", + "outputId": "ecfb56a6-c564-4bf6-f43f-2c95a103f4be" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 27.5 s, sys: 11.1 s, total: 38.5 s\n", + "Wall time: 39.5 s\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(1473, 1)" + ] + }, + "metadata": {}, + "execution_count": 75 + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "gg_gdf = gg.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "out = gg_gdf.chain([ n({'id': '116374117927631468606'}), e_forward(hops=1)])\n", + "print(out._nodes.shape, out._edges.shape)\n", + "del gg_gdf\n", + "del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Q6p3h6uCOABh", + "outputId": "817fc80f-ef5d-4070-eb48-a12344be709c" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(1473, 1) (13375, 2)\n", + "CPU times: user 4.57 s, sys: 2.11 s, total: 6.68 s\n", + "Wall time: 7.63 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "out = gg.chain([ n({'id': '116374117927631468606'}), e_forward(hops=2)])._nodes\n", + "out.shape" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "6UdCcMdqLw-P", + "outputId": "70742c79-b22b-4db2-c548-cb1e25d572eb" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 45.8 s, sys: 17 s, total: 1min 2s\n", + "Wall time: 1min 5s\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(44073, 1)" + ] + }, + "metadata": {}, + "execution_count": 77 + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "gg_gdf = gg.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "out = gg_gdf.chain([ n({'id': '116374117927631468606'}), e_forward(hops=2)])\n", + "print(out._nodes.shape, out._edges.shape)\n", + "del gg_gdf\n", + "del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QElqatDyNYCS", + "outputId": "0e15bd3e-d2d9-4965-df7d-c8856d036680" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(44073, 1) (2069325, 2)\n", + "CPU times: user 4.97 s, sys: 2.36 s, total: 7.34 s\n", + "Wall time: 10.6 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "out = gg.chain([ n({'id': '116374117927631468606'}), e_forward(hops=3)])._nodes\n", + "out.shape" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "3HJOItZ4MQMG", + "outputId": "f5be7bb4-7f09-4f80-c549-e703e99f5067" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 3min 45s, sys: 1min 5s, total: 4min 50s\n", + "Wall time: 4min 52s\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(102414, 1)" + ] + }, + "metadata": {}, + "execution_count": 79 + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "gg_gdf = gg.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "out = gg_gdf.chain([ n({'id': '116374117927631468606'}), e_forward(hops=3)])\n", + "print(out._nodes.shape, out._edges.shape)\n", + "del gg_gdf\n", + "del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "G32t_xthOUle", + "outputId": "7721741f-9c86-41aa-eb0b-2c8f0db2ed54" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(102414, 1) (24851333, 2)\n", + "CPU times: user 6.95 s, sys: 2.63 s, total: 9.57 s\n", + "Wall time: 9.84 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "out = gg.chain([ n({'id': '116374117927631468606'}), e_forward(hops=4)])\n", + "print(out._nodes.shape, out._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bXy2yyJsMsEG", + "outputId": "911f2680-067c-44f2-9ba2-7f27d3c9bc6b" + }, + "execution_count": 8, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(105479, 1) (30450354, 2)\n", + "CPU times: user 4min 36s, sys: 1min 25s, total: 6min 2s\n", + "Wall time: 6min 4s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "gg_gdf = gg.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "out = gg_gdf.chain([ n({'id': '116374117927631468606'}), e_forward(hops=4)])\n", + "print(out._nodes.shape, out._edges.shape)\n", + "del gg_gdf\n", + "del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Vt8hhjWDP_W_", + "outputId": "824ae644-e1cf-4239-bda9-84aecde52ad8" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(105479, 1) (30450354, 2)\n", + "CPU times: user 7.44 s, sys: 2.45 s, total: 9.88 s\n", + "Wall time: 9.9 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "out = gg.chain([ n({'id': '116374117927631468606'}), e_forward(hops=5)])\n", + "print(out._nodes.shape, out._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "_z4KpNZaOH8t", + "outputId": "2417f78b-e1b7-452d-8e26-7df259620c88" + }, + "execution_count": 9, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(105604, 1) (30468335, 2)\n", + "CPU times: user 5min 36s, sys: 1min 39s, total: 7min 16s\n", + "Wall time: 7min 15s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "gg_gdf = gg.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "out = gg_gdf.chain([ n({'id': '116374117927631468606'}), e_forward(hops=5)])\n", + "print(out._nodes.shape, out._edges.shape)\n", + "del gg_gdf\n", + "del out" + ], + "metadata": { + "id": "spUBH9EHSz2O", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "22340ce3-e8d4-4a72-b485-9839c667b965" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(105604, 1) (30468335, 2)\n", + "CPU times: user 8.82 s, sys: 2.71 s, total: 11.5 s\n", + "Wall time: 11.9 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = pd.DataFrame({gg._node: ['116374117927631468606']})\n", + "for i in range(1):\n", + " g2 = gg.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=1)\n", + "print(g2._nodes.shape, g2._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "vCsdmc62A7OM", + "outputId": "adc05d29-c628-49ed-cd6d-8921c6dcd206" + }, + "execution_count": 50, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(1473, 1) (13375, 2)\n", + "CPU times: user 19.9 s, sys: 9.36 s, total: 29.2 s\n", + "Wall time: 41.8 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({gg._node: ['116374117927631468606']})\n", + "gg_gdf = gg.nodes(cudf.from_pandas(gg._nodes)).edges(cudf.from_pandas(gg._edges))\n", + "for i in range(1):\n", + " g2 = gg_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=1)\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del gg_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "J3kV8NBYBQdW", + "outputId": "76073248-43e1-4c3c-c004-67324cc1d312" + }, + "execution_count": 52, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(1473, 1) (13375, 2)\n", + "CPU times: user 3.71 s, sys: 2.09 s, total: 5.8 s\n", + "Wall time: 6.05 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = pd.DataFrame({gg._node: ['116374117927631468606']})\n", + "for i in range(1):\n", + " g2 = gg.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=2)\n", + "print(g2._nodes.shape, g2._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ONv1RQeWBeeK", + "outputId": "58d57fa4-be72-45bc-abfa-5de9d1102f55" + }, + "execution_count": 53, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(44073, 1) (2069325, 2)\n", + "CPU times: user 27.8 s, sys: 13.2 s, total: 41 s\n", + "Wall time: 43.9 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({gg._node: ['116374117927631468606']})\n", + "gg_gdf = gg.nodes(cudf.from_pandas(gg._nodes)).edges(cudf.from_pandas(gg._edges))\n", + "for i in range(1):\n", + " g2 = gg_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=2)\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del gg_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ke5SZZ01BgqR", + "outputId": "4173fd28-a11b-4300-d28b-6fdb87e8e9f3" + }, + "execution_count": 54, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(44073, 1) (2069325, 2)\n", + "CPU times: user 4.26 s, sys: 2.37 s, total: 6.63 s\n", + "Wall time: 7.91 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = pd.DataFrame({gg._node: ['116374117927631468606']})\n", + "for i in range(1):\n", + " g2 = gg.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=3)\n", + "print(g2._nodes.shape, g2._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "U795pIBUBiZV", + "outputId": "d499433c-cc0c-4bbf-c69f-36b5d55402d9" + }, + "execution_count": 55, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(102414, 1) (24851333, 2)\n", + "CPU times: user 1min 3s, sys: 22.7 s, total: 1min 26s\n", + "Wall time: 1min 35s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({gg._node: ['116374117927631468606']})\n", + "gg_gdf = gg.nodes(cudf.from_pandas(gg._nodes)).edges(cudf.from_pandas(gg._edges))\n", + "for i in range(1):\n", + " g2 = gg_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=3)\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del gg_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "kIZYwSe1Bj2e", + "outputId": "b7e1ed9f-47d1-412e-9593-ecc436ac1486" + }, + "execution_count": 56, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(102414, 1) (24851333, 2)\n", + "CPU times: user 3.96 s, sys: 2.11 s, total: 6.07 s\n", + "Wall time: 6.05 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = pd.DataFrame({gg._node: ['116374117927631468606']})\n", + "for i in range(1):\n", + " g2 = gg.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=4)\n", + "print(g2._nodes.shape, g2._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YTI5sD6YBpYL", + "outputId": "b37bf2df-07dc-404c-8a83-a83f28e38bf6" + }, + "execution_count": 57, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(105479, 1) (30450354, 2)\n", + "CPU times: user 1min 34s, sys: 30.6 s, total: 2min 5s\n", + "Wall time: 2min 5s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({gg._node: ['116374117927631468606']})\n", + "gg_gdf = gg.nodes(cudf.from_pandas(gg._nodes)).edges(cudf.from_pandas(gg._edges))\n", + "for i in range(1):\n", + " g2 = gg_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=4)\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del gg_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "d5WBazICBrSz", + "outputId": "ef95e893-3a0f-4d47-ede4-bd8a6faebf98" + }, + "execution_count": 58, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(105479, 1) (30450354, 2)\n", + "CPU times: user 5.25 s, sys: 2.41 s, total: 7.67 s\n", + "Wall time: 7.69 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = pd.DataFrame({gg._node: ['116374117927631468606']})\n", + "for i in range(1):\n", + " g2 = gg.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=5)\n", + "print(g2._nodes.shape, g2._edges.shape)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ozQlRPaFBtPD", + "outputId": "4f1655c4-38fd-47f9-942d-836585e0d866" + }, + "execution_count": 59, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(105604, 1) (30468335, 2)\n", + "CPU times: user 2min 16s, sys: 39.1 s, total: 2min 55s\n", + "Wall time: 2min 58s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({gg._node: ['116374117927631468606']})\n", + "gg_gdf = gg.nodes(cudf.from_pandas(gg._nodes)).edges(cudf.from_pandas(gg._edges))\n", + "for i in range(1):\n", + " g2 = gg_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=5)\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del gg_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "-ACkMG20B6HM", + "outputId": "f26c03a9-9f25-4f93-c7d3-0e8676694040" + }, + "execution_count": 60, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(105604, 1) (30468335, 2)\n", + "CPU times: user 5.79 s, sys: 2.51 s, total: 8.3 s\n", + "Wall time: 8.29 s\n" + ] + } + ] + }, + { + "cell_type": "markdown", + "source": [ + "### Orkut\n", + "- 117M edges\n", + "- 3M nodes" + ], + "metadata": { + "id": "R03M_swxarKC" + } + }, + { + "cell_type": "code", + "source": [ + "! wget https://snap.stanford.edu/data/bigdata/communities/com-orkut.ungraph.txt.gz" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "QoabYR2maxPo", + "outputId": "2bb6275d-46bb-42da-ec05-d0e5a58b1f77" + }, + "execution_count": 8, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "--2023-12-26 00:55:52-- https://snap.stanford.edu/data/bigdata/communities/com-orkut.ungraph.txt.gz\n", + "Resolving snap.stanford.edu (snap.stanford.edu)... 171.64.75.80\n", + "Connecting to snap.stanford.edu (snap.stanford.edu)|171.64.75.80|:443... connected.\n", + "HTTP request sent, awaiting response... 200 OK\n", + "Length: 447251958 (427M) [application/x-gzip]\n", + "Saving to: ‘com-orkut.ungraph.txt.gz’\n", + "\n", + "com-orkut.ungraph.t 100%[===================>] 426.53M 45.1MB/s in 9.7s \n", + "\n", + "2023-12-26 00:56:02 (44.0 MB/s) - ‘com-orkut.ungraph.txt.gz’ saved [447251958/447251958]\n", + "\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "! gunzip com-orkut.ungraph.txt.gz" + ], + "metadata": { + "id": "BvvfFPKWbAVJ" + }, + "execution_count": 9, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "! head -n 7 com-orkut.ungraph.txt" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "YsWwRoPqbPIb", + "outputId": "2eb4f862-b4e1-42bf-ff5d-eec10b27cedc" + }, + "execution_count": 10, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "# Undirected graph: ../../data/output/orkut.txt\n", + "# Orkut\n", + "# Nodes: 3072441 Edges: 117185083\n", + "# FromNodeId\tToNodeId\n", + "1\t2\n", + "1\t3\n", + "1\t4\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "import pandas as pd\n", + "\n", + "import graphistry\n", + "\n", + "from graphistry import (\n", + "\n", + " # graph operators\n", + " n, e_undirected, e_forward, e_reverse,\n", + "\n", + " # attribute predicates\n", + " is_in, ge, startswith, contains, match as match_re\n", + ")\n", + "\n", + "import cudf\n", + "\n", + "#work around google colab shell encoding bugs\n", + "import locale\n", + "locale.getpreferredencoding = lambda: \"UTF-8\"\n", + "\n", + "cudf.__version__, graphistry.__version__" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cbMC8r2ldjbW", + "outputId": "82688d53-7d56-4563-d65e-7c5cd32ac14e" + }, + "execution_count": 11, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "('23.12.01', '0.32.0+12.g72e778c')" + ] + }, + "metadata": {}, + "execution_count": 11 + } + ] + }, + { + "cell_type": "code", + "source": [ + "! nvidia-smi" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "TopFxAvnh_Cv", + "outputId": "cc9d9dc9-e594-4190-fe84-3f1b6dce8a1a" + }, + "execution_count": 12, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Tue Dec 26 00:56:27 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 47C P0 27W / 70W | 103MiB / 15360MiB | 0% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "co_df = cudf.read_csv('com-orkut.ungraph.txt', sep='\\t', names=['s', 'd'], skiprows=5).to_pandas()\n", + "print(co_df.shape)\n", + "print(co_df.head(5))\n", + "print(co_df.dtypes)\n", + "#del co_df" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Oczs87ITbJgw", + "outputId": "ac203ddd-e684-4eb9-a586-f6a49fd1625d" + }, + "execution_count": 13, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(117185082, 2)\n", + " s d\n", + "0 1 3\n", + "1 1 4\n", + "2 1 5\n", + "3 1 6\n", + "4 1 7\n", + "s int64\n", + "d int64\n", + "dtype: object\n", + "CPU times: user 2.56 s, sys: 4.2 s, total: 6.76 s\n", + "Wall time: 6.76 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "co_g = graphistry.edges(cudf.DataFrame(co_df), 's', 'd').materialize_nodes(engine='cudf')\n", + "co_g = co_g.nodes(lambda g: g._nodes.to_pandas()).edges(lambda g: g._edges.to_pandas())\n", + "print(co_g._nodes.shape, co_g._edges.shape)\n", + "co_g._nodes.head(5)" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 258 + }, + "id": "gGSDjTtveFAT", + "outputId": "e7b38f4f-dc07-4f35-9bab-9c80a80bbf0b" + }, + "execution_count": 14, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "(3072441, 1) (117185082, 2)\n", + "CPU times: user 1.96 s, sys: 2.95 s, total: 4.91 s\n", + "Wall time: 4.92 s\n" + ] + }, + { + "output_type": "execute_result", + "data": { + "text/plain": [ + " id\n", + "0 1\n", + "1 2\n", + "2 3\n", + "3 4\n", + "4 5" + ], + "text/html": [ + "\n", + "
\n", + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
id
01
12
23
34
45
\n", + "
\n", + "
\n", + "\n", + "
\n", + " \n", + "\n", + " \n", + "\n", + " \n", + "
\n", + "\n", + "\n", + "
\n", + " \n", + "\n", + "\n", + "\n", + " \n", + "
\n", + "
\n", + "
\n" + ] + }, + "metadata": {}, + "execution_count": 14 + } + ] + }, + { + "cell_type": "code", + "source": [ + "! nvidia-smi" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "V5qL8K7-dqIZ", + "outputId": "e08319fc-74d3-4f33-df0f-f98950dc8c99" + }, + "execution_count": 15, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Tue Dec 26 00:56:39 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 49C P0 27W / 70W | 2819MiB / 15360MiB | 0% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "# crashes\n", + "if False:\n", + " out = co_g.chain([ n({'id': 1}), e_forward(hops=1)])._nodes\n", + " print(out.shape)\n", + " del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "hCbxZ8UmhRLp", + "outputId": "519aed6c-733d-41f4-d462-e57f5e32b131" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "CPU times: user 4 µs, sys: 1 µs, total: 5 µs\n", + "Wall time: 47.7 µs\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "co_gdf = co_g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "! nvidia-smi\n", + "for i in range(10):\n", + " out = co_gdf.chain([ n({'id': 1}), e_forward(hops=1)])\n", + "! nvidia-smi\n", + "print(out._nodes.shape, out._edges.shape)\n", + "del co_gdf\n", + "del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Q682scC_eC-S", + "outputId": "7ff5f829-0de7-4a6c-a77d-e2857896a8a5" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Mon Dec 25 06:23:46 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 63C P0 30W / 70W | 1925MiB / 15360MiB | 35% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "Mon Dec 25 06:23:49 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 66C P0 72W / 70W | 2845MiB / 15360MiB | 84% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "(12, 1) (11, 2)\n", + "CPU times: user 4.42 s, sys: 131 ms, total: 4.55 s\n", + "Wall time: 4.42 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "co_gdf = co_g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "! nvidia-smi\n", + "for i in range(10):\n", + " out = co_gdf.chain([ n({'id': 1}), e_forward(hops=2)])\n", + "! nvidia-smi\n", + "print(out._nodes.shape, out._edges.shape)\n", + "del co_gdf\n", + "del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "i0AXhfqVbVsm", + "outputId": "8271f469-a73f-48e3-e1a9-3077026ab8ec" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Mon Dec 25 06:24:52 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 61C P0 29W / 70W | 1925MiB / 15360MiB | 22% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "Mon Dec 25 06:24:58 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 64C P0 71W / 70W | 2845MiB / 15360MiB | 57% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "(391, 1) (461, 2)\n", + "CPU times: user 5.34 s, sys: 132 ms, total: 5.47 s\n", + "Wall time: 6.13 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "co_gdf = co_g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "! nvidia-smi\n", + "for i in range(10):\n", + " out = co_gdf.chain([ n({'id': 1}), e_forward(hops=3)])\n", + "! nvidia-smi\n", + "print(out._nodes.shape, out._edges.shape)\n", + "del co_gdf\n", + "del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Hid0-iPKhpOd", + "outputId": "ecaeb534-d4d7-48fa-d4e1-c80b22626afe" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Mon Dec 25 06:25:25 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 61C P0 29W / 70W | 1925MiB / 15360MiB | 31% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "Mon Dec 25 06:25:31 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 65C P0 71W / 70W | 2849MiB / 15360MiB | 58% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "(21767, 1) (28480, 2)\n", + "CPU times: user 6.25 s, sys: 100 ms, total: 6.35 s\n", + "Wall time: 6.37 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "co_gdf = co_g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "! nvidia-smi\n", + "for i in range(10):\n", + " out = co_gdf.chain([ n({'id': 1}), e_forward(hops=4)])\n", + "! nvidia-smi\n", + "print(out._nodes.shape, out._edges.shape)\n", + "del co_gdf\n", + "del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "buutj-ZjhrEe", + "outputId": "ae11addd-6bea-44e9-81c0-b431e1db8089" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Mon Dec 25 06:26:04 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 61C P0 29W / 70W | 1927MiB / 15360MiB | 36% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "Mon Dec 25 06:26:13 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 65C P0 71W / 70W | 2931MiB / 15360MiB | 90% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "(718640, 1) (2210961, 2)\n", + "CPU times: user 9.01 s, sys: 1.03 s, total: 10 s\n", + "Wall time: 9.84 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "co_gdf = co_g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "! nvidia-smi\n", + "for i in range(10):\n", + " out = co_gdf.chain([ n({'id': 1}), e_forward(hops=5)])\n", + "! nvidia-smi\n", + "print(out._nodes.shape, out._edges.shape)\n", + "del co_gdf\n", + "del out" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bK4C9Ly0hso-", + "outputId": "8a9a32ab-03e2-42b4-8b71-2bcf797b31b1" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Mon Dec 25 06:27:18 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 60C P0 29W / 70W | 1927MiB / 15360MiB | 28% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "Mon Dec 25 06:27:57 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 72C P0 43W / 70W | 4351MiB / 15360MiB | 100% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "(3041556, 1) (47622917, 2)\n", + "CPU times: user 34.9 s, sys: 4.76 s, total: 39.6 s\n", + "Wall time: 39.2 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "co_gdf = co_g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "out = co_gdf.chain([ n({'id': 1}), e_forward(hops=6)])._nodes\n", + "print(out.shape)\n", + "del co_gdf\n", + "del out" + ], + "metadata": { + "id": "qrga-la0hwhh" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "!lscpu\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "eiXFImxF-rzw", + "outputId": "b807cc3d-ed1a-4bef-c6e0-bfc2df7356ff" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Architecture: x86_64\n", + " CPU op-mode(s): 32-bit, 64-bit\n", + " Address sizes: 46 bits physical, 48 bits virtual\n", + " Byte Order: Little Endian\n", + "CPU(s): 2\n", + " On-line CPU(s) list: 0,1\n", + "Vendor ID: GenuineIntel\n", + " Model name: Intel(R) Xeon(R) CPU @ 2.20GHz\n", + " CPU family: 6\n", + " Model: 79\n", + " Thread(s) per core: 2\n", + " Core(s) per socket: 1\n", + " Socket(s): 1\n", + " Stepping: 0\n", + " BogoMIPS: 4399.99\n", + " Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clf\n", + " lush mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc rep_\n", + " good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fm\n", + " a cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hyp\n", + " ervisor lahf_lm abm 3dnowprefetch invpcid_single ssbd ibrs ibpb stibp fsgsb\n", + " ase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm rdseed adx smap xsa\n", + " veopt arat md_clear arch_capabilities\n", + "Virtualization features: \n", + " Hypervisor vendor: KVM\n", + " Virtualization type: full\n", + "Caches (sum of all): \n", + " L1d: 32 KiB (1 instance)\n", + " L1i: 32 KiB (1 instance)\n", + " L2: 256 KiB (1 instance)\n", + " L3: 55 MiB (1 instance)\n", + "NUMA: \n", + " NUMA node(s): 1\n", + " NUMA node0 CPU(s): 0,1\n", + "Vulnerabilities: \n", + " Gather data sampling: Not affected\n", + " Itlb multihit: Not affected\n", + " L1tf: Mitigation; PTE Inversion\n", + " Mds: Vulnerable; SMT Host state unknown\n", + " Meltdown: Vulnerable\n", + " Mmio stale data: Vulnerable\n", + " Retbleed: Vulnerable\n", + " Spec rstack overflow: Not affected\n", + " Spec store bypass: Vulnerable\n", + " Spectre v1: Vulnerable: __user pointer sanitization and usercopy barriers only; no swap\n", + " gs barriers\n", + " Spectre v2: Vulnerable, IBPB: disabled, STIBP: disabled, PBRSB-eIBRS: Not affected\n", + " Srbds: Not affected\n", + " Tsx async abort: Vulnerable\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "!free -h\n" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "wJohLi58-sN5", + "outputId": "c3e144f6-c19a-4c68-e867-f5e7fa2e9df4" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + " total used free shared buff/cache available\n", + "Mem: 12Gi 717Mi 8.0Gi 1.0Mi 3.9Gi 11Gi\n", + "Swap: 0B 0B 0B\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = pd.DataFrame({'id': [1]})\n", + "! nvidia-smi\n", + "for i in range(1):\n", + " g2 = co_g.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=1)\n", + "! nvidia-smi\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "#del start_nodes\n", + "#del co_gdf\n", + "#del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "zak4Inhco5il", + "outputId": "30bcf2bc-853e-4e5e-8c57-ba0cd9429554" + }, + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Tue Dec 26 01:01:43 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 64C P0 30W / 70W | 2821MiB / 15360MiB | 0% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({'id': [1]})\n", + "co_gdf = co_g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "! nvidia-smi\n", + "for i in range(10):\n", + " g2 = co_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=1)\n", + "! nvidia-smi\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del co_gdf\n", + "del g2" + ], + "metadata": { + "id": "-SmFlCBS_Bgx", + "colab": { + "base_uri": "https://localhost:8080/" + }, + "outputId": "d2326cf7-3ea6-4f99-9548-f2e98ece59a4" + }, + "execution_count": 16, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Tue Dec 26 00:56:45 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 49C P0 28W / 70W | 1923MiB / 15360MiB | 37% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "Tue Dec 26 00:56:47 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 52C P0 70W / 70W | 2819MiB / 15360MiB | 79% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "(12, 1) (11, 2)\n", + "CPU times: user 1.6 s, sys: 37.3 ms, total: 1.64 s\n", + "Wall time: 1.84 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({'id': [1]})\n", + "co_gdf = co_g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "! nvidia-smi\n", + "for i in range(10):\n", + " g2 = co_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=2)\n", + "! nvidia-smi\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del co_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "fjjt3YnYnabv", + "outputId": "05762f50-bfe1-4d23-9153-31431418c8e5" + }, + "execution_count": 17, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Tue Dec 26 00:56:47 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 51C P0 35W / 70W | 1923MiB / 15360MiB | 59% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "Tue Dec 26 00:56:49 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 53C P0 59W / 70W | 2821MiB / 15360MiB | 86% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "(391, 1) (461, 2)\n", + "CPU times: user 2.32 s, sys: 58.5 ms, total: 2.38 s\n", + "Wall time: 2.51 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({'id': [1]})\n", + "co_gdf = co_g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "! nvidia-smi\n", + "for i in range(10):\n", + " g2 = co_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=3)\n", + "! nvidia-smi\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del co_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "oIouuORgnbcY", + "outputId": "f07abe4c-5137-4ee3-935a-afbb2c5eaa1e" + }, + "execution_count": 18, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Tue Dec 26 00:56:50 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 52C P0 36W / 70W | 1925MiB / 15360MiB | 55% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "Tue Dec 26 00:56:53 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 54C P0 75W / 70W | 2825MiB / 15360MiB | 74% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "(21767, 1) (28480, 2)\n", + "CPU times: user 3.04 s, sys: 63.6 ms, total: 3.1 s\n", + "Wall time: 3.25 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({'id': [1]})\n", + "co_gdf = co_g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "! nvidia-smi\n", + "for i in range(10):\n", + " g2 = co_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=4)\n", + "! nvidia-smi\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del co_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "oNLZGjwInc85", + "outputId": "534097cf-4022-48cc-9419-a00c135f69e1" + }, + "execution_count": 19, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Tue Dec 26 00:56:53 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 54C P0 36W / 70W | 1927MiB / 15360MiB | 54% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "Tue Dec 26 00:56:58 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 56C P0 38W / 70W | 2907MiB / 15360MiB | 89% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "(718640, 1) (2210961, 2)\n", + "CPU times: user 4.58 s, sys: 309 ms, total: 4.89 s\n", + "Wall time: 5.02 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({'id': [1]})\n", + "co_gdf = co_g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "! nvidia-smi\n", + "for i in range(10):\n", + " g2 = co_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=5)\n", + "! nvidia-smi\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del co_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ePqaeujMneX8", + "outputId": "ffd88fff-016e-4ac0-ecb9-fa06baca60f8" + }, + "execution_count": 20, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Tue Dec 26 00:56:58 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 55C P0 37W / 70W | 1925MiB / 15360MiB | 59% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "Tue Dec 26 00:57:10 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 60C P0 48W / 70W | 4325MiB / 15360MiB | 99% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "(3041556, 1) (47622917, 2)\n", + "CPU times: user 10.8 s, sys: 1.29 s, total: 12.1 s\n", + "Wall time: 12 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [ + "%%time\n", + "start_nodes = cudf.DataFrame({'id': [1]})\n", + "co_gdf = co_g.nodes(lambda g: cudf.DataFrame(g._nodes)).edges(lambda g: cudf.DataFrame(g._edges))\n", + "! nvidia-smi\n", + "for i in range(10):\n", + " g2 = co_gdf.hop(\n", + " nodes=start_nodes,\n", + " direction='forward',\n", + " hops=6)\n", + "! nvidia-smi\n", + "print(g2._nodes.shape, g2._edges.shape)\n", + "del start_nodes\n", + "del co_gdf\n", + "del g2" + ], + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "PTBkoIVHnfzK", + "outputId": "5615ecd7-47ea-46ab-fd36-13bce4b3c787" + }, + "execution_count": 21, + "outputs": [ + { + "output_type": "stream", + "name": "stdout", + "text": [ + "Tue Dec 26 00:57:10 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 59C P0 38W / 70W | 1925MiB / 15360MiB | 44% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "Tue Dec 26 00:57:38 2023 \n", + "+---------------------------------------------------------------------------------------+\n", + "| NVIDIA-SMI 535.104.05 Driver Version: 535.104.05 CUDA Version: 12.2 |\n", + "|-----------------------------------------+----------------------+----------------------+\n", + "| GPU Name Persistence-M | Bus-Id Disp.A | Volatile Uncorr. ECC |\n", + "| Fan Temp Perf Pwr:Usage/Cap | Memory-Usage | GPU-Util Compute M. |\n", + "| | | MIG M. |\n", + "|=========================================+======================+======================|\n", + "| 0 Tesla T4 Off | 00000000:00:04.0 Off | 0 |\n", + "| N/A 68C P0 55W / 70W | 6445MiB / 15360MiB | 95% Default |\n", + "| | | N/A |\n", + "+-----------------------------------------+----------------------+----------------------+\n", + " \n", + "+---------------------------------------------------------------------------------------+\n", + "| Processes: |\n", + "| GPU GI CI PID Type Process name GPU Memory |\n", + "| ID ID Usage |\n", + "|=======================================================================================|\n", + "+---------------------------------------------------------------------------------------+\n", + "(3071927, 1) (117032738, 2)\n", + "CPU times: user 23.5 s, sys: 2.68 s, total: 26.2 s\n", + "Wall time: 28.2 s\n" + ] + } + ] + }, + { + "cell_type": "code", + "source": [], + "metadata": { + "id": "Ygc2nrkznlCu" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file From 9a9de515a725e1d173f9f3e2197249e34349833e Mon Sep 17 00:00:00 2001 From: Leo Meyerovich Date: Tue, 26 Dec 2023 23:52:50 -0800 Subject: [PATCH 104/104] docs(changelog) --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd1fcab40b..84348f3c9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,11 +7,13 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm ## [Development] +## [0.33.0 - 2023-12-26] + ### Added +* GFQL: GPU acceleration of `chain`, `hop`, `filter_by_dict` * `AbstractEngine` to `engine.py::Engine` enum * `compute.typing.DataFrameT` to centralize df-lib-agnostic type checking -* `chain`, `hop`, `filter_by_dict` variants support GPU execution ### Refactor