From be524759327d5594bbbf6baf2de41b480f9663ab Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Fri, 30 Aug 2024 18:37:39 -0300 Subject: [PATCH 01/48] Mechanism exploration --- dev/memory_storage.ipynb | 465 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 465 insertions(+) create mode 100644 dev/memory_storage.ipynb diff --git a/dev/memory_storage.ipynb b/dev/memory_storage.ipynb new file mode 100644 index 0000000..97486ca --- /dev/null +++ b/dev/memory_storage.ipynb @@ -0,0 +1,465 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import redis\n", + "import cst_python as cst\n", + "import json" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "client = redis.Redis(decode_responses=True)\n", + "pubsub = client.pubsub()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.flushall()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Node1 publica que existe" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.lpush(\"nodes\", \"node1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "class MemoryEncoder(json.JSONEncoder):\n", + " def default(self, memory:cst.core.entities.Memory):\n", + " return MemoryEncoder.to_dict(memory)\n", + " \n", + " @staticmethod\n", + " def to_dict(memory:cst.core.entities.Memory):\n", + " data = {\n", + " \"timestamp\": memory.get_timestamp(),\n", + " \"evaluation\": memory.get_evaluation(),\n", + " \"I\": memory.get_info(),\n", + " \"name\": memory.get_name(),\n", + " \"id\": memory.get_id()\n", + " }\n", + "\n", + " return data" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Node1 checa se memória com id \"memory1\" existe. Como não, publica key:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def update_memory(memory_name, memory_object:cst.MemoryObject, client:redis.Redis):\n", + " timestamp = float(client.hget(f\"memories:{memory_name}\", \"timestamp\"))\n", + " \n", + " if memory_object.timestamp < timestamp:\n", + " print(\"Retrieve update\")\n", + " memory_dict = client.hgetall(f\"memories:{memory_name}\")\n", + "\n", + " memory_object.set_evaluation(float(memory_dict[\"evaluation\"]))\n", + " memory_object.set_name(memory_dict[\"name\"])\n", + " memory_object.set_id(float(memory_dict[\"id\"]))\n", + "\n", + " info_json = memory_dict[\"I\"]\n", + " info = json.loads(info_json)\n", + "\n", + " memory_object.set_info(info)\n", + "\n", + " memory_object.timestamp = float(memory_dict[\"timestamp\"])\n", + " elif memory_object.timestamp > timestamp:\n", + " print(\"Send update\")\n", + " memory_dict = MemoryEncoder.to_dict(memory_object)\n", + " memory_dict[\"I\"] = json.dumps(memory_dict[\"I\"])\n", + "\n", + " client.hset(f\"memories:{memory_name}\", mapping=memory_dict)\n", + " client.publish(f\"memories:{memory_name}:update\", \"\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def create_memory(node, memory_name, client:redis.Redis, pubsub:redis.client.PubSub) -> cst.MemoryObject:\n", + " nodes = client.lrange(\"nodes\", 0, -1)\n", + "\n", + " memory_exist = False\n", + " memory_node = \"\"\n", + "\n", + " for n in nodes:\n", + " if n == node:\n", + " continue\n", + " \n", + " if memory_name in client.lrange(f\"{n}:memories\", 0, -1):\n", + " memory_exist = True\n", + " memory_node = n\n", + "\n", + " break\n", + "\n", + " memory = cst.MemoryObject()\n", + "\n", + " if memory_exist:\n", + " #Copia memória\n", + " print(\"Copia\")\n", + "\n", + " memory_dict = client.hgetall(f\"memories:{memory_name}\")\n", + "\n", + " memory.set_evaluation(float(memory_dict[\"evaluation\"]))\n", + " memory.set_name(memory_dict[\"name\"])\n", + " memory.set_id(float(memory_dict[\"id\"]))\n", + "\n", + " info_json = memory_dict[\"I\"]\n", + " info = json.loads(info_json)\n", + "\n", + " memory.set_info(info)\n", + "\n", + " memory.timestamp = float(memory_dict[\"timestamp\"])\n", + " \n", + " else:\n", + " #Indica que memória existe\n", + " print(\"Cria\")\n", + " client.lpush(f\"{node}:memories\", memory_name)\n", + "\n", + " memory_dict = MemoryEncoder.to_dict(memory)\n", + " memory_dict[\"I\"] = json.dumps(memory_dict[\"I\"])\n", + "\n", + " client.hset(f\"memories:{memory_name}\", mapping=memory_dict)\n", + "\n", + " subscribe_func = lambda message : update_memory(memory_name, memory, client)\n", + " pubsub.subscribe(**{f\"memories:{memory_name}:update\":subscribe_func})\n", + "\n", + " return memory" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cria\n" + ] + } + ], + "source": [ + "memory1 = create_memory(\"node1\", \"memory1\", client, pubsub)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "MemoryObject [idmemoryobject=0.0, timestamp=0.0, evaluation=0.0, I=None, name=]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memory1" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "node2 entra no jogo" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "client2 = redis.Redis()\n", + "pubsub2 = client2.pubsub()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "2" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client2.lpush(\"nodes\", \"node2\")" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[b'node2', b'node1']" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "nodes = client2.lrange(\"nodes\", 0, -1)\n", + "nodes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "node2 tenta criar memória, percebe que existe e sincroniza" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cria\n" + ] + }, + { + "data": { + "text/plain": [ + "MemoryObject [idmemoryobject=0.0, timestamp=0.0, evaluation=0.0, I=None, name=]" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "node2_memory1 = create_memory(\"node2\", \"memory1\", client2, pubsub2)\n", + "\n", + "node2_memory1" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Cria\n" + ] + }, + { + "data": { + "text/plain": [ + "MemoryObject [idmemoryobject=0.0, timestamp=0.0, evaluation=0.0, I=None, name=]" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memory2 = create_memory(\"node2\", \"memory2\", client2, pubsub2)\n", + "\n", + "memory2" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Send update\n" + ] + } + ], + "source": [ + "node2_memory1.set_info(\"INFO\")\n", + "update_memory(\"memory1\", node2_memory1, client2)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'type': 'subscribe', 'pattern': None, 'channel': 'memories:memory1:update', 'data': 1}\n", + "Retrieve update\n", + "{'type': 'subscribe', 'pattern': None, 'channel': b'memories:memory1:update', 'data': 1}\n", + "{'type': 'subscribe', 'pattern': None, 'channel': b'memories:memory2:update', 'data': 2}\n" + ] + } + ], + "source": [ + "for _pubsub in [pubsub, pubsub2]:\n", + " msg = _pubsub.get_message()\n", + " while msg is not None:\n", + " print(msg)\n", + " msg = _pubsub.get_message()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'timestamp': '1725053432.9742534',\n", + " 'evaluation': '0.0',\n", + " 'I': '\"INFO\"',\n", + " 'name': '',\n", + " 'id': '0.0'}" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.hgetall(\"memories:memory1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "MemoryObject [idmemoryobject=0.0, timestamp=1725053432.9742534, evaluation=0.0, I=INFO, name=]" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memory1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From d4157264f59f9797f8d68f2087df49d69ce6f8e0 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:25:44 -0300 Subject: [PATCH 02/48] MemoryStorageCodelet --- dev/memory_storage.ipynb | 167 ++++---- dev/memory_storage_codelet.ipynb | 715 +++++++++++++++++++++++++++++++ 2 files changed, 802 insertions(+), 80 deletions(-) create mode 100644 dev/memory_storage_codelet.ipynb diff --git a/dev/memory_storage.ipynb b/dev/memory_storage.ipynb index 97486ca..06854c0 100644 --- a/dev/memory_storage.ipynb +++ b/dev/memory_storage.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ @@ -13,7 +13,16 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "mind_name = \"default_mind\"" + ] + }, + { + "cell_type": "code", + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ @@ -23,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 25, "metadata": {}, "outputs": [ { @@ -32,7 +41,7 @@ "True" ] }, - "execution_count": 3, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -50,7 +59,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -59,18 +68,18 @@ "1" ] }, - "execution_count": 4, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "client.lpush(\"nodes\", \"node1\")" + "client.lpush(f\"{mind_name}:nodes\", \"node1\")" ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -100,16 +109,16 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "def update_memory(memory_name, memory_object:cst.MemoryObject, client:redis.Redis):\n", - " timestamp = float(client.hget(f\"memories:{memory_name}\", \"timestamp\"))\n", + " timestamp = float(client.hget(f\"{mind_name}:memories:{memory_name}\", \"timestamp\"))\n", " \n", " if memory_object.timestamp < timestamp:\n", " print(\"Retrieve update\")\n", - " memory_dict = client.hgetall(f\"memories:{memory_name}\")\n", + " memory_dict = client.hgetall(f\"{mind_name}:memories:{memory_name}\")\n", "\n", " memory_object.set_evaluation(float(memory_dict[\"evaluation\"]))\n", " memory_object.set_name(memory_dict[\"name\"])\n", @@ -126,39 +135,31 @@ " memory_dict = MemoryEncoder.to_dict(memory_object)\n", " memory_dict[\"I\"] = json.dumps(memory_dict[\"I\"])\n", "\n", - " client.hset(f\"memories:{memory_name}\", mapping=memory_dict)\n", - " client.publish(f\"memories:{memory_name}:update\", \"\")" + " client.hset(f\"{mind_name}:memories:{memory_name}\", mapping=memory_dict)\n", + " client.publish(f\"{mind_name}:memories:{memory_name}:update\", \"\")" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ "def create_memory(node, memory_name, client:redis.Redis, pubsub:redis.client.PubSub) -> cst.MemoryObject:\n", - " nodes = client.lrange(\"nodes\", 0, -1)\n", - "\n", - " memory_exist = False\n", - " memory_node = \"\"\n", + " memory = cst.MemoryObject()\n", "\n", - " for n in nodes:\n", - " if n == node:\n", - " continue\n", - " \n", - " if memory_name in client.lrange(f\"{n}:memories\", 0, -1):\n", - " memory_exist = True\n", - " memory_node = n\n", + " if client.exists(f\"{mind_name}:memories:{memory_name}\"):\n", + " memory_dict = client.hgetall(f\"{mind_name}:memories:{memory_name}\")\n", "\n", - " break\n", + " if memory_dict[\"owner\"] != \"\":\n", + " #Solicita memória\n", + " pass\n", "\n", - " memory = cst.MemoryObject()\n", "\n", - " if memory_exist:\n", " #Copia memória\n", " print(\"Copia\")\n", "\n", - " memory_dict = client.hgetall(f\"memories:{memory_name}\")\n", + " memory_dict = client.hgetall(f\"{mind_name}:memories:{memory_name}\")\n", "\n", " memory.set_evaluation(float(memory_dict[\"evaluation\"]))\n", " memory.set_name(memory_dict[\"name\"])\n", @@ -170,7 +171,6 @@ " memory.set_info(info)\n", "\n", " memory.timestamp = float(memory_dict[\"timestamp\"])\n", - " \n", " else:\n", " #Indica que memória existe\n", " print(\"Cria\")\n", @@ -178,18 +178,19 @@ "\n", " memory_dict = MemoryEncoder.to_dict(memory)\n", " memory_dict[\"I\"] = json.dumps(memory_dict[\"I\"])\n", + " memory_dict[\"owner\"] = \"\" #node\n", "\n", - " client.hset(f\"memories:{memory_name}\", mapping=memory_dict)\n", + " client.hset(f\"{mind_name}:memories:{memory_name}\", mapping=memory_dict)\n", "\n", " subscribe_func = lambda message : update_memory(memory_name, memory, client)\n", - " pubsub.subscribe(**{f\"memories:{memory_name}:update\":subscribe_func})\n", + " pubsub.subscribe(**{f\"{mind_name}:memories:{memory_name}:update\":subscribe_func})\n", "\n", " return memory" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 30, "metadata": {}, "outputs": [ { @@ -206,7 +207,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -215,7 +216,7 @@ "MemoryObject [idmemoryobject=0.0, timestamp=0.0, evaluation=0.0, I=None, name=]" ] }, - "execution_count": 9, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -233,17 +234,17 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ - "client2 = redis.Redis()\n", + "client2 = redis.Redis(decode_responses=True)\n", "pubsub2 = client2.pubsub()" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -252,33 +253,33 @@ "2" ] }, - "execution_count": 11, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "client2.lpush(\"nodes\", \"node2\")" + "client2.lpush(f\"{mind_name}:nodes\", \"node2\")" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 34, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "[b'node2', b'node1']" + "['node2', 'node1']" ] }, - "execution_count": 12, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "nodes = client2.lrange(\"nodes\", 0, -1)\n", + "nodes = client2.lrange(f\"{mind_name}:nodes\", 0, -1)\n", "nodes" ] }, @@ -291,14 +292,39 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'timestamp': '0.0',\n", + " 'evaluation': '0.0',\n", + " 'I': 'null',\n", + " 'name': '',\n", + " 'id': '0.0',\n", + " 'owner': ''}" + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.hgetall(f\"{mind_name}:memories:memory1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 36, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Cria\n" + "Copia\n" ] }, { @@ -307,7 +333,7 @@ "MemoryObject [idmemoryobject=0.0, timestamp=0.0, evaluation=0.0, I=None, name=]" ] }, - "execution_count": 13, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -320,7 +346,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 37, "metadata": {}, "outputs": [ { @@ -336,7 +362,7 @@ "MemoryObject [idmemoryobject=0.0, timestamp=0.0, evaluation=0.0, I=None, name=]" ] }, - "execution_count": 14, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } @@ -349,7 +375,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 38, "metadata": {}, "outputs": [ { @@ -367,17 +393,17 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 39, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "{'type': 'subscribe', 'pattern': None, 'channel': 'memories:memory1:update', 'data': 1}\n", + "{'type': 'subscribe', 'pattern': None, 'channel': 'default_mind:memories:memory1:update', 'data': 1}\n", "Retrieve update\n", - "{'type': 'subscribe', 'pattern': None, 'channel': b'memories:memory1:update', 'data': 1}\n", - "{'type': 'subscribe', 'pattern': None, 'channel': b'memories:memory2:update', 'data': 2}\n" + "{'type': 'subscribe', 'pattern': None, 'channel': 'default_mind:memories:memory1:update', 'data': 1}\n", + "{'type': 'subscribe', 'pattern': None, 'channel': 'default_mind:memories:memory2:update', 'data': 2}\n" ] } ], @@ -391,51 +417,32 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 40, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "{'timestamp': '1725053432.9742534',\n", + "{'timestamp': '1725638895.8791993',\n", " 'evaluation': '0.0',\n", " 'I': '\"INFO\"',\n", " 'name': '',\n", - " 'id': '0.0'}" + " 'id': '0.0',\n", + " 'owner': ''}" ] }, - "execution_count": 17, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "client.hgetall(\"memories:memory1\")" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "MemoryObject [idmemoryobject=0.0, timestamp=1725053432.9742534, evaluation=0.0, I=INFO, name=]" - ] - }, - "execution_count": 18, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "memory1" + "client.hgetall(f\"{mind_name}:memories:memory1\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 42, "metadata": {}, "outputs": [], "source": [] diff --git a/dev/memory_storage_codelet.ipynb b/dev/memory_storage_codelet.ipynb new file mode 100644 index 0000000..581e762 --- /dev/null +++ b/dev/memory_storage_codelet.ipynb @@ -0,0 +1,715 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import weakref\n", + "import json\n", + "import asyncio\n", + "import threading\n", + "from concurrent.futures import ThreadPoolExecutor\n", + "from typing import Optional, cast, List\n", + "\n", + "import redis\n", + "\n", + "\n", + "import cst_python as cst\n", + "from cst_python.core.entities import Memory, Mind" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client = redis.Redis(decode_responses=True)\n", + "client.flushall()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "class MemoryEncoder(json.JSONEncoder):\n", + " def default(self, memory:cst.core.entities.Memory):\n", + " return MemoryEncoder.to_dict(memory)\n", + " \n", + " @staticmethod\n", + " def to_dict(memory:cst.core.entities.Memory):\n", + " data = {\n", + " \"timestamp\": memory.get_timestamp(),\n", + " \"evaluation\": memory.get_evaluation(),\n", + " \"I\": memory.get_info(),\n", + " \"name\": memory.get_name(),\n", + " \"id\": memory.get_id()\n", + " }\n", + "\n", + " return data" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "class MemoryStorageCodelet:\n", + " def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[str]=None, request_timeout:float=500e-3) -> None:\n", + " self._mind = mind\n", + " self._request_timeout = request_timeout\n", + " \n", + " if mind_name is None:\n", + " mind_name = \"default_mind\"\n", + " self._mind_name = cast(str, mind_name)\n", + " \n", + " self._memories : weakref.WeakValueDictionary[str, Memory] = weakref.WeakValueDictionary()\n", + " \n", + " self._client = redis.Redis(decode_responses=True)\n", + " self._pubsub = self._client.pubsub()\n", + " self._pubsub_thread : redis.client.PubSubWorkerThread = self._pubsub.run_in_thread()\n", + "\n", + " if node_name is None:\n", + " node_number = self._client.scard(f\"{mind_name}:nodes\")\n", + "\n", + " node_name = f\"node{node_number}\"\n", + " while self._client.sismember(f\"{mind_name}:nodes\", node_name):\n", + " node_number += 1\n", + " node_name = f\"node{node_number}\"\n", + "\n", + " self._node_name = cast(str, node_name)\n", + "\n", + " self._client.sadd(f\"{mind_name}:nodes\", node_name)\n", + "\n", + " transfer_service_addr = f\"{self._mind_name}:nodes:{node_name}:transfer_memory\"\n", + " self._pubsub.subscribe(**{transfer_service_addr:self.transfer_memory})\n", + "\n", + " transfer_done_addr = f\"{self._mind_name}:nodes:{node_name}:transfer_done\"\n", + " self._pubsub.subscribe(**{transfer_done_addr:self.notify_transfer})\n", + "\n", + " self._last_update : dict[str, float] = {}\n", + " self._waiting_retrieve : set[str] = set()\n", + " \n", + " self._retrieve_executor = ThreadPoolExecutor(3)\n", + "\n", + " self._waiting_request_events : dict[str, threading.Event] = {}\n", + "\n", + " self._request = None\n", + "\n", + " def proc(self) -> None:\n", + " \n", + " #Check new memories\n", + "\n", + " mind_memories = {}\n", + " for memory in self._mind.raw_memory.all_memories:\n", + " if memory.get_name() == \"\": #No name -> No MS\n", + " continue\n", + "\n", + " mind_memories[memory.get_name()] = memory\n", + "\n", + " mind_memories_names = set(mind_memories.keys())\n", + " memories_names = set(self._memories.keys())\n", + "\n", + " #Check only not here (memories_names not in mind should be garbage collected)\n", + " difference = mind_memories_names - memories_names\n", + " for memory_name in difference:\n", + " memory : Memory = mind_memories[memory_name]\n", + " self._memories[memory_name] = memory\n", + "\n", + " if self._client.exists(f\"{self._mind_name}:memories:{memory_name}\"):\n", + " self._retrieve_executor.submit(self.retrieve_memory, memory)\n", + " \n", + " else: #Send impostor with owner\n", + " memory_impostor = {\"name\":memory.get_name(),\n", + " \"evalution\" : 0.0,\n", + " \"I\": \"\",\n", + " \"id\" : \"0.0\",\n", + " \"owner\": self._node_name}\n", + " \n", + " self._client.hset(f\"{self._mind_name}:memories:{memory_name}\", mapping=memory_impostor)\n", + "\n", + " subscribe_func = lambda message : self.update_memory(memory_name)\n", + " self._pubsub.subscribe(**{f\"{self._mind_name}:memories:{memory_name}:update\":subscribe_func})\n", + "\n", + " #Update memories\n", + " to_update = self._last_update.keys()\n", + " for memory_name in to_update:\n", + " if memory_name not in self._memories:\n", + " del self._last_update[memory_name]\n", + " continue\n", + "\n", + " memory = self._memories[memory_name]\n", + " if memory.get_timestamp() > self._last_update[memory_name]:\n", + " self.update_memory(memory_name)\n", + "\n", + " def transfer_memory(self, message) -> None:\n", + " request = json.loads(message[\"data\"])\n", + " \n", + " memory_name = request[\"memory_name\"]\n", + " requesting_node = request[\"node\"]\n", + "\n", + " print(self._node_name, \"Tranfering\", memory_name)\n", + "\n", + " if memory_name in self._memories:\n", + " memory = self._memories[memory_name]\n", + " else:\n", + " memory = cst.MemoryObject()\n", + " memory.set_name(memory_name)\n", + " \n", + " self.send_memory(memory)\n", + "\n", + " response_addr = f\"{self._mind_name}:nodes:{requesting_node}:transfer_done\"\n", + " self._client.publish(response_addr, memory_name)\n", + "\n", + "\n", + " def send_memory(self, memory:Memory) -> None:\n", + " memory_name = memory.get_name()\n", + " print(self._node_name, \"Send memory\", memory_name)\n", + " \n", + " memory_dict = MemoryEncoder.to_dict(memory)\n", + " memory_dict[\"I\"] = json.dumps(memory_dict[\"I\"])\n", + " memory_dict[\"owner\"] = \"\"\n", + "\n", + "\n", + " self._client.hset(f\"{self._mind_name}:memories:{memory_name}\", mapping=memory_dict)\n", + " self._client.publish(f\"{self._mind_name}:memories:{memory_name}:update\", \"\")\n", + "\n", + " self._last_update[memory_name] = memory.get_timestamp()\n", + " \n", + " def update_memory(self, memory_name:str) -> None:\n", + " print(self._node_name, \"Updating memory\", memory_name)\n", + "\n", + " timestamp = float(self._client.hget(f\"{self._mind_name}:memories:{memory_name}\", \"timestamp\"))\n", + " memory = self._memories[memory_name]\n", + " memory_timestamp = memory.get_timestamp()\n", + " \n", + " if memory_timestamp < timestamp:\n", + " self._retrieve_executor.submit(self.retrieve_memory, memory)\n", + "\n", + " elif memory_timestamp> timestamp:\n", + " self.send_memory(memory)\n", + "\n", + " self._last_update[memory_name] = memory.get_timestamp()\n", + "\n", + " def retrieve_memory(self, memory:Memory) -> None:\n", + " memory_name = memory.get_name()\n", + "\n", + " print(self._node_name, \"Retrieve\", memory_name)\n", + "\n", + " if memory_name in self._waiting_retrieve:\n", + " return\n", + " self._waiting_retrieve.add(memory_name)\n", + "\n", + " memory_dict = self._client.hgetall(f\"{self._mind_name}:memories:{memory_name}\")\n", + "\n", + " if memory_dict[\"owner\"] != \"\":\n", + " event = threading.Event()\n", + " self._waiting_request_events[memory_name] = event\n", + " self.request_memory(memory_name, memory_dict[\"owner\"])\n", + "\n", + " if not event.wait(timeout=self._request_timeout):\n", + " print(self._node_name, \"Request failed\", memory_name)\n", + " #Request failed\n", + " self.send_memory(memory)\n", + " return \n", + " \n", + " memory_dict = self._client.hgetall(f\"{self._mind_name}:memories:{memory_name}\")\n", + "\n", + " memory.set_evaluation(float(memory_dict[\"evaluation\"]))\n", + " memory.set_id(float(memory_dict[\"id\"]))\n", + "\n", + " info_json = memory_dict[\"I\"]\n", + " info = json.loads(info_json)\n", + "\n", + " print(self._node_name, \"INFO\", info, info_json)\n", + "\n", + " memory.set_info(info)\n", + "\n", + " self._last_update[memory_name] = memory.get_timestamp()\n", + "\n", + " self._waiting_retrieve.remove(memory_name)\n", + "\n", + " def request_memory(self, memory_name:str, owner_name:str):\n", + " print(self._node_name, \"Requesting\", memory_name)\n", + "\n", + " request_addr = f\"{self._mind_name}:nodes:{owner_name}:transfer_memory\"\n", + " \n", + " request_dict = {\"memory_name\":memory_name, \"node\":self._node_name}\n", + " request = json.dumps(request_dict)\n", + " self._client.publish(request_addr, request)\n", + "\n", + " def notify_transfer(self, message:str) -> None:\n", + " memory_name = message[\"data\"]\n", + " if memory_name in self._waiting_request_events:\n", + " event = self._waiting_request_events[memory_name]\n", + " event.set()\n", + " del self._waiting_request_events[memory_name]\n", + "\n", + " def __del__(self) -> None:\n", + " self._pubsub_thread.stop()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "mind = cst.Mind()\n", + "memory1 = mind.create_memory_object(\"Memory1\", \"\")" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "ms_codelet = MemoryStorageCodelet(mind, \"node0\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "ms_codelet.proc()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'node0'}" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.smembers(\"default_mind:nodes\")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'Memory1', 'evalution': '0.0', 'I': '', 'id': '0.0', 'owner': 'node0'}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.hgetall(\"default_mind:memories:Memory1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "mind2 = cst.Mind()\n", + "mind2_memory1 = mind2.create_memory_object(\"Memory1\", \"\")\n", + "mind2_ms_codelet = MemoryStorageCodelet(mind2)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "MemoryObject [idmemoryobject=0, timestamp=1725654264.298408, evaluation=0.0, I=, name=Memory1]" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mind2_memory1" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'node1'" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mind2_ms_codelet._node_name" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'node0', 'node1'}" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.smembers(\"default_mind:nodes\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "node1 Retrieve Memory1\n", + "node1 Requesting Memory1\n", + "node0 Tranfering Memory1\n", + "node0 Send memory Memory1\n" + ] + } + ], + "source": [ + "mind2_ms_codelet.proc()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "node1 Updating memory Memory1\n", + "node0 Updating memory Memory1\n", + "node1 Send memory Memory1\n", + "node0 Updating memory Memory1\n", + "node1 Updating memory Memory1\n", + "node0 Retrieve Memory1\n", + "node0 INFO \"\"\n", + "node1 INFO \"\"\n" + ] + }, + { + "data": { + "text/plain": [ + "MemoryObject [idmemoryobject=0.0, timestamp=1725654264.7690487, evaluation=0.0, I=, name=Memory1]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mind2_memory1" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'Memory1',\n", + " 'evalution': '0.0',\n", + " 'I': '\"\"',\n", + " 'id': '0',\n", + " 'owner': '',\n", + " 'timestamp': '1725654264.298408',\n", + " 'evaluation': '0.0'}" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.hgetall(\"default_mind:memories:Memory1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memory1.set_info(\"INFO\")" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "node0 Updating memory Memory1\n", + "node0 Send memory Memory1\n", + "node1 Updating memory Memory1\n", + "node0 Updating memory Memory1\n", + "node1 Retrieve Memory1\n" + ] + } + ], + "source": [ + "ms_codelet.proc()" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "node1 INFO INFO \"INFO\"\n" + ] + }, + { + "data": { + "text/plain": [ + "{'name': 'Memory1',\n", + " 'evalution': '0.0',\n", + " 'I': '\"INFO\"',\n", + " 'id': '0.0',\n", + " 'owner': '',\n", + " 'timestamp': '1725654264.794752',\n", + " 'evaluation': '0.0'}" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.hgetall(\"default_mind:memories:Memory1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "MemoryObject [idmemoryobject=0.0, timestamp=1725654264.8138657, evaluation=0.0, I=INFO, name=Memory1]" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mind2_memory1" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "mind2_ms_codelet.proc()" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'Memory1',\n", + " 'evalution': '0.0',\n", + " 'I': '\"INFO\"',\n", + " 'id': '0.0',\n", + " 'owner': '',\n", + " 'timestamp': '1725654264.794752',\n", + " 'evaluation': '0.0'}" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.hgetall(\"default_mind:memories:Memory1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "node1 Updating memory Memory1\n", + "node1 Send memory Memory1\n", + "node0 Updating memory Memory1\n", + "node1 Updating memory Memory1\n", + "node0 Retrieve Memory1\n", + "node0 INFO INFO2 \"INFO2\"\n" + ] + } + ], + "source": [ + "mind2_memory1.set_info(\"INFO2\")\n", + "mind2_ms_codelet.proc()" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'name': 'Memory1',\n", + " 'evalution': '0.0',\n", + " 'I': '\"INFO2\"',\n", + " 'id': '0.0',\n", + " 'owner': '',\n", + " 'timestamp': '1725654302.9360394',\n", + " 'evaluation': '0.0'}" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.hgetall(\"default_mind:memories:Memory1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "MemoryObject [idmemoryobject=0.0, timestamp=1725654302.943039, evaluation=0.0, I=INFO2, name=Memory1]" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memory1" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 837723d9d2d54a078dc08e06ac97acf125d75803 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Fri, 13 Sep 2024 11:12:31 -0300 Subject: [PATCH 03/48] Convert MS to Codelet --- dev/memory_storage_codelet.ipynb | 294 ++++++++++++++++++++++++------- dev/weak_test.py | 18 ++ 2 files changed, 249 insertions(+), 63 deletions(-) create mode 100644 dev/weak_test.py diff --git a/dev/memory_storage_codelet.ipynb b/dev/memory_storage_codelet.ipynb index 581e762..d90f888 100644 --- a/dev/memory_storage_codelet.ipynb +++ b/dev/memory_storage_codelet.ipynb @@ -9,14 +9,13 @@ "import json\n", "import weakref\n", "import json\n", - "import asyncio\n", "import threading\n", + "import time\n", "from concurrent.futures import ThreadPoolExecutor\n", "from typing import Optional, cast, List\n", "\n", "import redis\n", "\n", - "\n", "import cst_python as cst\n", "from cst_python.core.entities import Memory, Mind" ] @@ -71,8 +70,10 @@ "metadata": {}, "outputs": [], "source": [ - "class MemoryStorageCodelet:\n", + "class MemoryStorageCodelet(cst.Codelet):\n", " def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[str]=None, request_timeout:float=500e-3) -> None:\n", + " super().__init__()\n", + " \n", " self._mind = mind\n", " self._request_timeout = request_timeout\n", " \n", @@ -113,6 +114,12 @@ "\n", " self._request = None\n", "\n", + " def calculate_activation(self) -> None:\n", + " pass\n", + "\n", + " def access_memory_objects(self) -> None:\n", + " pass\n", + "\n", " def proc(self) -> None:\n", " \n", " #Check new memories\n", @@ -138,7 +145,7 @@ " \n", " else: #Send impostor with owner\n", " memory_impostor = {\"name\":memory.get_name(),\n", - " \"evalution\" : 0.0,\n", + " \"evaluation\" : 0.0,\n", " \"I\": \"\",\n", " \"id\" : \"0.0\",\n", " \"owner\": self._node_name}\n", @@ -196,6 +203,9 @@ " def update_memory(self, memory_name:str) -> None:\n", " print(self._node_name, \"Updating memory\", memory_name)\n", "\n", + " if memory_name not in self._memories:\n", + " self._pubsub.unsubscribe(f\"{self._mind_name}:memories:{memory_name}:update\")\n", + "\n", " timestamp = float(self._client.hget(f\"{self._mind_name}:memories:{memory_name}\", \"timestamp\"))\n", " memory = self._memories[memory_name]\n", " memory_timestamp = memory.get_timestamp()\n", @@ -263,7 +273,8 @@ " del self._waiting_request_events[memory_name]\n", "\n", " def __del__(self) -> None:\n", - " self._pubsub_thread.stop()" + " self._pubsub_thread.stop()\n", + " self._retrieve_executor.shutdown(cancel_futures=True)" ] }, { @@ -282,7 +293,11 @@ "metadata": {}, "outputs": [], "source": [ - "ms_codelet = MemoryStorageCodelet(mind, \"node0\")" + "ms_codelet = MemoryStorageCodelet(mind, \"node0\")\n", + "ms_codelet.time_step = 100\n", + "\n", + "mind.insert_codelet(ms_codelet)\n", + "mind.start()" ] }, { @@ -291,7 +306,7 @@ "metadata": {}, "outputs": [], "source": [ - "ms_codelet.proc()" + "time.sleep(1)" ] }, { @@ -322,7 +337,11 @@ { "data": { "text/plain": [ - "{'name': 'Memory1', 'evalution': '0.0', 'I': '', 'id': '0.0', 'owner': 'node0'}" + "{'name': 'Memory1',\n", + " 'evaluation': '0.0',\n", + " 'I': '',\n", + " 'id': '0.0',\n", + " 'owner': 'node0'}" ] }, "execution_count": 9, @@ -342,7 +361,10 @@ "source": [ "mind2 = cst.Mind()\n", "mind2_memory1 = mind2.create_memory_object(\"Memory1\", \"\")\n", - "mind2_ms_codelet = MemoryStorageCodelet(mind2)" + "mind2_ms_codelet = MemoryStorageCodelet(mind2)\n", + "mind2_ms_codelet.time_step = 100\n", + "mind2.insert_codelet(mind2_ms_codelet)\n", + "mind2.start()" ] }, { @@ -350,10 +372,28 @@ "execution_count": 11, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "node1 Retrieve Memory1\n", + "node1 Requesting Memory1\n", + "node0 Tranfering Memory1\n", + "node0 Send memory Memory1\n", + "node1 Updating memory Memory1\n", + "node0 Updating memory Memory1\n", + "node1 Send memory Memory1\n", + "node1 Updating memory Memory1\n", + "node0 Updating memory Memory1\n", + "node0 Retrieve Memory1\n", + "node0 INFO \"\"\n", + "node1 INFO \"\"\n" + ] + }, { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0, timestamp=1725654264.298408, evaluation=0.0, I=, name=Memory1]" + "MemoryObject [idmemoryobject=0.0, timestamp=1726077369.7999365, evaluation=0.0, I=, name=Memory1]" ] }, "execution_count": 11, @@ -409,20 +449,9 @@ "cell_type": "code", "execution_count": 14, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "node1 Retrieve Memory1\n", - "node1 Requesting Memory1\n", - "node0 Tranfering Memory1\n", - "node0 Send memory Memory1\n" - ] - } - ], + "outputs": [], "source": [ - "mind2_ms_codelet.proc()" + "time.sleep(1)" ] }, { @@ -430,24 +459,10 @@ "execution_count": 15, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "node1 Updating memory Memory1\n", - "node0 Updating memory Memory1\n", - "node1 Send memory Memory1\n", - "node0 Updating memory Memory1\n", - "node1 Updating memory Memory1\n", - "node0 Retrieve Memory1\n", - "node0 INFO \"\"\n", - "node1 INFO \"\"\n" - ] - }, { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0.0, timestamp=1725654264.7690487, evaluation=0.0, I=, name=Memory1]" + "MemoryObject [idmemoryobject=0.0, timestamp=1726077369.7999365, evaluation=0.0, I=, name=Memory1]" ] }, "execution_count": 15, @@ -468,12 +483,11 @@ "data": { "text/plain": [ "{'name': 'Memory1',\n", - " 'evalution': '0.0',\n", + " 'evaluation': '0.0',\n", " 'I': '\"\"',\n", " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1725654264.298408',\n", - " 'evaluation': '0.0'}" + " 'timestamp': '1726077369.5866976'}" ] }, "execution_count": 16, @@ -518,12 +532,13 @@ "node0 Send memory Memory1\n", "node1 Updating memory Memory1\n", "node0 Updating memory Memory1\n", - "node1 Retrieve Memory1\n" + "node1 Retrieve Memory1\n", + "node1 INFO INFO \"INFO\"\n" ] } ], "source": [ - "ms_codelet.proc()" + "time.sleep(1)" ] }, { @@ -531,23 +546,15 @@ "execution_count": 19, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "node1 INFO INFO \"INFO\"\n" - ] - }, { "data": { "text/plain": [ "{'name': 'Memory1',\n", - " 'evalution': '0.0',\n", + " 'evaluation': '0.0',\n", " 'I': '\"INFO\"',\n", " 'id': '0.0',\n", " 'owner': '',\n", - " 'timestamp': '1725654264.794752',\n", - " 'evaluation': '0.0'}" + " 'timestamp': '1726077370.926107'}" ] }, "execution_count": 19, @@ -567,7 +574,7 @@ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0.0, timestamp=1725654264.8138657, evaluation=0.0, I=INFO, name=Memory1]" + "MemoryObject [idmemoryobject=0.0, timestamp=1726077371.003417, evaluation=0.0, I=INFO, name=Memory1]" ] }, "execution_count": 20, @@ -585,7 +592,7 @@ "metadata": {}, "outputs": [], "source": [ - "mind2_ms_codelet.proc()" + "time.sleep(1)" ] }, { @@ -597,12 +604,11 @@ "data": { "text/plain": [ "{'name': 'Memory1',\n", - " 'evalution': '0.0',\n", + " 'evaluation': '0.0',\n", " 'I': '\"INFO\"',\n", " 'id': '0.0',\n", " 'owner': '',\n", - " 'timestamp': '1725654264.794752',\n", - " 'evaluation': '0.0'}" + " 'timestamp': '1726077370.926107'}" ] }, "execution_count": 22, @@ -634,7 +640,7 @@ ], "source": [ "mind2_memory1.set_info(\"INFO2\")\n", - "mind2_ms_codelet.proc()" + "time.sleep(1)" ] }, { @@ -646,12 +652,11 @@ "data": { "text/plain": [ "{'name': 'Memory1',\n", - " 'evalution': '0.0',\n", + " 'evaluation': '0.0',\n", " 'I': '\"INFO2\"',\n", " 'id': '0.0',\n", " 'owner': '',\n", - " 'timestamp': '1725654302.9360394',\n", - " 'evaluation': '0.0'}" + " 'timestamp': '1726077373.0085642'}" ] }, "execution_count": 24, @@ -671,7 +676,7 @@ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0.0, timestamp=1725654302.943039, evaluation=0.0, I=INFO2, name=Memory1]" + "MemoryObject [idmemoryobject=0.0, timestamp=1726077373.1104536, evaluation=0.0, I=INFO2, name=Memory1]" ] }, "execution_count": 25, @@ -683,6 +688,169 @@ "memory1" ] }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "node0 Updating memory Memory1\n", + "node0 Send memory Memory1\n", + "node0 Updating memory Memory1\n", + "node1 Updating memory Memory1\n", + "node1 Retrieve Memory1\n", + "node1 INFO 1 1\n" + ] + } + ], + "source": [ + "memory1.set_info(1)\n", + "time.sleep(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mind2_memory1.get_info()" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "node0 Updating memory Memory1\n", + "node0 Send memory Memory1\n", + "node0 Updating memory Memory1\n", + "node1 Updating memory Memory1\n", + "node1 Retrieve Memory1\n", + "node1 INFO 1 \"1\"\n" + ] + } + ], + "source": [ + "memory1.set_info(\"1\")\n", + "time.sleep(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'1'" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mind2_memory1.get_info()" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "node0 Updating memory Memory1\n", + "node0 Send memory Memory1\n", + "node1 Updating memory Memory1\n", + "node0 Updating memory Memory1\n", + "node1 Retrieve Memory1\n", + "node1 INFO True true\n" + ] + } + ], + "source": [ + "memory1.set_info(True)\n", + "time.sleep(1)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(True, bool)" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mind2_memory1.get_info(), type(mind2_memory1.get_info())" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "node0 Updating memory Memory1\n", + "node0 Send memory Memory1\n", + "node1 Updating memory Memory1\n", + "node0 Updating memory Memory1\n", + "node1 Retrieve Memory1\n", + "node1 INFO [1, 2, 3] [1, 2, 3]\n" + ] + }, + { + "data": { + "text/plain": [ + "([1, 2, 3], list)" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memory1.set_info([1,2,3])\n", + "time.sleep(1)\n", + "mind2_memory1.get_info(), type(mind2_memory1.get_info())" + ] + }, { "cell_type": "code", "execution_count": null, diff --git a/dev/weak_test.py b/dev/weak_test.py new file mode 100644 index 0000000..1669116 --- /dev/null +++ b/dev/weak_test.py @@ -0,0 +1,18 @@ +import weakref + +class Dummy: + + def __init__(self, value): + self.value = value + +weak_dict = weakref.WeakValueDictionary() + +var = Dummy(1) + +weak_dict["var"] = var + +print("var" in weak_dict) + +del var + +print("var" in weak_dict) \ No newline at end of file From 054b59a68d38464880580315970ecc936581d16d Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:07:56 -0300 Subject: [PATCH 04/48] Fix Memory/MO data types timestamp and id float->int --- src/cst_python/core/entities/memory.py | 8 ++++---- src/cst_python/core/entities/memory_object.py | 18 +++++++++--------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/cst_python/core/entities/memory.py b/src/cst_python/core/entities/memory.py index 30e0160..bbaa0a6 100644 --- a/src/cst_python/core/entities/memory.py +++ b/src/cst_python/core/entities/memory.py @@ -38,7 +38,7 @@ def set_evaluation(self, evaluation:float) -> None: #@alias.alias("getTimestamp") @abc.abstractmethod - def get_timestamp(self) -> float: + def get_timestamp(self) -> int: ... #@alias.alias("addMemoryObserver") @@ -53,17 +53,17 @@ def remove_memory_observer(self, observer:MemoryObserver) -> None: #@alias.alias("getId") @abc.abstractmethod - def get_id(self) -> float: + def get_id(self) -> int: ... #@alias.alias("setId") @abc.abstractmethod - def set_id(self, memory_id:float) -> None: + def set_id(self, memory_id:int) -> None: ... #@alias.alias("getTimestamp") @abc.abstractmethod - def get_timestamp(self) -> float: + def get_timestamp(self) -> int: ... diff --git a/src/cst_python/core/entities/memory_object.py b/src/cst_python/core/entities/memory_object.py index 9ab855d..48a8160 100644 --- a/src/cst_python/core/entities/memory_object.py +++ b/src/cst_python/core/entities/memory_object.py @@ -10,8 +10,8 @@ class MemoryObject(Memory): def __init__(self) -> None: - self._id = 0.0 - self._timestamp = 0.0 + self._id = 0 + self._timestamp = 0 self._evaluation = 0.0 self._info : Any = None self._name = "" @@ -24,10 +24,10 @@ def __getstate__(self) -> object: return state - def get_id(self) -> float: + def get_id(self) -> int: return self._id - def set_id(self, memory_id: float) -> None: + def set_id(self, memory_id: int) -> None: self._id = memory_id def get_info(self) -> Any: @@ -35,7 +35,7 @@ def get_info(self) -> Any: def set_info(self, value: Any) -> int: self._info = value - self._timestamp = time.time() + self._timestamp = int(time.time()*1000) self._notify_memory_observers() return -1 @@ -47,16 +47,16 @@ def _notify_memory_observers(self) -> None: def update_info(self, info:Any) -> None: self.set_info(info) - def get_timestamp(self) -> float: + def get_timestamp(self) -> int: return self._timestamp @property - def timestamp(self) -> float: + def timestamp(self) -> int: return self._timestamp #@alias.alias("setTimestamp") @timestamp.setter - def timestamp(self, value:float) -> None: + def timestamp(self, value:int) -> None: self._timestamp = value def get_name(self) -> str: @@ -72,7 +72,7 @@ def get_evaluation(self) -> float: return self._evaluation def set_evaluation(self, evaluation: float) -> None: - return self._evaluation + self._evaluation = evaluation #@alias.alias("toString", "to_string") def __str__(self) -> str: From 1f52cf6c44cb5d2477a3e181b49974cd9de55253 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Fri, 20 Sep 2024 16:12:55 -0300 Subject: [PATCH 05/48] MS: Fix type safety warning --- dev/memory_storage_codelet.ipynb | 201 +++++++++++++++++++++++-------- 1 file changed, 149 insertions(+), 52 deletions(-) diff --git a/dev/memory_storage_codelet.ipynb b/dev/memory_storage_codelet.ipynb index d90f888..d1dbc3f 100644 --- a/dev/memory_storage_codelet.ipynb +++ b/dev/memory_storage_codelet.ipynb @@ -43,7 +43,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -66,7 +66,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -87,25 +87,30 @@ " self._pubsub = self._client.pubsub()\n", " self._pubsub_thread : redis.client.PubSubWorkerThread = self._pubsub.run_in_thread()\n", "\n", - " if node_name is None:\n", - " node_number = self._client.scard(f\"{mind_name}:nodes\")\n", + " base_name = node_name\n", + " if base_name is None:\n", + " base_name = \"node\"\n", "\n", - " node_name = f\"node{node_number}\"\n", + " \n", + " if self._client.sismember(f\"{mind_name}:nodes\", node_name):\n", + " node_number = self._client.scard(f\"{mind_name}:nodes\")\n", + " node_name = base_name+str(node_number)\n", " while self._client.sismember(f\"{mind_name}:nodes\", node_name):\n", " node_number += 1\n", - " node_name = f\"node{node_number}\"\n", + " node_name = base_name+str(node_number)\n", + " \n", "\n", " self._node_name = cast(str, node_name)\n", "\n", " self._client.sadd(f\"{mind_name}:nodes\", node_name)\n", "\n", " transfer_service_addr = f\"{self._mind_name}:nodes:{node_name}:transfer_memory\"\n", - " self._pubsub.subscribe(**{transfer_service_addr:self.transfer_memory})\n", + " self._pubsub.subscribe(**{transfer_service_addr:self._handler_transfer_memory})\n", "\n", " transfer_done_addr = f\"{self._mind_name}:nodes:{node_name}:transfer_done\"\n", - " self._pubsub.subscribe(**{transfer_done_addr:self.notify_transfer})\n", + " self._pubsub.subscribe(**{transfer_done_addr:self._handler_notify_transfer})\n", "\n", - " self._last_update : dict[str, float] = {}\n", + " self._last_update : dict[str, int] = {}\n", " self._waiting_retrieve : set[str] = set()\n", " \n", " self._retrieve_executor = ThreadPoolExecutor(3)\n", @@ -141,13 +146,13 @@ " self._memories[memory_name] = memory\n", "\n", " if self._client.exists(f\"{self._mind_name}:memories:{memory_name}\"):\n", - " self._retrieve_executor.submit(self.retrieve_memory, memory)\n", + " self._retrieve_executor.submit(self._retrieve_memory, memory)\n", " \n", " else: #Send impostor with owner\n", " memory_impostor = {\"name\":memory.get_name(),\n", " \"evaluation\" : 0.0,\n", " \"I\": \"\",\n", - " \"id\" : \"0.0\",\n", + " \"id\" : 0,\n", " \"owner\": self._node_name}\n", " \n", " self._client.hset(f\"{self._mind_name}:memories:{memory_name}\", mapping=memory_impostor)\n", @@ -166,27 +171,25 @@ " if memory.get_timestamp() > self._last_update[memory_name]:\n", " self.update_memory(memory_name)\n", "\n", - " def transfer_memory(self, message) -> None:\n", - " request = json.loads(message[\"data\"])\n", - " \n", - " memory_name = request[\"memory_name\"]\n", - " requesting_node = request[\"node\"]\n", + " def update_memory(self, memory_name:str) -> None:\n", + " print(self._node_name, \"Updating memory\", memory_name)\n", "\n", - " print(self._node_name, \"Tranfering\", memory_name)\n", + " if memory_name not in self._memories:\n", + " self._pubsub.unsubscribe(f\"{self._mind_name}:memories:{memory_name}:update\")\n", "\n", - " if memory_name in self._memories:\n", - " memory = self._memories[memory_name]\n", - " else:\n", - " memory = cst.MemoryObject()\n", - " memory.set_name(memory_name)\n", + " timestamp = float(self._client.hget(f\"{self._mind_name}:memories:{memory_name}\", \"timestamp\"))\n", + " memory = self._memories[memory_name]\n", + " memory_timestamp = memory.get_timestamp()\n", " \n", - " self.send_memory(memory)\n", + " if memory_timestamp < timestamp:\n", + " self._retrieve_executor.submit(self._retrieve_memory, memory)\n", "\n", - " response_addr = f\"{self._mind_name}:nodes:{requesting_node}:transfer_done\"\n", - " self._client.publish(response_addr, memory_name)\n", + " elif memory_timestamp> timestamp:\n", + " self._send_memory(memory)\n", "\n", + " self._last_update[memory_name] = memory.get_timestamp()\n", "\n", - " def send_memory(self, memory:Memory) -> None:\n", + " def _send_memory(self, memory:Memory) -> None:\n", " memory_name = memory.get_name()\n", " print(self._node_name, \"Send memory\", memory_name)\n", " \n", @@ -200,25 +203,8 @@ "\n", " self._last_update[memory_name] = memory.get_timestamp()\n", " \n", - " def update_memory(self, memory_name:str) -> None:\n", - " print(self._node_name, \"Updating memory\", memory_name)\n", - "\n", - " if memory_name not in self._memories:\n", - " self._pubsub.unsubscribe(f\"{self._mind_name}:memories:{memory_name}:update\")\n", - "\n", - " timestamp = float(self._client.hget(f\"{self._mind_name}:memories:{memory_name}\", \"timestamp\"))\n", - " memory = self._memories[memory_name]\n", - " memory_timestamp = memory.get_timestamp()\n", - " \n", - " if memory_timestamp < timestamp:\n", - " self._retrieve_executor.submit(self.retrieve_memory, memory)\n", - "\n", - " elif memory_timestamp> timestamp:\n", - " self.send_memory(memory)\n", "\n", - " self._last_update[memory_name] = memory.get_timestamp()\n", - "\n", - " def retrieve_memory(self, memory:Memory) -> None:\n", + " def _retrieve_memory(self, memory:Memory) -> None:\n", " memory_name = memory.get_name()\n", "\n", " print(self._node_name, \"Retrieve\", memory_name)\n", @@ -232,18 +218,18 @@ " if memory_dict[\"owner\"] != \"\":\n", " event = threading.Event()\n", " self._waiting_request_events[memory_name] = event\n", - " self.request_memory(memory_name, memory_dict[\"owner\"])\n", + " self._request_memory(memory_name, memory_dict[\"owner\"])\n", "\n", " if not event.wait(timeout=self._request_timeout):\n", " print(self._node_name, \"Request failed\", memory_name)\n", " #Request failed\n", - " self.send_memory(memory)\n", + " self._send_memory(memory)\n", " return \n", " \n", " memory_dict = self._client.hgetall(f\"{self._mind_name}:memories:{memory_name}\")\n", "\n", " memory.set_evaluation(float(memory_dict[\"evaluation\"]))\n", - " memory.set_id(float(memory_dict[\"id\"]))\n", + " memory.set_id(int(memory_dict[\"id\"]))\n", "\n", " info_json = memory_dict[\"I\"]\n", " info = json.loads(info_json)\n", @@ -256,7 +242,7 @@ "\n", " self._waiting_retrieve.remove(memory_name)\n", "\n", - " def request_memory(self, memory_name:str, owner_name:str):\n", + " def _request_memory(self, memory_name:str, owner_name:str) -> None:\n", " print(self._node_name, \"Requesting\", memory_name)\n", "\n", " request_addr = f\"{self._mind_name}:nodes:{owner_name}:transfer_memory\"\n", @@ -265,21 +251,74 @@ " request = json.dumps(request_dict)\n", " self._client.publish(request_addr, request)\n", "\n", - " def notify_transfer(self, message:str) -> None:\n", + " def _handler_notify_transfer(self, message:str) -> None:\n", " memory_name = message[\"data\"]\n", " if memory_name in self._waiting_request_events:\n", " event = self._waiting_request_events[memory_name]\n", " event.set()\n", " del self._waiting_request_events[memory_name]\n", "\n", + " def _handler_transfer_memory(self, message) -> None:\n", + " request = json.loads(message[\"data\"])\n", + " \n", + " memory_name = request[\"memory_name\"]\n", + " requesting_node = request[\"node\"]\n", + "\n", + " print(self._node_name, \"Tranfering\", memory_name)\n", + "\n", + " if memory_name in self._memories:\n", + " memory = self._memories[memory_name]\n", + " else:\n", + " memory = cst.MemoryObject()\n", + " memory.set_name(memory_name)\n", + " \n", + " self._send_memory(memory)\n", + "\n", + " response_addr = f\"{self._mind_name}:nodes:{requesting_node}:transfer_done\"\n", + " self._client.publish(response_addr, memory_name)\n", + "\n", " def __del__(self) -> None:\n", " self._pubsub_thread.stop()\n", " self._retrieve_executor.shutdown(cancel_futures=True)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "```mermaid\n", + "flowchart LR\n", + "\n", + "update[Update Memory]\n", + "send[Send Memory]\n", + "retrieve[Retrieve Memory]\n", + "request[Request Memory]\n", + "handler_notify_transfer[Handler: Notify Transfer]\n", + "handler_transfer_memory[Handler: Transfer Memory]\n", + "\n", + "\n", + "update --> |\"timestamp(MS) < timestamp(local)\"| send\n", + "update --> |\"timestamp(MS) > timestamp(local)\"| retrieve\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "handler_transfer_memory --> send\n", + "\n", + "subgraph retrieveContext\n", + "retrieve --> |owner is not empty| request\n", + "\n", + "request -.->|Wait transfer event| handler_notify_transfer\n", + "\n", + "end\n", + "\n", + "```" + ] + }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -289,9 +328,18 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "node03 Retrieve Memory1\n", + "node03 INFO INFO \"INFO\"\n" + ] + } + ], "source": [ "ms_codelet = MemoryStorageCodelet(mind, \"node0\")\n", "ms_codelet.time_step = 100\n", @@ -300,6 +348,55 @@ "mind.start()" ] }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "MemoryObject [idmemoryobject=1, timestamp=1726858916840, evaluation=0.0, I=INFO, name=Memory1]" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memory1" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "node03 Updating memory Memory1\n", + "node03 Send memory Memory1\n", + "node03 Updating memory Memory1\n" + ] + } + ], + "source": [ + "memory1.set_info(1.0)" + ] + }, { "cell_type": "code", "execution_count": 7, From b5651144f8e85f383a80805c7b94f1feab7e9397 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:58:36 -0300 Subject: [PATCH 06/48] Move MS and MemoryEncoder to module --- dev/memory_storage_codelet.ipynb | 274 +----------------- src/cst_python/memory_storage/__init__.py | 1 + .../memory_storage/memory_encoder.py | 32 ++ .../memory_storage/memory_storage.py | 212 ++++++++++++++ 4 files changed, 258 insertions(+), 261 deletions(-) create mode 100644 src/cst_python/memory_storage/__init__.py create mode 100644 src/cst_python/memory_storage/memory_encoder.py create mode 100644 src/cst_python/memory_storage/memory_storage.py diff --git a/dev/memory_storage_codelet.ipynb b/dev/memory_storage_codelet.ipynb index d1dbc3f..25f6692 100644 --- a/dev/memory_storage_codelet.ipynb +++ b/dev/memory_storage_codelet.ipynb @@ -6,18 +6,12 @@ "metadata": {}, "outputs": [], "source": [ - "import json\n", - "import weakref\n", - "import json\n", - "import threading\n", "import time\n", - "from concurrent.futures import ThreadPoolExecutor\n", - "from typing import Optional, cast, List\n", "\n", "import redis\n", "\n", "import cst_python as cst\n", - "from cst_python.core.entities import Memory, Mind" + "from cst_python.memory_storage import MemoryStorageCodelet" ] }, { @@ -41,247 +35,6 @@ "client.flushall()" ] }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "class MemoryEncoder(json.JSONEncoder):\n", - " def default(self, memory:cst.core.entities.Memory):\n", - " return MemoryEncoder.to_dict(memory)\n", - " \n", - " @staticmethod\n", - " def to_dict(memory:cst.core.entities.Memory):\n", - " data = {\n", - " \"timestamp\": memory.get_timestamp(),\n", - " \"evaluation\": memory.get_evaluation(),\n", - " \"I\": memory.get_info(),\n", - " \"name\": memory.get_name(),\n", - " \"id\": memory.get_id()\n", - " }\n", - "\n", - " return data" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [], - "source": [ - "class MemoryStorageCodelet(cst.Codelet):\n", - " def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[str]=None, request_timeout:float=500e-3) -> None:\n", - " super().__init__()\n", - " \n", - " self._mind = mind\n", - " self._request_timeout = request_timeout\n", - " \n", - " if mind_name is None:\n", - " mind_name = \"default_mind\"\n", - " self._mind_name = cast(str, mind_name)\n", - " \n", - " self._memories : weakref.WeakValueDictionary[str, Memory] = weakref.WeakValueDictionary()\n", - " \n", - " self._client = redis.Redis(decode_responses=True)\n", - " self._pubsub = self._client.pubsub()\n", - " self._pubsub_thread : redis.client.PubSubWorkerThread = self._pubsub.run_in_thread()\n", - "\n", - " base_name = node_name\n", - " if base_name is None:\n", - " base_name = \"node\"\n", - "\n", - " \n", - " if self._client.sismember(f\"{mind_name}:nodes\", node_name):\n", - " node_number = self._client.scard(f\"{mind_name}:nodes\")\n", - " node_name = base_name+str(node_number)\n", - " while self._client.sismember(f\"{mind_name}:nodes\", node_name):\n", - " node_number += 1\n", - " node_name = base_name+str(node_number)\n", - " \n", - "\n", - " self._node_name = cast(str, node_name)\n", - "\n", - " self._client.sadd(f\"{mind_name}:nodes\", node_name)\n", - "\n", - " transfer_service_addr = f\"{self._mind_name}:nodes:{node_name}:transfer_memory\"\n", - " self._pubsub.subscribe(**{transfer_service_addr:self._handler_transfer_memory})\n", - "\n", - " transfer_done_addr = f\"{self._mind_name}:nodes:{node_name}:transfer_done\"\n", - " self._pubsub.subscribe(**{transfer_done_addr:self._handler_notify_transfer})\n", - "\n", - " self._last_update : dict[str, int] = {}\n", - " self._waiting_retrieve : set[str] = set()\n", - " \n", - " self._retrieve_executor = ThreadPoolExecutor(3)\n", - "\n", - " self._waiting_request_events : dict[str, threading.Event] = {}\n", - "\n", - " self._request = None\n", - "\n", - " def calculate_activation(self) -> None:\n", - " pass\n", - "\n", - " def access_memory_objects(self) -> None:\n", - " pass\n", - "\n", - " def proc(self) -> None:\n", - " \n", - " #Check new memories\n", - "\n", - " mind_memories = {}\n", - " for memory in self._mind.raw_memory.all_memories:\n", - " if memory.get_name() == \"\": #No name -> No MS\n", - " continue\n", - "\n", - " mind_memories[memory.get_name()] = memory\n", - "\n", - " mind_memories_names = set(mind_memories.keys())\n", - " memories_names = set(self._memories.keys())\n", - "\n", - " #Check only not here (memories_names not in mind should be garbage collected)\n", - " difference = mind_memories_names - memories_names\n", - " for memory_name in difference:\n", - " memory : Memory = mind_memories[memory_name]\n", - " self._memories[memory_name] = memory\n", - "\n", - " if self._client.exists(f\"{self._mind_name}:memories:{memory_name}\"):\n", - " self._retrieve_executor.submit(self._retrieve_memory, memory)\n", - " \n", - " else: #Send impostor with owner\n", - " memory_impostor = {\"name\":memory.get_name(),\n", - " \"evaluation\" : 0.0,\n", - " \"I\": \"\",\n", - " \"id\" : 0,\n", - " \"owner\": self._node_name}\n", - " \n", - " self._client.hset(f\"{self._mind_name}:memories:{memory_name}\", mapping=memory_impostor)\n", - "\n", - " subscribe_func = lambda message : self.update_memory(memory_name)\n", - " self._pubsub.subscribe(**{f\"{self._mind_name}:memories:{memory_name}:update\":subscribe_func})\n", - "\n", - " #Update memories\n", - " to_update = self._last_update.keys()\n", - " for memory_name in to_update:\n", - " if memory_name not in self._memories:\n", - " del self._last_update[memory_name]\n", - " continue\n", - "\n", - " memory = self._memories[memory_name]\n", - " if memory.get_timestamp() > self._last_update[memory_name]:\n", - " self.update_memory(memory_name)\n", - "\n", - " def update_memory(self, memory_name:str) -> None:\n", - " print(self._node_name, \"Updating memory\", memory_name)\n", - "\n", - " if memory_name not in self._memories:\n", - " self._pubsub.unsubscribe(f\"{self._mind_name}:memories:{memory_name}:update\")\n", - "\n", - " timestamp = float(self._client.hget(f\"{self._mind_name}:memories:{memory_name}\", \"timestamp\"))\n", - " memory = self._memories[memory_name]\n", - " memory_timestamp = memory.get_timestamp()\n", - " \n", - " if memory_timestamp < timestamp:\n", - " self._retrieve_executor.submit(self._retrieve_memory, memory)\n", - "\n", - " elif memory_timestamp> timestamp:\n", - " self._send_memory(memory)\n", - "\n", - " self._last_update[memory_name] = memory.get_timestamp()\n", - "\n", - " def _send_memory(self, memory:Memory) -> None:\n", - " memory_name = memory.get_name()\n", - " print(self._node_name, \"Send memory\", memory_name)\n", - " \n", - " memory_dict = MemoryEncoder.to_dict(memory)\n", - " memory_dict[\"I\"] = json.dumps(memory_dict[\"I\"])\n", - " memory_dict[\"owner\"] = \"\"\n", - "\n", - "\n", - " self._client.hset(f\"{self._mind_name}:memories:{memory_name}\", mapping=memory_dict)\n", - " self._client.publish(f\"{self._mind_name}:memories:{memory_name}:update\", \"\")\n", - "\n", - " self._last_update[memory_name] = memory.get_timestamp()\n", - " \n", - "\n", - " def _retrieve_memory(self, memory:Memory) -> None:\n", - " memory_name = memory.get_name()\n", - "\n", - " print(self._node_name, \"Retrieve\", memory_name)\n", - "\n", - " if memory_name in self._waiting_retrieve:\n", - " return\n", - " self._waiting_retrieve.add(memory_name)\n", - "\n", - " memory_dict = self._client.hgetall(f\"{self._mind_name}:memories:{memory_name}\")\n", - "\n", - " if memory_dict[\"owner\"] != \"\":\n", - " event = threading.Event()\n", - " self._waiting_request_events[memory_name] = event\n", - " self._request_memory(memory_name, memory_dict[\"owner\"])\n", - "\n", - " if not event.wait(timeout=self._request_timeout):\n", - " print(self._node_name, \"Request failed\", memory_name)\n", - " #Request failed\n", - " self._send_memory(memory)\n", - " return \n", - " \n", - " memory_dict = self._client.hgetall(f\"{self._mind_name}:memories:{memory_name}\")\n", - "\n", - " memory.set_evaluation(float(memory_dict[\"evaluation\"]))\n", - " memory.set_id(int(memory_dict[\"id\"]))\n", - "\n", - " info_json = memory_dict[\"I\"]\n", - " info = json.loads(info_json)\n", - "\n", - " print(self._node_name, \"INFO\", info, info_json)\n", - "\n", - " memory.set_info(info)\n", - "\n", - " self._last_update[memory_name] = memory.get_timestamp()\n", - "\n", - " self._waiting_retrieve.remove(memory_name)\n", - "\n", - " def _request_memory(self, memory_name:str, owner_name:str) -> None:\n", - " print(self._node_name, \"Requesting\", memory_name)\n", - "\n", - " request_addr = f\"{self._mind_name}:nodes:{owner_name}:transfer_memory\"\n", - " \n", - " request_dict = {\"memory_name\":memory_name, \"node\":self._node_name}\n", - " request = json.dumps(request_dict)\n", - " self._client.publish(request_addr, request)\n", - "\n", - " def _handler_notify_transfer(self, message:str) -> None:\n", - " memory_name = message[\"data\"]\n", - " if memory_name in self._waiting_request_events:\n", - " event = self._waiting_request_events[memory_name]\n", - " event.set()\n", - " del self._waiting_request_events[memory_name]\n", - "\n", - " def _handler_transfer_memory(self, message) -> None:\n", - " request = json.loads(message[\"data\"])\n", - " \n", - " memory_name = request[\"memory_name\"]\n", - " requesting_node = request[\"node\"]\n", - "\n", - " print(self._node_name, \"Tranfering\", memory_name)\n", - "\n", - " if memory_name in self._memories:\n", - " memory = self._memories[memory_name]\n", - " else:\n", - " memory = cst.MemoryObject()\n", - " memory.set_name(memory_name)\n", - " \n", - " self._send_memory(memory)\n", - "\n", - " response_addr = f\"{self._mind_name}:nodes:{requesting_node}:transfer_done\"\n", - " self._client.publish(response_addr, memory_name)\n", - "\n", - " def __del__(self) -> None:\n", - " self._pubsub_thread.stop()\n", - " self._retrieve_executor.shutdown(cancel_futures=True)" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -318,7 +71,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -328,15 +81,14 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "node03 Retrieve Memory1\n", - "node03 INFO INFO \"INFO\"\n" + "node03 Retrieve Memory1\n" ] } ], @@ -350,16 +102,16 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=1, timestamp=1726858916840, evaluation=0.0, I=INFO, name=Memory1]" + "MemoryObject [idmemoryobject=1, timestamp=1727456263799, evaluation=0.0, I=[1, 1, 1], name=Memory1]" ] }, - "execution_count": 6, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -370,7 +122,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -379,7 +131,7 @@ "-1" ] }, - "execution_count": 9, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" }, @@ -387,14 +139,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "node03 Updating memory Memory1\n", - "node03 Send memory Memory1\n", - "node03 Updating memory Memory1\n" + "node02 Updating memory Memory1\n", + "node02 Send memory Memory1\n", + "node02 Updating memory Memory1\n" ] } ], "source": [ - "memory1.set_info(1.0)" + "memory1.set_info([1,1,1])" ] }, { diff --git a/src/cst_python/memory_storage/__init__.py b/src/cst_python/memory_storage/__init__.py new file mode 100644 index 0000000..5abd58d --- /dev/null +++ b/src/cst_python/memory_storage/__init__.py @@ -0,0 +1 @@ +from .memory_storage import MemoryStorageCodelet \ No newline at end of file diff --git a/src/cst_python/memory_storage/memory_encoder.py b/src/cst_python/memory_storage/memory_encoder.py new file mode 100644 index 0000000..7c5a9e9 --- /dev/null +++ b/src/cst_python/memory_storage/memory_encoder.py @@ -0,0 +1,32 @@ +import json +from typing import Any + +from cst_python.core.entities import Memory + +class MemoryEncoder(json.JSONEncoder): + def default(self, memory:Memory): + return MemoryEncoder.to_dict(memory) + + @staticmethod + def to_dict(memory:Memory, jsonify_info:bool=False): + data = { + "timestamp": memory.get_timestamp(), + "evaluation": memory.get_evaluation(), + "I": memory.get_info(), + "name": memory.get_name(), + "id": memory.get_id() + } + + if jsonify_info: + data["I"] = json.dumps(data["I"]) + + return data + + def load_memory(memory:Memory, memory_dict:dict[str,Any], load_json:bool=True): + memory.set_evaluation(float(memory_dict["evaluation"])) + memory.set_id(int(memory_dict["id"])) + + info_json = memory_dict["I"] + info = json.loads(info_json) + + memory.set_info(info) diff --git a/src/cst_python/memory_storage/memory_storage.py b/src/cst_python/memory_storage/memory_storage.py new file mode 100644 index 0000000..7abb474 --- /dev/null +++ b/src/cst_python/memory_storage/memory_storage.py @@ -0,0 +1,212 @@ +import json +import weakref +import json +import threading +from concurrent.futures import ThreadPoolExecutor +from typing import Optional, cast + +import redis + +from cst_python.core.entities import Codelet, Mind, Memory, MemoryObject +from .memory_encoder import MemoryEncoder + +class MemoryStorageCodelet(Codelet): + def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[str]=None, request_timeout:float=500e-3) -> None: + super().__init__() + + self._mind = mind + self._request_timeout = request_timeout + + if mind_name is None: + mind_name = "default_mind" + self._mind_name = cast(str, mind_name) + + self._memories : weakref.WeakValueDictionary[str, Memory] = weakref.WeakValueDictionary() + + self._client = redis.Redis(decode_responses=True) + self._pubsub = self._client.pubsub() + self._pubsub_thread : redis.client.PubSubWorkerThread = self._pubsub.run_in_thread() + + base_name = node_name + if base_name is None: + base_name = "node" + + if self._client.sismember(f"{mind_name}:nodes", node_name): + node_number = self._client.scard(f"{mind_name}:nodes") + node_name = base_name+str(node_number) + while self._client.sismember(f"{mind_name}:nodes", node_name): + node_number += 1 + node_name = base_name+str(node_number) + + + self._node_name = cast(str, node_name) + + self._client.sadd(f"{mind_name}:nodes", node_name) + + transfer_service_addr = f"{self._mind_name}:nodes:{node_name}:transfer_memory" + self._pubsub.subscribe(**{transfer_service_addr:self._handler_transfer_memory}) + + transfer_done_addr = f"{self._mind_name}:nodes:{node_name}:transfer_done" + self._pubsub.subscribe(**{transfer_done_addr:self._handler_notify_transfer}) + + self._last_update : dict[str, int] = {} + self._waiting_retrieve : set[str] = set() + + self._retrieve_executor = ThreadPoolExecutor(3) + + self._waiting_request_events : dict[str, threading.Event] = {} + + self._request = None + + def calculate_activation(self) -> None: + pass + + def access_memory_objects(self) -> None: + pass + + def proc(self) -> None: + + #Check new memories + + mind_memories = {} + for memory in self._mind.raw_memory.all_memories: + if memory.get_name() == "": #No name -> No MS + continue + + mind_memories[memory.get_name()] = memory + + mind_memories_names = set(mind_memories.keys()) + memories_names = set(self._memories.keys()) + + #Check only not here (memories_names not in mind should be garbage collected) + difference = mind_memories_names - memories_names + for memory_name in difference: + memory : Memory = mind_memories[memory_name] + self._memories[memory_name] = memory + + if self._client.exists(f"{self._mind_name}:memories:{memory_name}"): + self._retrieve_executor.submit(self._retrieve_memory, memory) + + else: #Send impostor with owner + memory_impostor = {"name":memory.get_name(), + "evaluation" : 0.0, + "I": "", + "id" : 0, + "owner": self._node_name} + + self._client.hset(f"{self._mind_name}:memories:{memory_name}", mapping=memory_impostor) + + subscribe_func = lambda message : self.update_memory(memory_name) + self._pubsub.subscribe(**{f"{self._mind_name}:memories:{memory_name}:update":subscribe_func}) + + #Update memories + to_update = self._last_update.keys() + for memory_name in to_update: + if memory_name not in self._memories: + del self._last_update[memory_name] + continue + + memory = self._memories[memory_name] + if memory.get_timestamp() > self._last_update[memory_name]: + self.update_memory(memory_name) + + def update_memory(self, memory_name:str) -> None: + print(self._node_name, "Updating memory", memory_name) + + if memory_name not in self._memories: + self._pubsub.unsubscribe(f"{self._mind_name}:memories:{memory_name}:update") + + timestamp = float(self._client.hget(f"{self._mind_name}:memories:{memory_name}", "timestamp")) + memory = self._memories[memory_name] + memory_timestamp = memory.get_timestamp() + + if memory_timestamp < timestamp: + self._retrieve_executor.submit(self._retrieve_memory, memory) + + elif memory_timestamp> timestamp: + self._send_memory(memory) + + self._last_update[memory_name] = memory.get_timestamp() + + def _send_memory(self, memory:Memory) -> None: + memory_name = memory.get_name() + print(self._node_name, "Send memory", memory_name) + + memory_dict = MemoryEncoder.to_dict(memory, jsonify_info=True) + memory_dict["owner"] = "" + + + self._client.hset(f"{self._mind_name}:memories:{memory_name}", mapping=memory_dict) + self._client.publish(f"{self._mind_name}:memories:{memory_name}:update", "") + + self._last_update[memory_name] = memory.get_timestamp() + + + def _retrieve_memory(self, memory:Memory) -> None: + memory_name = memory.get_name() + + print(self._node_name, "Retrieve", memory_name) + + if memory_name in self._waiting_retrieve: + return + self._waiting_retrieve.add(memory_name) + + memory_dict = self._client.hgetall(f"{self._mind_name}:memories:{memory_name}") + + if memory_dict["owner"] != "": + event = threading.Event() + self._waiting_request_events[memory_name] = event + self._request_memory(memory_name, memory_dict["owner"]) + + if not event.wait(timeout=self._request_timeout): + print(self._node_name, "Request failed", memory_name) + #Request failed + self._send_memory(memory) + return + + memory_dict = self._client.hgetall(f"{self._mind_name}:memories:{memory_name}") + + MemoryEncoder.load_memory(memory, memory_dict) + + self._last_update[memory_name] = memory.get_timestamp() + + self._waiting_retrieve.remove(memory_name) + + def _request_memory(self, memory_name:str, owner_name:str) -> None: + print(self._node_name, "Requesting", memory_name) + + request_addr = f"{self._mind_name}:nodes:{owner_name}:transfer_memory" + + request_dict = {"memory_name":memory_name, "node":self._node_name} + request = json.dumps(request_dict) + self._client.publish(request_addr, request) + + def _handler_notify_transfer(self, message:str) -> None: + memory_name = message["data"] + if memory_name in self._waiting_request_events: + event = self._waiting_request_events[memory_name] + event.set() + del self._waiting_request_events[memory_name] + + def _handler_transfer_memory(self, message) -> None: + request = json.loads(message["data"]) + + memory_name = request["memory_name"] + requesting_node = request["node"] + + print(self._node_name, "Tranfering", memory_name) + + if memory_name in self._memories: + memory = self._memories[memory_name] + else: + memory = MemoryObject() + memory.set_name(memory_name) + + self._send_memory(memory) + + response_addr = f"{self._mind_name}:nodes:{requesting_node}:transfer_done" + self._client.publish(response_addr, memory_name) + + def __del__(self) -> None: + self._pubsub_thread.stop() + self._retrieve_executor.shutdown(cancel_futures=True) \ No newline at end of file From 25868a51d0bc2e3904553de502034ece430e8135 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Tue, 26 Nov 2024 19:29:36 -0300 Subject: [PATCH 07/48] Codelet: print exception in proc --- src/cst_python/core/entities/codelet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cst_python/core/entities/codelet.py b/src/cst_python/core/entities/codelet.py index eed248e..3139ecf 100644 --- a/src/cst_python/core/entities/codelet.py +++ b/src/cst_python/core/entities/codelet.py @@ -694,8 +694,8 @@ def notify_codelet(self) -> None: self._raise_exception() except Exception as e: + traceback.print_exception(e) #TODO Logging - pass finally: if self._codelet_profiler is not None: From da47ccbe81dcd345d47a29cfc9f25f78c62698b4 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Tue, 26 Nov 2024 19:30:08 -0300 Subject: [PATCH 08/48] Gym experiment --- dev/Gym codelet.ipynb | 548 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 548 insertions(+) create mode 100644 dev/Gym codelet.ipynb diff --git a/dev/Gym codelet.ipynb b/dev/Gym codelet.ipynb new file mode 100644 index 0000000..8ec8e5e --- /dev/null +++ b/dev/Gym codelet.ipynb @@ -0,0 +1,548 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Optional, Any\n", + "\n", + "import gymnasium as gym\n", + "from gymnasium.wrappers import TransformAction, TransformObservation\n", + "\n", + "import cst_python as cst\n", + "from cst_python.core.entities import Memory, MemoryObject" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "class GymCodelet(cst.Codelet):\n", + " _last_indexes = {\"reward\":-1, \"reset\":-1, \"terminated\":-1, \"truncated\":-1, \"info\":-1, \"seed\":-1}\n", + "\n", + " def __init__(self, mind:cst.Mind, env:gym.Env):\n", + " super().__init__()\n", + " \n", + " self.env = env\n", + " \n", + " self.observation_memories = self.space_to_memories(mind, env.observation_space)\n", + " self.action_memories = self.space_to_memories(mind, env.action_space, action=True)\n", + "\n", + " self._common_memories : dict[str, MemoryObject] = {}\n", + " for name in [\"reward\", \"reset\", \"terminated\", \"truncated\", \"info\", \"seed\"]:\n", + " self._last_indexes[name] += 1\n", + "\n", + " memory_name = name\n", + " if self._last_indexes[name] != 0:\n", + " memory_name += str(self._last_indexes[name])\n", + " \n", + " self._common_memories[name] = mind.create_memory_object(memory_name)\n", + "\n", + " self._common_memories[\"reward\"].set_info(0.0)\n", + " self._common_memories[\"reset\"].set_info(False)\n", + " self._common_memories[\"terminated\"].set_info(False)\n", + " self._common_memories[\"truncated\"].set_info(False)\n", + " self._common_memories[\"info\"].set_info({})\n", + " self._common_memories[\"seed\"].set_info(None)\n", + "\n", + "\n", + " self.is_memory_observer = True\n", + " for memory_name in self.action_memories:\n", + " memory = self.action_memories[memory_name]\n", + " memory.add_memory_observer(self)\n", + " self._common_memories[\"reset\"].add_memory_observer(self)\n", + "\n", + " self._last_reset = self._common_memories[\"reset\"].get_timestamp()\n", + "\n", + " @property\n", + " def reward_memory(self) -> MemoryObject:\n", + " return self._common_memories[\"reward\"]\n", + " \n", + " @property\n", + " def reset_memory(self) -> MemoryObject:\n", + " return self._common_memories[\"reset\"]\n", + " \n", + " @property\n", + " def terminated_memory(self) -> MemoryObject:\n", + " return self._common_memories[\"terminated\"]\n", + " \n", + " @property\n", + " def truncated_memory(self) -> MemoryObject:\n", + " return self._common_memories[\"truncated\"]\n", + " \n", + " @property\n", + " def info_memory(self) -> MemoryObject:\n", + " return self._common_memories[\"info\"]\n", + " \n", + " @property\n", + " def seed_memory(self) -> MemoryObject:\n", + " return self._common_memories[\"seed\"]\n", + "\n", + " def access_memory_objects(self):\n", + " pass\n", + "\n", + " def calculate_activation(self):\n", + " pass\n", + "\n", + " def proc(self):\n", + " if self._last_reset < self.reset_memory.get_timestamp():\n", + " self._last_reset = self.reset_memory.get_timestamp()\n", + "\n", + " observation, info = self.env.reset(seed=self.seed_memory.get_info())\n", + " reward = 0\n", + " terminated = False\n", + " truncated = False\n", + "\n", + " else:\n", + " action = self.memories_to_space(self.action_memories, self.env.action_space)\n", + " observation, reward, terminated, truncated, info = self.env.step(action)\n", + "\n", + " print(\"Observation\", observation)\n", + " \n", + " self.reward_memory.set_info(reward)\n", + " self.terminated_memory.set_info(terminated)\n", + " self.truncated_memory.set_info(truncated)\n", + " self.info_memory.set_info(info)\n", + "\n", + " self.sample_to_memories(observation, self.observation_memories)\n", + "\n", + " @classmethod\n", + " def space_to_memories(cls, mind:cst.Mind, \n", + " space:gym.Space,\n", + " action:bool=False) -> dict[str, cst.MemoryObject]:\n", + " memories = {}\n", + "\n", + " if isinstance(space, gym.spaces.Dict):\n", + " for space_name in space:\n", + " subspace = space[space_name]\n", + "\n", + " name = space_name\n", + " if space_name in cls._last_indexes:\n", + " cls._last_indexes[space_name] += 1\n", + " name += str(cls._last_indexes[space_name])\n", + " else:\n", + " cls._last_indexes[space_name] = 0\n", + "\n", + " info = subspace.sample()\n", + " memory = mind.create_memory_object(name, info)\n", + " memories[space_name] = memory\n", + " \n", + " else:\n", + " if action:\n", + " space_name = \"action\"\n", + " else:\n", + " space_name = \"observation\"\n", + "\n", + " name = space_name\n", + " if space_name in cls._last_indexes:\n", + " cls._last_indexes[space_name] += 1\n", + " name += str(cls._last_indexes[space_name])\n", + " else:\n", + " cls._last_indexes[space_name] = 0\n", + "\n", + " info = space.sample()\n", + " memory = mind.create_memory_object(name, info)\n", + " memories[space_name] = memory\n", + " \n", + "\n", + " return memories\n", + " \n", + " @classmethod\n", + " def sample_to_memories(cls, sample:dict[str, Any]|Any, memories:dict[str, Memory]) -> None:\n", + " if isinstance(sample, dict):\n", + " for name in sample:\n", + " element = sample[name]\n", + " memory = memories[name]\n", + " \n", + " memory.set_info(element)\n", + " else:\n", + " memory = memories[next(iter(memories))]\n", + " memory.set_info(sample)\n", + " \n", + "\n", + " @classmethod\n", + " def memories_to_space(cls, memories:dict[str, Memory], space:gym.spaces.Dict) -> dict[str, Any]|Any:\n", + " if isinstance(space, gym.spaces.Dict):\n", + " sample = {}\n", + " for memory_name in memories:\n", + " sample[memory_name] = memories[memory_name].get_info()\n", + " else:\n", + " sample = memories[next(iter(memories))].get_info()\n", + "\n", + " if not space.contains(sample):\n", + " raise ValueError(\"Memories do not correspond to an element of the Space.\")\n", + " \n", + " return sample" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [], + "source": [ + "env = gym.make(\"Blackjack-v1\")\n", + "\n", + "env = TransformObservation(env, \n", + " lambda obs:{\"player_sum\":obs[0], \"dealer_card\":obs[1], \"usable_ace\":obs[2]}, \n", + " gym.spaces.Dict({\"player_sum\":env.observation_space[0], \"dealer_card\":env.observation_space[1], \"usable_ace\":env.observation_space[2]}))\n", + "\n", + "env = TransformAction(env, \n", + " lambda action:action[\"hit\"], \n", + " gym.spaces.Dict({\"hit\":env.action_space}))\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "GymCodelet execution\n" + ] + }, + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mind = cst.Mind()\n", + "gym_codelet = GymCodelet(mind, env)\n", + "mind.insert_codelet(gym_codelet)\n", + "\n", + "mind.start()\n", + "gym_codelet.seed_memory.set_info(42)\n", + "gym_codelet.reset_memory.set_info(not gym_codelet.reset_memory.get_info())" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "({'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732659816658, evaluation=0.0, I=2, name=dealer_card],\n", + " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732659816658, evaluation=0.0, I=15, name=player_sum],\n", + " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732659816658, evaluation=0.0, I=0, name=usable_ace]},\n", + " MemoryObject [idmemoryobject=6, timestamp=1732659816658, evaluation=0.0, I=False, name=terminated],\n", + " MemoryObject [idmemoryobject=4, timestamp=1732659816658, evaluation=0.0, I=0, name=reward])" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.observation_memories, gym_codelet.terminated_memory, gym_codelet.reward_memory" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Observation {'player_sum': 25, 'dealer_card': 2, 'usable_ace': 0}\n", + "GymCodelet execution\n" + ] + }, + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 61, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.action_memories[\"hit\"].set_info(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "({'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732659816687, evaluation=0.0, I=2, name=dealer_card],\n", + " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732659816687, evaluation=0.0, I=25, name=player_sum],\n", + " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732659816687, evaluation=0.0, I=0, name=usable_ace]},\n", + " MemoryObject [idmemoryobject=6, timestamp=1732659816687, evaluation=0.0, I=True, name=terminated],\n", + " MemoryObject [idmemoryobject=4, timestamp=1732659816687, evaluation=0.0, I=-1.0, name=reward])" + ] + }, + "execution_count": 62, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.observation_memories, gym_codelet.terminated_memory, gym_codelet.reward_memory" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "GymCodelet execution\n" + ] + }, + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 63, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.reset_memory.set_info(True)" + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "({'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732659816736, evaluation=0.0, I=2, name=dealer_card],\n", + " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732659816736, evaluation=0.0, I=15, name=player_sum],\n", + " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732659816736, evaluation=0.0, I=0, name=usable_ace]},\n", + " MemoryObject [idmemoryobject=6, timestamp=1732659816736, evaluation=0.0, I=False, name=terminated],\n", + " MemoryObject [idmemoryobject=4, timestamp=1732659816736, evaluation=0.0, I=0, name=reward])" + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.observation_memories, gym_codelet.terminated_memory, gym_codelet.reward_memory" + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Observation {'player_sum': 15, 'dealer_card': 2, 'usable_ace': 0}\n", + "GymCodelet execution\n" + ] + }, + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.action_memories[\"hit\"].set_info(0)" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "({'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732659816814, evaluation=0.0, I=2, name=dealer_card],\n", + " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732659816814, evaluation=0.0, I=15, name=player_sum],\n", + " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732659816814, evaluation=0.0, I=0, name=usable_ace]},\n", + " MemoryObject [idmemoryobject=6, timestamp=1732659816814, evaluation=0.0, I=True, name=terminated],\n", + " MemoryObject [idmemoryobject=4, timestamp=1732659816814, evaluation=0.0, I=1.0, name=reward])" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.observation_memories, gym_codelet.terminated_memory, gym_codelet.reward_memory" + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "GymCodelet execution\n" + ] + }, + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "env = gym.make(\"Blackjack-v1\")\n", + "mind = cst.Mind()\n", + "\n", + "gym_codelet = GymCodelet(mind, env)\n", + "mind.insert_codelet(gym_codelet)\n", + "\n", + "mind.start()\n", + "gym_codelet.seed_memory.set_info(42)\n", + "gym_codelet.reset_memory.set_info(not gym_codelet.reset_memory.get_info())" + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "({'observation': MemoryObject [idmemoryobject=0, timestamp=1732659816913, evaluation=0.0, I=(15, 2, 0), name=observation]},\n", + " MemoryObject [idmemoryobject=4, timestamp=1732659816913, evaluation=0.0, I=False, name=terminated1],\n", + " MemoryObject [idmemoryobject=2, timestamp=1732659816913, evaluation=0.0, I=0, name=reward1])" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.observation_memories, gym_codelet.terminated_memory, gym_codelet.reward_memory" + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Observation (25, 2, 0)\n", + "GymCodelet execution\n" + ] + }, + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.action_memories[\"action\"].set_info(1)" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "({'observation': MemoryObject [idmemoryobject=0, timestamp=1732659816947, evaluation=0.0, I=(25, 2, 0), name=observation]},\n", + " MemoryObject [idmemoryobject=4, timestamp=1732659816947, evaluation=0.0, I=True, name=terminated1],\n", + " MemoryObject [idmemoryobject=2, timestamp=1732659816947, evaluation=0.0, I=-1.0, name=reward1])" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.observation_memories, gym_codelet.terminated_memory, gym_codelet.reward_memory" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} From 6826ea4bd5e561d587fa3cea565e151a3800adbc Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:59:41 -0300 Subject: [PATCH 09/48] GymCodelet integrated --- dev/Gym codelet.ipynb | 300 +++++------------------ src/cst_python/python/gym/__init__.py | 1 + src/cst_python/python/gym/gym_codelet.py | 255 +++++++++++++++++++ 3 files changed, 312 insertions(+), 244 deletions(-) create mode 100644 src/cst_python/python/gym/__init__.py create mode 100644 src/cst_python/python/gym/gym_codelet.py diff --git a/dev/Gym codelet.ipynb b/dev/Gym codelet.ipynb index 8ec8e5e..aab533e 100644 --- a/dev/Gym codelet.ipynb +++ b/dev/Gym codelet.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 56, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -12,176 +12,12 @@ "from gymnasium.wrappers import TransformAction, TransformObservation\n", "\n", "import cst_python as cst\n", - "from cst_python.core.entities import Memory, MemoryObject" + "from cst_python.python.gym import GymCodelet" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "class GymCodelet(cst.Codelet):\n", - " _last_indexes = {\"reward\":-1, \"reset\":-1, \"terminated\":-1, \"truncated\":-1, \"info\":-1, \"seed\":-1}\n", - "\n", - " def __init__(self, mind:cst.Mind, env:gym.Env):\n", - " super().__init__()\n", - " \n", - " self.env = env\n", - " \n", - " self.observation_memories = self.space_to_memories(mind, env.observation_space)\n", - " self.action_memories = self.space_to_memories(mind, env.action_space, action=True)\n", - "\n", - " self._common_memories : dict[str, MemoryObject] = {}\n", - " for name in [\"reward\", \"reset\", \"terminated\", \"truncated\", \"info\", \"seed\"]:\n", - " self._last_indexes[name] += 1\n", - "\n", - " memory_name = name\n", - " if self._last_indexes[name] != 0:\n", - " memory_name += str(self._last_indexes[name])\n", - " \n", - " self._common_memories[name] = mind.create_memory_object(memory_name)\n", - "\n", - " self._common_memories[\"reward\"].set_info(0.0)\n", - " self._common_memories[\"reset\"].set_info(False)\n", - " self._common_memories[\"terminated\"].set_info(False)\n", - " self._common_memories[\"truncated\"].set_info(False)\n", - " self._common_memories[\"info\"].set_info({})\n", - " self._common_memories[\"seed\"].set_info(None)\n", - "\n", - "\n", - " self.is_memory_observer = True\n", - " for memory_name in self.action_memories:\n", - " memory = self.action_memories[memory_name]\n", - " memory.add_memory_observer(self)\n", - " self._common_memories[\"reset\"].add_memory_observer(self)\n", - "\n", - " self._last_reset = self._common_memories[\"reset\"].get_timestamp()\n", - "\n", - " @property\n", - " def reward_memory(self) -> MemoryObject:\n", - " return self._common_memories[\"reward\"]\n", - " \n", - " @property\n", - " def reset_memory(self) -> MemoryObject:\n", - " return self._common_memories[\"reset\"]\n", - " \n", - " @property\n", - " def terminated_memory(self) -> MemoryObject:\n", - " return self._common_memories[\"terminated\"]\n", - " \n", - " @property\n", - " def truncated_memory(self) -> MemoryObject:\n", - " return self._common_memories[\"truncated\"]\n", - " \n", - " @property\n", - " def info_memory(self) -> MemoryObject:\n", - " return self._common_memories[\"info\"]\n", - " \n", - " @property\n", - " def seed_memory(self) -> MemoryObject:\n", - " return self._common_memories[\"seed\"]\n", - "\n", - " def access_memory_objects(self):\n", - " pass\n", - "\n", - " def calculate_activation(self):\n", - " pass\n", - "\n", - " def proc(self):\n", - " if self._last_reset < self.reset_memory.get_timestamp():\n", - " self._last_reset = self.reset_memory.get_timestamp()\n", - "\n", - " observation, info = self.env.reset(seed=self.seed_memory.get_info())\n", - " reward = 0\n", - " terminated = False\n", - " truncated = False\n", - "\n", - " else:\n", - " action = self.memories_to_space(self.action_memories, self.env.action_space)\n", - " observation, reward, terminated, truncated, info = self.env.step(action)\n", - "\n", - " print(\"Observation\", observation)\n", - " \n", - " self.reward_memory.set_info(reward)\n", - " self.terminated_memory.set_info(terminated)\n", - " self.truncated_memory.set_info(truncated)\n", - " self.info_memory.set_info(info)\n", - "\n", - " self.sample_to_memories(observation, self.observation_memories)\n", - "\n", - " @classmethod\n", - " def space_to_memories(cls, mind:cst.Mind, \n", - " space:gym.Space,\n", - " action:bool=False) -> dict[str, cst.MemoryObject]:\n", - " memories = {}\n", - "\n", - " if isinstance(space, gym.spaces.Dict):\n", - " for space_name in space:\n", - " subspace = space[space_name]\n", - "\n", - " name = space_name\n", - " if space_name in cls._last_indexes:\n", - " cls._last_indexes[space_name] += 1\n", - " name += str(cls._last_indexes[space_name])\n", - " else:\n", - " cls._last_indexes[space_name] = 0\n", - "\n", - " info = subspace.sample()\n", - " memory = mind.create_memory_object(name, info)\n", - " memories[space_name] = memory\n", - " \n", - " else:\n", - " if action:\n", - " space_name = \"action\"\n", - " else:\n", - " space_name = \"observation\"\n", - "\n", - " name = space_name\n", - " if space_name in cls._last_indexes:\n", - " cls._last_indexes[space_name] += 1\n", - " name += str(cls._last_indexes[space_name])\n", - " else:\n", - " cls._last_indexes[space_name] = 0\n", - "\n", - " info = space.sample()\n", - " memory = mind.create_memory_object(name, info)\n", - " memories[space_name] = memory\n", - " \n", - "\n", - " return memories\n", - " \n", - " @classmethod\n", - " def sample_to_memories(cls, sample:dict[str, Any]|Any, memories:dict[str, Memory]) -> None:\n", - " if isinstance(sample, dict):\n", - " for name in sample:\n", - " element = sample[name]\n", - " memory = memories[name]\n", - " \n", - " memory.set_info(element)\n", - " else:\n", - " memory = memories[next(iter(memories))]\n", - " memory.set_info(sample)\n", - " \n", - "\n", - " @classmethod\n", - " def memories_to_space(cls, memories:dict[str, Memory], space:gym.spaces.Dict) -> dict[str, Any]|Any:\n", - " if isinstance(space, gym.spaces.Dict):\n", - " sample = {}\n", - " for memory_name in memories:\n", - " sample[memory_name] = memories[memory_name].get_info()\n", - " else:\n", - " sample = memories[next(iter(memories))].get_info()\n", - "\n", - " if not space.contains(sample):\n", - " raise ValueError(\"Memories do not correspond to an element of the Space.\")\n", - " \n", - " return sample" - ] - }, - { - "cell_type": "code", - "execution_count": 58, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -199,23 +35,16 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 4, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GymCodelet execution\n" - ] - }, { "data": { "text/plain": [ "-1" ] }, - "execution_count": 59, + "execution_count": 4, "metadata": {}, "output_type": "execute_result" } @@ -232,20 +61,20 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "({'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732659816658, evaluation=0.0, I=2, name=dealer_card],\n", - " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732659816658, evaluation=0.0, I=15, name=player_sum],\n", - " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732659816658, evaluation=0.0, I=0, name=usable_ace]},\n", - " MemoryObject [idmemoryobject=6, timestamp=1732659816658, evaluation=0.0, I=False, name=terminated],\n", - " MemoryObject [idmemoryobject=4, timestamp=1732659816658, evaluation=0.0, I=0, name=reward])" + "({'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732724413462, evaluation=0.0, I=2, name=dealer_card],\n", + " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732724413462, evaluation=0.0, I=15, name=player_sum],\n", + " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732724413462, evaluation=0.0, I=0, name=usable_ace]},\n", + " MemoryObject [idmemoryobject=6, timestamp=1732724413462, evaluation=0.0, I=False, name=terminated],\n", + " MemoryObject [idmemoryobject=4, timestamp=1732724413462, evaluation=0.0, I=0, name=reward])" ] }, - "execution_count": 60, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -256,15 +85,14 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Observation {'player_sum': 25, 'dealer_card': 2, 'usable_ace': 0}\n", - "GymCodelet execution\n" + "Observation {'player_sum': 25, 'dealer_card': 2, 'usable_ace': 0}\n" ] }, { @@ -273,7 +101,7 @@ "-1" ] }, - "execution_count": 61, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -284,20 +112,20 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "({'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732659816687, evaluation=0.0, I=2, name=dealer_card],\n", - " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732659816687, evaluation=0.0, I=25, name=player_sum],\n", - " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732659816687, evaluation=0.0, I=0, name=usable_ace]},\n", - " MemoryObject [idmemoryobject=6, timestamp=1732659816687, evaluation=0.0, I=True, name=terminated],\n", - " MemoryObject [idmemoryobject=4, timestamp=1732659816687, evaluation=0.0, I=-1.0, name=reward])" + "({'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732724413492, evaluation=0.0, I=2, name=dealer_card],\n", + " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732724413492, evaluation=0.0, I=25, name=player_sum],\n", + " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732724413492, evaluation=0.0, I=0, name=usable_ace]},\n", + " MemoryObject [idmemoryobject=6, timestamp=1732724413492, evaluation=0.0, I=True, name=terminated],\n", + " MemoryObject [idmemoryobject=4, timestamp=1732724413492, evaluation=0.0, I=-1.0, name=reward])" ] }, - "execution_count": 62, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -308,23 +136,16 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 8, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GymCodelet execution\n" - ] - }, { "data": { "text/plain": [ "-1" ] }, - "execution_count": 63, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -335,20 +156,20 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "({'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732659816736, evaluation=0.0, I=2, name=dealer_card],\n", - " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732659816736, evaluation=0.0, I=15, name=player_sum],\n", - " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732659816736, evaluation=0.0, I=0, name=usable_ace]},\n", - " MemoryObject [idmemoryobject=6, timestamp=1732659816736, evaluation=0.0, I=False, name=terminated],\n", - " MemoryObject [idmemoryobject=4, timestamp=1732659816736, evaluation=0.0, I=0, name=reward])" + "({'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732724413554, evaluation=0.0, I=2, name=dealer_card],\n", + " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732724413554, evaluation=0.0, I=15, name=player_sum],\n", + " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732724413554, evaluation=0.0, I=0, name=usable_ace]},\n", + " MemoryObject [idmemoryobject=6, timestamp=1732724413554, evaluation=0.0, I=False, name=terminated],\n", + " MemoryObject [idmemoryobject=4, timestamp=1732724413554, evaluation=0.0, I=0, name=reward])" ] }, - "execution_count": 64, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -359,15 +180,14 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Observation {'player_sum': 15, 'dealer_card': 2, 'usable_ace': 0}\n", - "GymCodelet execution\n" + "Observation {'player_sum': 15, 'dealer_card': 2, 'usable_ace': 0}\n" ] }, { @@ -376,7 +196,7 @@ "-1" ] }, - "execution_count": 65, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -387,20 +207,20 @@ }, { "cell_type": "code", - "execution_count": 66, + "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "({'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732659816814, evaluation=0.0, I=2, name=dealer_card],\n", - " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732659816814, evaluation=0.0, I=15, name=player_sum],\n", - " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732659816814, evaluation=0.0, I=0, name=usable_ace]},\n", - " MemoryObject [idmemoryobject=6, timestamp=1732659816814, evaluation=0.0, I=True, name=terminated],\n", - " MemoryObject [idmemoryobject=4, timestamp=1732659816814, evaluation=0.0, I=1.0, name=reward])" + "({'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732724413580, evaluation=0.0, I=2, name=dealer_card],\n", + " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732724413580, evaluation=0.0, I=15, name=player_sum],\n", + " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732724413580, evaluation=0.0, I=0, name=usable_ace]},\n", + " MemoryObject [idmemoryobject=6, timestamp=1732724413580, evaluation=0.0, I=True, name=terminated],\n", + " MemoryObject [idmemoryobject=4, timestamp=1732724413580, evaluation=0.0, I=1.0, name=reward])" ] }, - "execution_count": 66, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -411,23 +231,16 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 12, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "GymCodelet execution\n" - ] - }, { "data": { "text/plain": [ "-1" ] }, - "execution_count": 67, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -446,18 +259,18 @@ }, { "cell_type": "code", - "execution_count": 68, + "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "({'observation': MemoryObject [idmemoryobject=0, timestamp=1732659816913, evaluation=0.0, I=(15, 2, 0), name=observation]},\n", - " MemoryObject [idmemoryobject=4, timestamp=1732659816913, evaluation=0.0, I=False, name=terminated1],\n", - " MemoryObject [idmemoryobject=2, timestamp=1732659816913, evaluation=0.0, I=0, name=reward1])" + "({'observation': MemoryObject [idmemoryobject=0, timestamp=1732724413609, evaluation=0.0, I=(15, 2, 0), name=observation]},\n", + " MemoryObject [idmemoryobject=4, timestamp=1732724413609, evaluation=0.0, I=False, name=terminated1],\n", + " MemoryObject [idmemoryobject=2, timestamp=1732724413609, evaluation=0.0, I=0, name=reward1])" ] }, - "execution_count": 68, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -468,15 +281,14 @@ }, { "cell_type": "code", - "execution_count": 69, + "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Observation (25, 2, 0)\n", - "GymCodelet execution\n" + "Observation (25, 2, 0)\n" ] }, { @@ -485,7 +297,7 @@ "-1" ] }, - "execution_count": 69, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -496,18 +308,18 @@ }, { "cell_type": "code", - "execution_count": 70, + "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "({'observation': MemoryObject [idmemoryobject=0, timestamp=1732659816947, evaluation=0.0, I=(25, 2, 0), name=observation]},\n", - " MemoryObject [idmemoryobject=4, timestamp=1732659816947, evaluation=0.0, I=True, name=terminated1],\n", - " MemoryObject [idmemoryobject=2, timestamp=1732659816947, evaluation=0.0, I=-1.0, name=reward1])" + "({'observation': MemoryObject [idmemoryobject=0, timestamp=1732724413632, evaluation=0.0, I=(25, 2, 0), name=observation]},\n", + " MemoryObject [idmemoryobject=4, timestamp=1732724413632, evaluation=0.0, I=True, name=terminated1],\n", + " MemoryObject [idmemoryobject=2, timestamp=1732724413632, evaluation=0.0, I=-1.0, name=reward1])" ] }, - "execution_count": 70, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } diff --git a/src/cst_python/python/gym/__init__.py b/src/cst_python/python/gym/__init__.py new file mode 100644 index 0000000..2f2b3d4 --- /dev/null +++ b/src/cst_python/python/gym/__init__.py @@ -0,0 +1 @@ +from .gym_codelet import GymCodelet \ No newline at end of file diff --git a/src/cst_python/python/gym/gym_codelet.py b/src/cst_python/python/gym/gym_codelet.py new file mode 100644 index 0000000..e702319 --- /dev/null +++ b/src/cst_python/python/gym/gym_codelet.py @@ -0,0 +1,255 @@ +from typing import Optional, Any, cast, Mapping + +try: + import gymnasium as gym +except ModuleNotFoundError: + import gym # type: ignore + +from cst_python.core.entities import Codelet, Mind, Memory, MemoryObject + +class GymCodelet(Codelet): + ''' + Codelet to interface with gymnasium/gym environments. Creates memories for the observation, + action, reward, reset, terminated, truncated, info and seed; and updates them stepping the + environment with the action. + ''' + + _last_indexes : dict[str, int] = {"reward":-1, "reset":-1, "terminated":-1, "truncated":-1, "info":-1, "seed":-1} + + def __init__(self, mind:Mind, env:gym.Env): + ''' + GymCodelet constructor. + + Always runs automatically in publish-subscribe mode. + + Args: + mind (Mind): agent's mind. + env (gym.Env): environment to interface. + ''' + super().__init__() + + assert mind._raw_memory is not None # RawMemory cannot be None for creating memories + + self.env = env + + self.observation_memories = self.space_to_memories(mind, env.observation_space) + self.action_memories = self.space_to_memories(mind, env.action_space, action=True) + + self._common_memories : dict[str, MemoryObject] = {} + for name in ["reward", "reset", "terminated", "truncated", "info", "seed"]: + self._last_indexes[name] += 1 + + memory_name = name + if self._last_indexes[name] != 0: + memory_name += str(self._last_indexes[name]) + + self._common_memories[name] = cast(MemoryObject, mind.create_memory_object(memory_name)) + + self._common_memories["reward"].set_info(0.0) + self._common_memories["reset"].set_info(False) + self._common_memories["terminated"].set_info(False) + self._common_memories["truncated"].set_info(False) + self._common_memories["info"].set_info({}) + self._common_memories["seed"].set_info(None) + + + self.is_memory_observer = True + for memory_name in self.action_memories: + memory = self.action_memories[memory_name] + memory.add_memory_observer(self) + self._common_memories["reset"].add_memory_observer(self) + + self._last_reset = self._common_memories["reset"].get_timestamp() + + @property + def reward_memory(self) -> MemoryObject: + ''' + Memory that contains the environment reward (float). + ''' + return self._common_memories["reward"] + + @property + def reset_memory(self) -> MemoryObject: + ''' + Memory that contains the environment reset. + + If timestamp changes, the codelet resets the environment. + ''' + return self._common_memories["reset"] + + @property + def terminated_memory(self) -> MemoryObject: + ''' + Memory that contains the environment terminated state. + ''' + return self._common_memories["terminated"] + + @property + def truncated_memory(self) -> MemoryObject: + ''' + Memory that contains the environment truncated state. + ''' + return self._common_memories["truncated"] + + @property + def info_memory(self) -> MemoryObject: + ''' + Memory that contains the environment info. + ''' + return self._common_memories["info"] + + @property + def seed_memory(self) -> MemoryObject: + ''' + Memory that contains the seed to use in the environment reset. + ''' + return self._common_memories["seed"] + + + def access_memory_objects(self) -> None: #NOSONAR + pass + + def calculate_activation(self) -> None: #NOSONAR + pass + + def proc(self) -> None: + if self._last_reset < self.reset_memory.get_timestamp(): + self._last_reset = self.reset_memory.get_timestamp() + + observation, info = self.env.reset(seed=self.seed_memory.get_info()) + reward = 0.0 + terminated = False + truncated = False + + else: + action = self.memories_to_space(self.action_memories, self.env.action_space) + observation, r, terminated, truncated, info = self.env.step(action) + reward = float(r) #SupportsFloat to float + + print("Observation", observation) + + self.reward_memory.set_info(reward) + self.terminated_memory.set_info(terminated) + self.truncated_memory.set_info(truncated) + self.info_memory.set_info(info) + + self.sample_to_memories(observation, self.observation_memories) + + @classmethod + def space_to_memories(cls, mind:Mind, + space:gym.Space, + action:bool=False, + memory_prefix:Optional[str]=None) -> dict[str, MemoryObject]: + ''' + Creates memories from a gym Space definition. + + Args: + mind (Mind): mind to create the memories. + space (gym.Space): space defining the memories to create. + If gym.space.Dict, creates a memory for each element, + creates a single memory otherwise. + action (bool, optional): If True, creates a memory with 'action' + name for non Dict space, uses 'observation' name otherwise. + Defaults to False. + memory_prefix (Optional[str], optional): prefix to memories name. + Defaults to None. + + Returns: + dict[str, MemoryObject]: created memories, indexed by the space + element name or 'action'/'observation'. + ''' + assert mind._raw_memory is not None # RawMemory cannot be None for creating memories + + if memory_prefix is None: + memory_prefix = "" + + memories : dict[str, MemoryObject] = {} + + if isinstance(space, gym.spaces.Dict): + for space_name in space: + subspace = space[space_name] + + name = space_name + if space_name in cls._last_indexes: + cls._last_indexes[space_name] += 1 + name += str(cls._last_indexes[space_name]) + else: + cls._last_indexes[space_name] = 0 + name = memory_prefix+name + + info = subspace.sample() + memory = cast(MemoryObject, mind.create_memory_object(name, info)) + memories[space_name] = memory + + else: + if action: + space_name = "action" + else: + space_name = "observation" + + name = space_name + if space_name in cls._last_indexes: + cls._last_indexes[space_name] += 1 + name += str(cls._last_indexes[space_name]) + else: + cls._last_indexes[space_name] = 0 + + name = memory_prefix+name + + info = space.sample() + memory = cast(MemoryObject, mind.create_memory_object(name, info)) + memories[space_name] = memory + + + return memories + + @classmethod + def sample_to_memories(cls, sample:Mapping[str, Any]|Any, + memories:Mapping[str, Memory]) -> None: + ''' + Writes a gym.Space sample to memories. + + Args: + sample (Mapping[str, Any] | Any): sample to write in the memories. + memories (Mapping[str, Memory]): memories corresponding to + the space elements. + ''' + if isinstance(sample, dict): + for name in sample: + element = sample[name] + memory = memories[name] + + memory.set_info(element) + else: + memory = memories[next(iter(memories))] + memory.set_info(sample) + + + @classmethod + def memories_to_space(cls, memories:Mapping[str, Memory], + space:gym.Space) -> dict[str, Any]|Any: + ''' + Convert the memories info to the space sample. + + Args: + memories (Mapping[str, Memory]): memories to get the sample. + space (gym.Space): space the sample belongs + + Raises: + ValueError: if the generated sample from the memories + doesn't belongs to the space + + Returns: + dict[str, Any]|Any: converted sample. + ''' + if isinstance(space, gym.spaces.Dict): + sample = {} + for memory_name in memories: + sample[memory_name] = memories[memory_name].get_info() + else: + sample = memories[next(iter(memories))].get_info() + + if not space.contains(sample): + raise ValueError("Memories do not correspond to an element of the Space.") + + return sample \ No newline at end of file From 13875fb1bc73517dc04cd1693be69ca914a6d790 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:59:54 -0300 Subject: [PATCH 10/48] Added "gym" dependencies --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 0f8634c..f5c97c8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,6 +30,7 @@ Homepage = "https://hiaac.unicamp.br" tests = ["mypy", "testbook", "ipython", "ipykernel", "numpy", "matplotlib"] doc_generation = ["sphinx", "sphinx_rtd_theme", "nbsphinx", "sphinx-mdinclude==0.5.4"] dev = ["cffconvert"] +gym = ["gymnasium"] [tool.setuptools] include-package-data = true From 8d9befd72a94e96845d0e94503ff369574f247c7 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:00:11 -0300 Subject: [PATCH 11/48] Gym Codelet example --- .github/workflows/test.yml | 2 +- dev/Gym codelet.ipynb | 2 +- examples/Gymnasium Integration.ipynb | 714 +++++++++++++++++++++++ src/cst_python/python/gym/gym_codelet.py | 21 +- tests/examples/test_gym_integration.py | 45 ++ 5 files changed, 778 insertions(+), 6 deletions(-) create mode 100644 examples/Gymnasium Integration.ipynb create mode 100644 tests/examples/test_gym_integration.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a01d4e4..d6497dc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,7 @@ jobs: python3 -m pip install --upgrade pip python3 -m pip install pytest python3 -m pip install pytest-cov - python3 -m pip install -e .[tests] + python3 -m pip install -e .[tests, gym] - name: Tests run: | diff --git a/dev/Gym codelet.ipynb b/dev/Gym codelet.ipynb index aab533e..ea44177 100644 --- a/dev/Gym codelet.ipynb +++ b/dev/Gym codelet.ipynb @@ -281,7 +281,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [ { diff --git a/examples/Gymnasium Integration.ipynb b/examples/Gymnasium Integration.ipynb new file mode 100644 index 0000000..ad81ade --- /dev/null +++ b/examples/Gymnasium Integration.ipynb @@ -0,0 +1,714 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Gymnasium Integration\n", + "\n", + "[![Open in Colab](https://img.shields.io/badge/Open%20in%20Colab-F9AB00?style=for-the-badge&logo=googlecolab&color=525252)](https://colab.research.google.com/github/H-IAAC/CST-Python/blob/main/examples/Gymnasium%20Integration.ipynb) [![Open in Github](https://img.shields.io/badge/Open%20in%20Github-100000?style=for-the-badge&logo=github&logoColor=white)](https://github.com/H-IAAC/CST-Python/blob/main/examples/Gymnasium%20Integration.ipynb)\n", + "\n", + "[Gymnasium](https://gymnasium.farama.org/) is the library that defines the most widely used interface for creating environments for reinforcement learning problems. CST-Python provides an interface for interacting with environments using a cognitive agent." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lets start by importing the CST-Python and other required modules:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " import cst_python as cst\n", + " import gymnasium as gym\n", + "except:\n", + " !python3 -m pip install cst_python[gym]" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "\n", + "from gymnasium.wrappers import TransformAction, TransformObservation\n", + "\n", + "from cst_python.python.gym import GymCodelet" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## The GymCodelet\n", + "\n", + "The GymCodelet is the main interface with environments. Before we use it, we need to create the environment and the agent's mind.\n", + "\n", + "The environment we gonna use is the Blackjack card game. See the [environment documentation](https://gymnasium.farama.org/environments/toy_text/blackjack/) for more details about the game and the environment." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "env = gym.make(\"Blackjack-v1\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "mind = cst.Mind()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "With the mind and environment, we can create the codelet, insert it inside the mind and start it:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "gym_codelet = GymCodelet(mind, env)\n", + "mind.insert_codelet(gym_codelet)\n", + "\n", + "mind.start()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "One important detail is that the GymCodelet always runs in the [Publisher-Subscriber](https://h-iaac.github.io/CST-Python/_build/html/_examples/Publisher-Subscriber.html) mode.\n", + "\n", + "It creates two important memories for starting the environment: the seed memory and the reset memory.\n", + "\n", + "We gonna set the environment seed to 42 to exemplify how it works, and restart the environment: " + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.seed_memory.set_info(42)\n", + "gym_codelet.reset_memory.set_info(True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If we look the observation memories, we gonna see a single memory with the environment provided observation, a tuple with the player current sum, dealer showing card value and usable ace:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "tags": [ + "observation0" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'observation': MemoryObject [idmemoryobject=0, timestamp=1732730372039, evaluation=0.0, I=(15, 2, 0), name=observation]}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.observation_memories" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "tags": [ + "observation1" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(15, 2, 0)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.observation_memories[\"observation\"].get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The step count memory shows the steps since the episode start:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "tags": [ + "step_count" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.step_count_memory.get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The action memories also contains a single \"action\" memory:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "tags": [ + "action0" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'action': MemoryObject [idmemoryobject=1, timestamp=1732730372025, evaluation=0.0, I=1, name=action]}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.action_memories" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We gonna set it to `1` for a hit." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.action_memories[\"action\"].set_info(1)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When the action memory changes, the codelet executes a step in the environment. We can see that the step count and observation changes:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "tags": [ + "step_count+observation0" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(1, (25, 2, 0))" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.step_count_memory.get_info(), gym_codelet.observation_memories[\"observation\"].get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As we busted, the environment terminated:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "tags": [ + "terminated0" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.terminated_memory.get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And the step reward is -1 as we lost:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "tags": [ + "reward0" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "-1.0" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.reward_memory.get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We gonna start a new episode. Observes that the codelet resets the environment each time the reset memory timestamp changes, even if the content is the same. The first observation is the same as before, since we setted the environment seed:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "tags": [ + "observation2" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(15, 2, 0)" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.reset_memory.set_info(True)\n", + "gym_codelet.observation_memories[\"observation\"].get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This time, we gonna choose to stick:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "tags": [ + "observation3" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(15, 2, 0)" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.action_memories[\"action\"].set_info(0)\n", + "gym_codelet.observation_memories[\"observation\"].get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And we won this game:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "tags": [ + "terminated+reward0" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(True, 1.0)" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.terminated_memory.get_info(), gym_codelet.reward_memory.get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Dict Spaces\n", + "\n", + "So far, we have used the codelet to map all observations in the environment to a single memory with a generic name. However, if the environment has observation and action spaces of type Dict, the Codelet will map each observation and each action to a specific memory.\n", + "\n", + "Let's see this." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "env = gym.make(\"Blackjack-v1\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Different from before, we will use TransformObservation and TransformAction to transform the original observations and actions into Dict Spaces:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "env = TransformObservation(env, \n", + " lambda obs:{\"player_sum\":obs[0], \"dealer_card\":obs[1], \"usable_ace\":obs[2]}, \n", + " gym.spaces.Dict({\"player_sum\":env.observation_space[0], \"dealer_card\":env.observation_space[1], \"usable_ace\":env.observation_space[2]}))\n", + "\n", + "env = TransformAction(env, \n", + " lambda action:action[\"hit\"], \n", + " gym.spaces.Dict({\"hit\":env.action_space}))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's create and start the agent and environment just like before:" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mind = cst.Mind()\n", + "gym_codelet = GymCodelet(mind, env)\n", + "mind.insert_codelet(gym_codelet)\n", + "\n", + "mind.start()\n", + "\n", + "gym_codelet.seed_memory.set_info(42)\n", + "gym_codelet.reset_memory.set_info(True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This time, we can see that the observation memories changed, with a single memory for each observation:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": { + "tags": [ + "observation4" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'dealer_card': MemoryObject [idmemoryobject=0, timestamp=1732730372367, evaluation=0.0, I=2, name=dealer_card],\n", + " 'player_sum': MemoryObject [idmemoryobject=1, timestamp=1732730372367, evaluation=0.0, I=15, name=player_sum],\n", + " 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=1732730372367, evaluation=0.0, I=0, name=usable_ace]}" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.observation_memories" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": { + "tags": [ + "observation5" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'dealer_card': 2, 'player_sum': 15, 'usable_ace': 0}" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "{memory_name:gym_codelet.observation_memories[memory_name].get_info() for memory_name in gym_codelet.observation_memories}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The action memory also changed it's name:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": { + "tags": [ + "action1" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "{'hit': MemoryObject [idmemoryobject=3, timestamp=1732730372365, evaluation=0.0, I=0, name=hit]}" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.action_memories" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Just like before, we choose to stick:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "-1" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.action_memories[\"hit\"].set_info(0)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And won:" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": { + "tags": [ + "terminated+reward1" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "(True, 1.0)" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "gym_codelet.terminated_memory.get_info(), gym_codelet.reward_memory.get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Next steps\n", + "\n", + "The idea is not to use the Codelet to manually interface with the environment like this example, but to create a cognitive architecture to perform the environment's task.\n", + "\n", + "Another possibility is to combine GymCodelet with MemoryStorage to use gym environments with a remote cognitive agent or in CST-Java." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/cst_python/python/gym/gym_codelet.py b/src/cst_python/python/gym/gym_codelet.py index e702319..2498f2b 100644 --- a/src/cst_python/python/gym/gym_codelet.py +++ b/src/cst_python/python/gym/gym_codelet.py @@ -14,7 +14,10 @@ class GymCodelet(Codelet): environment with the action. ''' - _last_indexes : dict[str, int] = {"reward":-1, "reset":-1, "terminated":-1, "truncated":-1, "info":-1, "seed":-1} + _last_indexes : dict[str, int] = {"reward":-1, "reset":-1, + "terminated":-1, "truncated":-1, + "info":-1, "seed":-1, + "step_count":-1} def __init__(self, mind:Mind, env:gym.Env): ''' @@ -36,7 +39,7 @@ def __init__(self, mind:Mind, env:gym.Env): self.action_memories = self.space_to_memories(mind, env.action_space, action=True) self._common_memories : dict[str, MemoryObject] = {} - for name in ["reward", "reset", "terminated", "truncated", "info", "seed"]: + for name in ["reward", "reset", "terminated", "truncated", "info", "seed", "step_count"]: self._last_indexes[name] += 1 memory_name = name @@ -51,6 +54,7 @@ def __init__(self, mind:Mind, env:gym.Env): self._common_memories["truncated"].set_info(False) self._common_memories["info"].set_info({}) self._common_memories["seed"].set_info(None) + self._common_memories["step_count"].set_info(0) self.is_memory_observer = True @@ -59,7 +63,7 @@ def __init__(self, mind:Mind, env:gym.Env): memory.add_memory_observer(self) self._common_memories["reset"].add_memory_observer(self) - self._last_reset = self._common_memories["reset"].get_timestamp() + self._last_reset = 0 @property def reward_memory(self) -> MemoryObject: @@ -105,6 +109,13 @@ def seed_memory(self) -> MemoryObject: ''' return self._common_memories["seed"] + @property + def step_count_memory(self) -> MemoryObject: + ''' + Memory that contains the step count for the current environment + episode. + ''' + return self._common_memories["step_count"] def access_memory_objects(self) -> None: #NOSONAR pass @@ -120,18 +131,20 @@ def proc(self) -> None: reward = 0.0 terminated = False truncated = False + step_count = 0 else: action = self.memories_to_space(self.action_memories, self.env.action_space) observation, r, terminated, truncated, info = self.env.step(action) reward = float(r) #SupportsFloat to float - print("Observation", observation) + step_count = self.step_count_memory.get_info()+1 self.reward_memory.set_info(reward) self.terminated_memory.set_info(terminated) self.truncated_memory.set_info(truncated) self.info_memory.set_info(info) + self.step_count_memory.set_info(step_count) self.sample_to_memories(observation, self.observation_memories) diff --git a/tests/examples/test_gym_integration.py b/tests/examples/test_gym_integration.py new file mode 100644 index 0000000..f1f7e55 --- /dev/null +++ b/tests/examples/test_gym_integration.py @@ -0,0 +1,45 @@ +import os +import re + +from testbook import testbook +from testbook.client import TestbookNotebookClient + +from ..utils import get_examples_path + +examples_path = get_examples_path() + +@testbook(os.path.join(examples_path, "Gymnasium Integration.ipynb"), execute=True) +def test_gym_integration(tb :TestbookNotebookClient): + + expected_result = {"observation0":"{'observation': MemoryObject [idmemoryobject=0, timestamp=, evaluation=0.0, I=(15, 2, 0), name=observation]}", + "observation1":"(15, 2, 0)", + "step_count":"0", + "action0":"{'action': MemoryObject [idmemoryobject=1, timestamp=, evaluation=0.0, I=, name=action]}", + "step_count+observation0":"(1, (25, 2, 0))", + "terminated0":"True", + "reward0":"-1.0", + "observation2":"(15, 2, 0)", + "observation3":"(15, 2, 0)", + "terminated+reward0":"(True, 1.0)", + + "observation4":'''{'dealer_card': MemoryObject [idmemoryobject=0, timestamp=, evaluation=0.0, I=2, name=dealer_card], + 'player_sum': MemoryObject [idmemoryobject=1, timestamp=, evaluation=0.0, I=15, name=player_sum], + 'usable_ace': MemoryObject [idmemoryobject=2, timestamp=, evaluation=0.0, I=0, name=usable_ace]}''', + + "observation5":"{'dealer_card': 2, 'player_sum': 15, 'usable_ace': 0}", + "action1":"{'hit': MemoryObject [idmemoryobject=3, timestamp=, evaluation=0.0, I=, name=hit]}", + "terminated+reward1":"(True, 1.0)" + } + + clear_info = ["action0", "action1"] + + for tag in expected_result: + result = tb.cell_output_text(tag) + result = re.sub(r"timestamp=[0-9]+", "timestamp=", result) + + if tag in clear_info: + result = re.sub(r"I=[0-9]+", "I=", result) + + assert result == expected_result[tag] + + From 34ae5acdb1b829c661dc9445597434685209b9b2 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:03:48 -0300 Subject: [PATCH 12/48] Gym example in documentation --- docs/index.rst | 1 + examples/README.md | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 06ffaa8..a8afed6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -19,6 +19,7 @@ _examples/Implementing a Architecture _examples/Publisher-Subscriber _examples/Activation and Monitoring + _examples/Gymnasium Integration .. toctree:: :maxdepth: 4 diff --git a/examples/README.md b/examples/README.md index 2116e3e..621060e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -5,4 +5,5 @@ Here we have some examples of how to use the CST-Python: - [Introduction to CST-Python](https://h-iaac.github.io/CST-Python/_build/html/_examples/Introduction%20to%20CST-Python.html): what is CST-Python, and basics about how to use it. - [Implementing a Architecture](https://h-iaac.github.io/CST-Python/_build/html/_examples/Implementing%20a%20Architecture.html): how to implement a cognitive architecture using CST-Python. - [Publisher-Subscriber](https://h-iaac.github.io/CST-Python/_build/html/_examples/Publisher-Subscriber.html): using the publisher-subscriber mechanism for synchronous codelets. -- [Activation and Monitoring](https://h-iaac.github.io/CST-Python/_build/html/_examples/Activation%20and%20Monitoring.html): using codelet's activation value and monitoring the agent. \ No newline at end of file +- [Activation and Monitoring](https://h-iaac.github.io/CST-Python/_build/html/_examples/Activation%20and%20Monitoring.html): using codelet's activation value and monitoring the agent. +- [Gymnasium Integration](https://h-iaac.github.io/CST-Python/_build/html/_examples/Gymnasium%20Integration.html): using gymnasium environments with CST. \ No newline at end of file From d5a219884aec048d11ac07ae87f78b474dbfa158 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:32:38 -0300 Subject: [PATCH 13/48] GymCodelet tests --- src/cst_python/python/gym/gym_codelet.py | 12 +- tests/cst_python/python/__init__.py | 0 tests/cst_python/python/gym/__init__.py | 0 .../cst_python/python/gym/test_gym_codelet.py | 124 ++++++++++++++++++ 4 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 tests/cst_python/python/__init__.py create mode 100644 tests/cst_python/python/gym/__init__.py create mode 100644 tests/cst_python/python/gym/test_gym_codelet.py diff --git a/src/cst_python/python/gym/gym_codelet.py b/src/cst_python/python/gym/gym_codelet.py index 2498f2b..3c99e50 100644 --- a/src/cst_python/python/gym/gym_codelet.py +++ b/src/cst_python/python/gym/gym_codelet.py @@ -64,7 +64,7 @@ def __init__(self, mind:Mind, env:gym.Env): self._common_memories["reset"].add_memory_observer(self) self._last_reset = 0 - + @property def reward_memory(self) -> MemoryObject: ''' @@ -148,6 +148,16 @@ def proc(self) -> None: self.sample_to_memories(observation, self.observation_memories) + @classmethod + def reset_indexes(cls) -> None: + ''' + Reset the indexes for setting the sufix of new memories. + ''' + cls._last_indexes : dict[str, int] = {"reward":-1, "reset":-1, + "terminated":-1, "truncated":-1, + "info":-1, "seed":-1, + "step_count":-1} + @classmethod def space_to_memories(cls, mind:Mind, space:gym.Space, diff --git a/tests/cst_python/python/__init__.py b/tests/cst_python/python/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/cst_python/python/gym/__init__.py b/tests/cst_python/python/gym/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/cst_python/python/gym/test_gym_codelet.py b/tests/cst_python/python/gym/test_gym_codelet.py new file mode 100644 index 0000000..5651016 --- /dev/null +++ b/tests/cst_python/python/gym/test_gym_codelet.py @@ -0,0 +1,124 @@ +from contextlib import redirect_stdout +import math +import unittest +import time +import threading +import io + +import gymnasium as gym +from gymnasium.spaces import Box, Dict +import numpy as np +from numpy.testing import assert_array_almost_equal + +from cst_python import MemoryObject, Mind +from cst_python.python.gym import GymCodelet + +class TestGymCodelet(unittest.TestCase): + def setUp(self) -> None: + ... + + def test_space_to_memories(self) -> None: + space = Box(-1, 1, (2,)) + mind = Mind() + + GymCodelet.reset_indexes() + + memories = GymCodelet.space_to_memories(mind, space) + keys = list(memories.keys()) + assert len(keys) == 1 + assert keys[0] == "observation" + memory = memories[keys[0]] + assert memory.get_name() == "observation" + assert space.contains(memory.get_info()) + + memories = GymCodelet.space_to_memories(mind, space) + memory = memories[next(iter(memories))] + assert memory.get_name() == "observation1" + + space = Dict({"x":Box(-1, 1, (2,)), "y":Box(-2, 1, (1,))}) + memories = GymCodelet.space_to_memories(mind, space) + keys = list(memories.keys()) + assert len(keys) == 2 + assert "x" in keys + assert "y" in keys + assert memories["x"].get_name() == "x" + assert memories["y"].get_name() == "y" + + memories = GymCodelet.space_to_memories(mind, space) + keys = list(memories.keys()) + assert len(keys) == 2 + assert "x" in keys + assert "y" in keys + assert memories["x"].get_name() == "x1" + assert memories["y"].get_name() == "y1" + + def test_sample_to_memories(self) -> None: + space = Box(-1, 1, (2,)) + sample = space.sample() + memories = {"observation":MemoryObject()} + + GymCodelet.sample_to_memories(sample, memories) + + assert_array_almost_equal(memories["observation"].get_info(), sample) + + + space = Dict({"x":Box(-1, 1, (2,)), "y":Box(-2, 1, (1,))}) + sample = space.sample() + memories = {"x":MemoryObject(), "y":MemoryObject()} + + GymCodelet.sample_to_memories(sample, memories) + + assert_array_almost_equal(memories["x"].get_info(), sample["x"]) + assert_array_almost_equal(memories["y"].get_info(), sample["y"]) + + def test_memories_to_space(self) -> None: + space = Box(-1, 1, (2,)) + sample = space.sample() + memories = {"observation":MemoryObject()} + memories["observation"].set_info(sample) + + reconstruced_sample = GymCodelet.memories_to_space(memories, space) + assert space.contains(reconstruced_sample) + assert_array_almost_equal(reconstruced_sample, sample) + + space = Dict({"x":Box(-1, 1, (2,)), "y":Box(-2, 1, (1,))}) + sample = space.sample() + memories = {"x":MemoryObject(), "y":MemoryObject()} + memories["x"].set_info(sample["x"]) + memories["y"].set_info(sample["y"]) + + reconstruced_sample = GymCodelet.memories_to_space(memories, space) + assert space.contains(reconstruced_sample) + assert_array_almost_equal(reconstruced_sample["x"], sample["x"]) + assert_array_almost_equal(reconstruced_sample["y"], sample["y"]) + + def test_episode(self) -> None: + env = gym.make("MountainCar-v0") + mind = Mind() + gym_codelet = GymCodelet(mind, env) + + mind.start() + + assert gym_codelet.step_count_memory.get_info() == 0 + gym_codelet.reset_memory.set_info(True) + assert gym_codelet.step_count_memory.get_info() == 0 + gym_codelet.action_memories["action"].set_info(1) + assert gym_codelet.step_count_memory.get_info() == 1 + gym_codelet.action_memories["action"].set_info(1) + assert gym_codelet.step_count_memory.get_info() == 2 + time.sleep(1e-3) #Minimum time for memory timestamp comparation is 1 ms + gym_codelet.reset_memory.set_info(True) + assert gym_codelet.step_count_memory.get_info() == 0 + + def test_env_memories(self) -> None: + env = gym.make("Blackjack-v1") + mind = Mind() + gym_codelet = GymCodelet(mind, env) + + assert len(gym_codelet.observation_memories) == 1 + assert "observation" in gym_codelet.observation_memories + assert env.observation_space.contains(gym_codelet.observation_memories["observation"].get_info()) + + assert len(gym_codelet.action_memories) == 1 + assert "action" in gym_codelet.action_memories + assert env.action_space.contains(gym_codelet.action_memories["action"].get_info()) \ No newline at end of file From 44915f1e629483601e3d9eab631c78a5d6cb3f5e Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:36:39 -0300 Subject: [PATCH 14/48] Fix test pipeline package install --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d6497dc..f8939f8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,7 +28,7 @@ jobs: python3 -m pip install --upgrade pip python3 -m pip install pytest python3 -m pip install pytest-cov - python3 -m pip install -e .[tests, gym] + python3 -m pip install -e .[tests,gym] - name: Tests run: | From efb0a58a204e487d9c0f68949c05bb88a5cef126 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:39:30 -0300 Subject: [PATCH 15/48] Fix GymCodelet typecheking --- src/cst_python/python/gym/gym_codelet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cst_python/python/gym/gym_codelet.py b/src/cst_python/python/gym/gym_codelet.py index 3c99e50..055ec5c 100644 --- a/src/cst_python/python/gym/gym_codelet.py +++ b/src/cst_python/python/gym/gym_codelet.py @@ -153,7 +153,7 @@ def reset_indexes(cls) -> None: ''' Reset the indexes for setting the sufix of new memories. ''' - cls._last_indexes : dict[str, int] = {"reward":-1, "reset":-1, + cls._last_indexes = {"reward":-1, "reset":-1, "terminated":-1, "truncated":-1, "info":-1, "seed":-1, "step_count":-1} From 6a549614042f69c401c3c37013f20940f215f714 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Wed, 27 Nov 2024 19:17:31 -0300 Subject: [PATCH 16/48] RawMemory class documentation --- src/cst_python/core/entities/raw_memory.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cst_python/core/entities/raw_memory.py b/src/cst_python/core/entities/raw_memory.py index 6f7fabf..246834e 100644 --- a/src/cst_python/core/entities/raw_memory.py +++ b/src/cst_python/core/entities/raw_memory.py @@ -11,6 +11,10 @@ #TODO createMemoryContainer, REST methods class RawMemory: + ''' + The Raw Memory contains all memories in the system. + ''' + _last_id = 0 def __init__(self) -> None: From bdc45bcd75c22bf2036dac3cb71b4adf8bcd6388 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Thu, 28 Nov 2024 14:24:34 -0300 Subject: [PATCH 17/48] MS type check --- dev/memory_storage_codelet.ipynb | 111 +++++++----------- dev/test.ipynb | 0 pyproject.toml | 2 +- .../memory_storage/memory_encoder.py | 1 + .../memory_storage/memory_storage.py | 24 ++-- 5 files changed, 57 insertions(+), 81 deletions(-) create mode 100644 dev/test.ipynb diff --git a/dev/memory_storage_codelet.ipynb b/dev/memory_storage_codelet.ipynb index 25f6692..e9b3f34 100644 --- a/dev/memory_storage_codelet.ipynb +++ b/dev/memory_storage_codelet.ipynb @@ -71,7 +71,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -81,17 +81,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "node03 Retrieve Memory1\n" - ] - } - ], + "outputs": [], "source": [ "ms_codelet = MemoryStorageCodelet(mind, \"node0\")\n", "ms_codelet.time_step = 100\n", @@ -102,16 +94,16 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=1, timestamp=1727456263799, evaluation=0.0, I=[1, 1, 1], name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1732814618975, evaluation=0.0, I=, name=Memory1]" ] }, - "execution_count": 4, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -134,15 +126,6 @@ "execution_count": 6, "metadata": {}, "output_type": "execute_result" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "node02 Updating memory Memory1\n", - "node02 Send memory Memory1\n", - "node02 Updating memory Memory1\n" - ] } ], "source": [ @@ -186,11 +169,7 @@ { "data": { "text/plain": [ - "{'name': 'Memory1',\n", - " 'evaluation': '0.0',\n", - " 'I': '',\n", - " 'id': '0.0',\n", - " 'owner': 'node0'}" + "{'name': 'Memory1', 'evaluation': '0.0', 'I': '', 'id': '0', 'owner': 'node0'}" ] }, "execution_count": 9, @@ -225,24 +204,24 @@ "name": "stdout", "output_type": "stream", "text": [ - "node1 Retrieve Memory1\n", - "node1 Requesting Memory1\n", + "node Retrieve Memory1\n", + "node Requesting Memory1\n", "node0 Tranfering Memory1\n", "node0 Send memory Memory1\n", - "node1 Updating memory Memory1\n", + "node Updating memory Memory1\n", "node0 Updating memory Memory1\n", - "node1 Send memory Memory1\n", - "node1 Updating memory Memory1\n", + "node Send memory Memory1\n", "node0 Updating memory Memory1\n", - "node0 Retrieve Memory1\n", - "node0 INFO \"\"\n", - "node1 INFO \"\"\n" + "\n", + "{'type': 'message', 'pattern': None, 'channel': 'default_mind:nodes:node:transfer_done', 'data': 'Memory1'}\n", + "node Updating memory Memory1\n", + "node0 Retrieve Memory1\n" ] }, { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0.0, timestamp=1726077369.7999365, evaluation=0.0, I=, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1732814620358, evaluation=0.0, I=, name=Memory1]" ] }, "execution_count": 11, @@ -262,7 +241,7 @@ { "data": { "text/plain": [ - "'node1'" + "'node'" ] }, "execution_count": 12, @@ -282,7 +261,7 @@ { "data": { "text/plain": [ - "{'node0', 'node1'}" + "{'node', 'node0'}" ] }, "execution_count": 13, @@ -311,7 +290,7 @@ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0.0, timestamp=1726077369.7999365, evaluation=0.0, I=, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1732814620646, evaluation=0.0, I=, name=Memory1]" ] }, "execution_count": 15, @@ -336,7 +315,7 @@ " 'I': '\"\"',\n", " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1726077369.5866976'}" + " 'timestamp': '1732814620358'}" ] }, "execution_count": 16, @@ -379,10 +358,9 @@ "text": [ "node0 Updating memory Memory1\n", "node0 Send memory Memory1\n", - "node1 Updating memory Memory1\n", + "node Updating memory Memory1\n", "node0 Updating memory Memory1\n", - "node1 Retrieve Memory1\n", - "node1 INFO INFO \"INFO\"\n" + "node Retrieve Memory1\n" ] } ], @@ -401,9 +379,9 @@ "{'name': 'Memory1',\n", " 'evaluation': '0.0',\n", " 'I': '\"INFO\"',\n", - " 'id': '0.0',\n", + " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1726077370.926107'}" + " 'timestamp': '1732814621740'}" ] }, "execution_count": 19, @@ -423,7 +401,7 @@ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0.0, timestamp=1726077371.003417, evaluation=0.0, I=INFO, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1732814621841, evaluation=0.0, I=INFO, name=Memory1]" ] }, "execution_count": 20, @@ -455,9 +433,9 @@ "{'name': 'Memory1',\n", " 'evaluation': '0.0',\n", " 'I': '\"INFO\"',\n", - " 'id': '0.0',\n", + " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1726077370.926107'}" + " 'timestamp': '1732814621740'}" ] }, "execution_count": 22, @@ -478,12 +456,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "node1 Updating memory Memory1\n", - "node1 Send memory Memory1\n", + "node Updating memory Memory1\n", + "node Send memory Memory1\n", + "node Updating memory Memory1\n", "node0 Updating memory Memory1\n", - "node1 Updating memory Memory1\n", - "node0 Retrieve Memory1\n", - "node0 INFO INFO2 \"INFO2\"\n" + "node0 Retrieve Memory1\n" ] } ], @@ -503,9 +480,9 @@ "{'name': 'Memory1',\n", " 'evaluation': '0.0',\n", " 'I': '\"INFO2\"',\n", - " 'id': '0.0',\n", + " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1726077373.0085642'}" + " 'timestamp': '1732814623859'}" ] }, "execution_count": 24, @@ -525,7 +502,7 @@ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0.0, timestamp=1726077373.1104536, evaluation=0.0, I=INFO2, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1732814623892, evaluation=0.0, I=INFO2, name=Memory1]" ] }, "execution_count": 25, @@ -548,10 +525,9 @@ "text": [ "node0 Updating memory Memory1\n", "node0 Send memory Memory1\n", + "node Updating memory Memory1\n", "node0 Updating memory Memory1\n", - "node1 Updating memory Memory1\n", - "node1 Retrieve Memory1\n", - "node1 INFO 1 1\n" + "node Retrieve Memory1\n" ] } ], @@ -592,9 +568,8 @@ "node0 Updating memory Memory1\n", "node0 Send memory Memory1\n", "node0 Updating memory Memory1\n", - "node1 Updating memory Memory1\n", - "node1 Retrieve Memory1\n", - "node1 INFO 1 \"1\"\n" + "node Updating memory Memory1\n", + "node Retrieve Memory1\n" ] } ], @@ -634,10 +609,9 @@ "text": [ "node0 Updating memory Memory1\n", "node0 Send memory Memory1\n", - "node1 Updating memory Memory1\n", + "node Updating memory Memory1\n", "node0 Updating memory Memory1\n", - "node1 Retrieve Memory1\n", - "node1 INFO True true\n" + "node Retrieve Memory1\n" ] } ], @@ -677,10 +651,9 @@ "text": [ "node0 Updating memory Memory1\n", "node0 Send memory Memory1\n", - "node1 Updating memory Memory1\n", + "node Updating memory Memory1\n", "node0 Updating memory Memory1\n", - "node1 Retrieve Memory1\n", - "node1 INFO [1, 2, 3] [1, 2, 3]\n" + "node Retrieve Memory1\n" ] }, { diff --git a/dev/test.ipynb b/dev/test.ipynb new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index f5c97c8..9f9cd96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ Homepage = "https://hiaac.unicamp.br" # Documentation = [project.optional-dependencies] -tests = ["mypy", "testbook", "ipython", "ipykernel", "numpy", "matplotlib"] +tests = ["mypy", "testbook", "ipython", "ipykernel", "numpy", "matplotlib", "types-redis"] doc_generation = ["sphinx", "sphinx_rtd_theme", "nbsphinx", "sphinx-mdinclude==0.5.4"] dev = ["cffconvert"] gym = ["gymnasium"] diff --git a/src/cst_python/memory_storage/memory_encoder.py b/src/cst_python/memory_storage/memory_encoder.py index 7c5a9e9..2a58dca 100644 --- a/src/cst_python/memory_storage/memory_encoder.py +++ b/src/cst_python/memory_storage/memory_encoder.py @@ -22,6 +22,7 @@ def to_dict(memory:Memory, jsonify_info:bool=False): return data + @staticmethod def load_memory(memory:Memory, memory_dict:dict[str,Any], load_json:bool=True): memory.set_evaluation(float(memory_dict["evaluation"])) memory.set_id(int(memory_dict["id"])) diff --git a/src/cst_python/memory_storage/memory_storage.py b/src/cst_python/memory_storage/memory_storage.py index 7abb474..f4c1705 100644 --- a/src/cst_python/memory_storage/memory_storage.py +++ b/src/cst_python/memory_storage/memory_storage.py @@ -27,10 +27,10 @@ def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[s self._pubsub = self._client.pubsub() self._pubsub_thread : redis.client.PubSubWorkerThread = self._pubsub.run_in_thread() + if node_name is None: + node_name = "node" base_name = node_name - if base_name is None: - base_name = "node" - + if self._client.sismember(f"{mind_name}:nodes", node_name): node_number = self._client.scard(f"{mind_name}:nodes") node_name = base_name+str(node_number) @@ -58,17 +58,17 @@ def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[s self._request = None - def calculate_activation(self) -> None: + def calculate_activation(self) -> None: #NOSONAR pass - def access_memory_objects(self) -> None: + def access_memory_objects(self) -> None: #NOSONAR pass def proc(self) -> None: #Check new memories - mind_memories = {} + mind_memories : dict[str, Memory] = {} for memory in self._mind.raw_memory.all_memories: if memory.get_name() == "": #No name -> No MS continue @@ -81,14 +81,14 @@ def proc(self) -> None: #Check only not here (memories_names not in mind should be garbage collected) difference = mind_memories_names - memories_names for memory_name in difference: - memory : Memory = mind_memories[memory_name] + memory = mind_memories[memory_name] self._memories[memory_name] = memory if self._client.exists(f"{self._mind_name}:memories:{memory_name}"): self._retrieve_executor.submit(self._retrieve_memory, memory) else: #Send impostor with owner - memory_impostor = {"name":memory.get_name(), + memory_impostor : dict[str|bytes, str|float|int] = {"name":memory.get_name(), "evaluation" : 0.0, "I": "", "id" : 0, @@ -116,7 +116,9 @@ def update_memory(self, memory_name:str) -> None: if memory_name not in self._memories: self._pubsub.unsubscribe(f"{self._mind_name}:memories:{memory_name}:update") - timestamp = float(self._client.hget(f"{self._mind_name}:memories:{memory_name}", "timestamp")) + timestamp_result = self._client.hget(f"{self._mind_name}:memories:{memory_name}", "timestamp") + assert timestamp_result is not None + timestamp = float(timestamp_result) memory = self._memories[memory_name] memory_timestamp = memory.get_timestamp() @@ -181,14 +183,14 @@ def _request_memory(self, memory_name:str, owner_name:str) -> None: request = json.dumps(request_dict) self._client.publish(request_addr, request) - def _handler_notify_transfer(self, message:str) -> None: + def _handler_notify_transfer(self, message:dict[str,str]) -> None: memory_name = message["data"] if memory_name in self._waiting_request_events: event = self._waiting_request_events[memory_name] event.set() del self._waiting_request_events[memory_name] - def _handler_transfer_memory(self, message) -> None: + def _handler_transfer_memory(self, message:dict[str,str]) -> None: request = json.loads(message["data"]) memory_name = request["memory_name"] From edc19f31bf45e542832e6a43f069d6df8c47056f Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Thu, 28 Nov 2024 16:51:21 -0300 Subject: [PATCH 18/48] MS logging --- dev/memory_storage_codelet.ipynb | 221 +++++++++--------- .../memory_storage/memory_storage.py | 23 +- 2 files changed, 127 insertions(+), 117 deletions(-) diff --git a/dev/memory_storage_codelet.ipynb b/dev/memory_storage_codelet.ipynb index e9b3f34..7c8e5fa 100644 --- a/dev/memory_storage_codelet.ipynb +++ b/dev/memory_storage_codelet.ipynb @@ -18,6 +18,21 @@ "cell_type": "code", "execution_count": 2, "metadata": {}, + "outputs": [], + "source": [ + "import logging\n", + "import sys\n", + "\n", + "ch = logging.StreamHandler(sys.stdout)\n", + "ch.setLevel(logging.INFO)\n", + "\n", + "logging.getLogger(\"MemoryStorageCodelet\").addHandler(ch)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, "outputs": [ { "data": { @@ -25,7 +40,7 @@ "True" ] }, - "execution_count": 2, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } @@ -71,7 +86,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -81,7 +96,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -94,16 +109,16 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0, timestamp=1732814618975, evaluation=0.0, I=, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1732819369223, evaluation=0.0, I=, name=Memory1]" ] }, - "execution_count": 5, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" } @@ -114,7 +129,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -123,7 +138,7 @@ "-1" ] }, - "execution_count": 6, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -134,7 +149,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -143,7 +158,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -152,7 +167,7 @@ "{'node0'}" ] }, - "execution_count": 8, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -163,7 +178,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -172,7 +187,7 @@ "{'name': 'Memory1', 'evaluation': '0.0', 'I': '', 'id': '0', 'owner': 'node0'}" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -183,9 +198,17 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieving memory [Memory1@node]\n" + ] + } + ], "source": [ "mind2 = cst.Mind()\n", "mind2_memory1 = mind2.create_memory_object(\"Memory1\", \"\")\n", @@ -197,34 +220,16 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "node Retrieve Memory1\n", - "node Requesting Memory1\n", - "node0 Tranfering Memory1\n", - "node0 Send memory Memory1\n", - "node Updating memory Memory1\n", - "node0 Updating memory Memory1\n", - "node Send memory Memory1\n", - "node0 Updating memory Memory1\n", - "\n", - "{'type': 'message', 'pattern': None, 'channel': 'default_mind:nodes:node:transfer_done', 'data': 'Memory1'}\n", - "node Updating memory Memory1\n", - "node0 Retrieve Memory1\n" - ] - }, { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0, timestamp=1732814620358, evaluation=0.0, I=, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1732819370759, evaluation=0.0, I=, name=Memory1]" ] }, - "execution_count": 11, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -235,7 +240,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -244,7 +249,7 @@ "'node'" ] }, - "execution_count": 12, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -255,7 +260,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -264,7 +269,7 @@ "{'node', 'node0'}" ] }, - "execution_count": 13, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -275,7 +280,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -284,16 +289,16 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0, timestamp=1732814620646, evaluation=0.0, I=, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1732819370759, evaluation=0.0, I=, name=Memory1]" ] }, - "execution_count": 15, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" } @@ -304,7 +309,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -315,10 +320,10 @@ " 'I': '\"\"',\n", " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1732814620358'}" + " 'timestamp': '1732819370746'}" ] }, - "execution_count": 16, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -329,7 +334,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "metadata": {}, "outputs": [ { @@ -338,7 +343,7 @@ "-1" ] }, - "execution_count": 17, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -349,18 +354,18 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 20, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "node0 Updating memory Memory1\n", - "node0 Send memory Memory1\n", - "node Updating memory Memory1\n", - "node0 Updating memory Memory1\n", - "node Retrieve Memory1\n" + "Updating memory [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n" ] } ], @@ -370,7 +375,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 21, "metadata": {}, "outputs": [ { @@ -381,10 +386,10 @@ " 'I': '\"INFO\"',\n", " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1732814621740'}" + " 'timestamp': '1732819378505'}" ] }, - "execution_count": 19, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -395,16 +400,16 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 22, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0, timestamp=1732814621841, evaluation=0.0, I=INFO, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1732819378534, evaluation=0.0, I=INFO, name=Memory1]" ] }, - "execution_count": 20, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -415,7 +420,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -424,7 +429,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 24, "metadata": {}, "outputs": [ { @@ -435,10 +440,10 @@ " 'I': '\"INFO\"',\n", " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1732814621740'}" + " 'timestamp': '1732819378505'}" ] }, - "execution_count": 22, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -449,18 +454,18 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "node Updating memory Memory1\n", - "node Send memory Memory1\n", - "node Updating memory Memory1\n", - "node0 Updating memory Memory1\n", - "node0 Retrieve Memory1\n" + "Updating memory [Memory1@node]\n", + "Sending memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node0]\n" ] } ], @@ -471,7 +476,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 26, "metadata": {}, "outputs": [ { @@ -482,10 +487,10 @@ " 'I': '\"INFO2\"',\n", " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1732814623859'}" + " 'timestamp': '1732819380596'}" ] }, - "execution_count": 24, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -496,16 +501,16 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 27, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0, timestamp=1732814623892, evaluation=0.0, I=INFO2, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1732819380701, evaluation=0.0, I=INFO2, name=Memory1]" ] }, - "execution_count": 25, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -516,18 +521,18 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 28, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "node0 Updating memory Memory1\n", - "node0 Send memory Memory1\n", - "node Updating memory Memory1\n", - "node0 Updating memory Memory1\n", - "node Retrieve Memory1\n" + "Updating memory [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n" ] } ], @@ -538,7 +543,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 29, "metadata": {}, "outputs": [ { @@ -547,7 +552,7 @@ "1" ] }, - "execution_count": 27, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -558,18 +563,18 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 30, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "node0 Updating memory Memory1\n", - "node0 Send memory Memory1\n", - "node0 Updating memory Memory1\n", - "node Updating memory Memory1\n", - "node Retrieve Memory1\n" + "Updating memory [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node]\n" ] } ], @@ -580,7 +585,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 31, "metadata": {}, "outputs": [ { @@ -589,7 +594,7 @@ "'1'" ] }, - "execution_count": 29, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -600,18 +605,18 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 32, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "node0 Updating memory Memory1\n", - "node0 Send memory Memory1\n", - "node Updating memory Memory1\n", - "node0 Updating memory Memory1\n", - "node Retrieve Memory1\n" + "Updating memory [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node]\n" ] } ], @@ -622,7 +627,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -631,7 +636,7 @@ "(True, bool)" ] }, - "execution_count": 31, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -642,18 +647,18 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "node0 Updating memory Memory1\n", - "node0 Send memory Memory1\n", - "node Updating memory Memory1\n", - "node0 Updating memory Memory1\n", - "node Retrieve Memory1\n" + "Updating memory [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node]\n" ] }, { @@ -662,7 +667,7 @@ "([1, 2, 3], list)" ] }, - "execution_count": 32, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } diff --git a/src/cst_python/memory_storage/memory_storage.py b/src/cst_python/memory_storage/memory_storage.py index f4c1705..aa309bd 100644 --- a/src/cst_python/memory_storage/memory_storage.py +++ b/src/cst_python/memory_storage/memory_storage.py @@ -3,6 +3,8 @@ import json import threading from concurrent.futures import ThreadPoolExecutor +import logging +import functools from typing import Optional, cast import redis @@ -10,6 +12,9 @@ from cst_python.core.entities import Codelet, Mind, Memory, MemoryObject from .memory_encoder import MemoryEncoder +logger = logging.getLogger("MemoryStorageCodelet") +logger.setLevel(logging.DEBUG) + class MemoryStorageCodelet(Codelet): def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[str]=None, request_timeout:float=500e-3) -> None: super().__init__() @@ -96,7 +101,8 @@ def proc(self) -> None: self._client.hset(f"{self._mind_name}:memories:{memory_name}", mapping=memory_impostor) - subscribe_func = lambda message : self.update_memory(memory_name) + subscribe_func = lambda _, name : self.update_memory(name) + subscribe_func = functools.partial(subscribe_func, name=memory_name) self._pubsub.subscribe(**{f"{self._mind_name}:memories:{memory_name}:update":subscribe_func}) #Update memories @@ -111,7 +117,7 @@ def proc(self) -> None: self.update_memory(memory_name) def update_memory(self, memory_name:str) -> None: - print(self._node_name, "Updating memory", memory_name) + logger.info(f"Updating memory [{memory_name}@{self._node_name}]") if memory_name not in self._memories: self._pubsub.unsubscribe(f"{self._mind_name}:memories:{memory_name}:update") @@ -132,8 +138,8 @@ def update_memory(self, memory_name:str) -> None: def _send_memory(self, memory:Memory) -> None: memory_name = memory.get_name() - print(self._node_name, "Send memory", memory_name) - + logger.info(f"Sending memory [{memory_name}@{self._node_name}]") + memory_dict = MemoryEncoder.to_dict(memory, jsonify_info=True) memory_dict["owner"] = "" @@ -146,8 +152,7 @@ def _send_memory(self, memory:Memory) -> None: def _retrieve_memory(self, memory:Memory) -> None: memory_name = memory.get_name() - - print(self._node_name, "Retrieve", memory_name) + logger.info(f"Retrieving memory [{memory_name}@{self._node_name}]") if memory_name in self._waiting_retrieve: return @@ -161,7 +166,7 @@ def _retrieve_memory(self, memory:Memory) -> None: self._request_memory(memory_name, memory_dict["owner"]) if not event.wait(timeout=self._request_timeout): - print(self._node_name, "Request failed", memory_name) + logger.warning(f"Request failed [{memory_name}@{memory_dict['owner']} to {self._node_name}]") #Request failed self._send_memory(memory) return @@ -175,7 +180,7 @@ def _retrieve_memory(self, memory:Memory) -> None: self._waiting_retrieve.remove(memory_name) def _request_memory(self, memory_name:str, owner_name:str) -> None: - print(self._node_name, "Requesting", memory_name) + logger.info(f"Requesting memory [{memory_name}@{owner_name} to {self._node_name}]") request_addr = f"{self._mind_name}:nodes:{owner_name}:transfer_memory" @@ -196,7 +201,7 @@ def _handler_transfer_memory(self, message:dict[str,str]) -> None: memory_name = request["memory_name"] requesting_node = request["node"] - print(self._node_name, "Tranfering", memory_name) + logger.info(f"Transfering memory to server [{memory_name}@{self._node_name}]") if memory_name in self._memories: memory = self._memories[memory_name] From 5cbcfca58aeb81ede309714bde2127701be91783 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:29:56 -0300 Subject: [PATCH 19/48] MS logical time --- dev/LogicalTime.ipynb | 199 ++++++++ dev/memory_storage_codelet.ipynb | 463 +++++++++++++++--- dev/memory_storage_codelet.py | 121 +++++ src/cst_python/memory_storage/logical_time.py | 87 ++++ .../memory_storage/memory_storage.py | 70 ++- 5 files changed, 861 insertions(+), 79 deletions(-) create mode 100644 dev/LogicalTime.ipynb create mode 100644 dev/memory_storage_codelet.py create mode 100644 src/cst_python/memory_storage/logical_time.py diff --git a/dev/LogicalTime.ipynb b/dev/LogicalTime.ipynb new file mode 100644 index 0000000..028685c --- /dev/null +++ b/dev/LogicalTime.ipynb @@ -0,0 +1,199 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "import abc\n", + "import functools\n", + "\n", + "from typing import Self" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "import abc\n", + "import functools\n", + "\n", + "from typing import Self\n", + "\n", + "class LogicalTime(abc.ABC):\n", + "\n", + " @abc.abstractmethod\n", + " def increment(self) -> \"LogicalTime\":\n", + " ...\n", + "\n", + "\n", + " @abc.abstractmethod\n", + " def __str__(self) -> str:\n", + " ...\n", + " \n", + " @classmethod\n", + " @abc.abstractmethod\n", + " def from_str(cls, string:str) -> \"LogicalTime\":\n", + " ...\n", + "\n", + " @classmethod\n", + " @abc.abstractmethod\n", + " def syncronize(cls, time0:Self, time1:Self) -> \"LogicalTime\":\n", + " ...\n", + "\n", + " @abc.abstractmethod\n", + " def __eq__(self, other) -> bool:\n", + " ...\n", + " \n", + " @abc.abstractmethod\n", + " def __lt__(self, other) -> bool:\n", + " ...\n", + "\n", + " @abc.abstractmethod\n", + " def __le__(self, other) -> bool:\n", + " ...\n", + "\n", + " @abc.abstractmethod\n", + " def __gt__(self, other) -> bool:\n", + " ...\n", + "\n", + " @abc.abstractmethod\n", + " def __ge__(self, other) -> bool:\n", + " ...\n", + "\n", + "\n", + "@functools.total_ordering\n", + "class LamportTime(LogicalTime):\n", + " __le__ = object.__lt__\n", + " __gt__ = object.__gt__\n", + " __ge__ = object.__ge__\n", + "\n", + "\n", + " def __init__(self, initial_time:int=0):\n", + " super().__init__()\n", + " self._time = initial_time\n", + "\n", + " def increment(self) -> \"LamportTime\":\n", + " return LamportTime(initial_time=self._time+1)\n", + "\n", + " def __eq__(self, other) -> bool:\n", + " return self._time == other._time\n", + "\n", + " def __lt__(self, other) -> bool:\n", + " return self._time < other._time \n", + "\n", + " def __str__(self) -> str:\n", + " return str(self._time)\n", + "\n", + " @classmethod\n", + " def from_str(cls, string:str) -> \"LamportTime\":\n", + " return LamportTime(int(string))\n", + "\n", + " @classmethod\n", + " def syncronize(cls, time0:Self, time1:Self) -> \"LamportTime\":\n", + " new_time = 0\n", + " if time0 < time1:\n", + " new_time = time1._time\n", + " else:\n", + " new_time = time0._time\n", + "\n", + " new_time += 1\n", + "\n", + " return LamportTime(new_time)" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "time0 = LamportTime()" + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n" + ] + } + ], + "source": [ + "print(time0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n" + ] + } + ], + "source": [ + "time1 = time0.increment()\n", + "print(time1)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2\n" + ] + } + ], + "source": [ + "time3 = LamportTime.syncronize(time0, time1)\n", + "print(time3)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/dev/memory_storage_codelet.ipynb b/dev/memory_storage_codelet.ipynb index 7c8e5fa..aaead99 100644 --- a/dev/memory_storage_codelet.ipynb +++ b/dev/memory_storage_codelet.ipynb @@ -96,11 +96,11 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "ms_codelet = MemoryStorageCodelet(mind, \"node0\")\n", + "ms_codelet = MemoryStorageCodelet(mind)\n", "ms_codelet.time_step = 100\n", "\n", "mind.insert_codelet(ms_codelet)\n", @@ -115,7 +115,7 @@ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0, timestamp=1732819369223, evaluation=0.0, I=, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1733073393528, evaluation=0.0, I=, name=Memory1]" ] }, "execution_count": 6, @@ -184,7 +184,12 @@ { "data": { "text/plain": [ - "{'name': 'Memory1', 'evaluation': '0.0', 'I': '', 'id': '0', 'owner': 'node0'}" + "{'name': 'Memory1',\n", + " 'evaluation': '0.0',\n", + " 'I': '',\n", + " 'id': '0',\n", + " 'owner': 'node0',\n", + " 'logical_time': '0'}" ] }, "execution_count": 10, @@ -205,10 +210,19 @@ "name": "stdout", "output_type": "stream", "text": [ - "Retrieving memory [Memory1@node]\n" + "1\n" ] } ], + "source": [ + "print(ms_codelet._current_time)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], "source": [ "mind2 = cst.Mind()\n", "mind2_memory1 = mind2.create_memory_object(\"Memory1\", \"\")\n", @@ -220,18 +234,264 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieving memory [Memory1@node]\n" + ] + }, { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0, timestamp=1732819370759, evaluation=0.0, I=, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1733073334814, evaluation=0.0, I=, name=Memory1]" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Retrieving memory [Memory1@node]\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Transfering memory to server [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Transfering memory to server [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Sending memory [Memory1@node0]\n", + "Sending memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Transfering memory to server [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Transfering memory to server [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Sending memory [Memory1@node0]\n", + "Sending memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Transfering memory to server [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Transfering memory to server [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Transfering memory to server [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Sending memory [Memory1@node]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Transfering memory to server [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Sending memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Retrieving memory [Memory1@node]\n", + "Retrieving memory [Memory1@node0]\n", + "Transfering memory to server [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Sending memory [Memory1@node]\n", + "Sending memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "Updating memory [Memory1@node0]\n" + ] } ], "source": [ @@ -240,9 +500,18 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requesting memory [Memory1@node0 to node]\n", + "Transfering memory to server [Memory1@node0]\n", + "Sending memory [Memory1@node0]\n" + ] + }, { "data": { "text/plain": [ @@ -260,7 +529,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -269,7 +538,7 @@ "{'node', 'node0'}" ] }, - "execution_count": 15, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" } @@ -280,7 +549,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -289,16 +558,16 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0, timestamp=1732819370759, evaluation=0.0, I=, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1733073335060, evaluation=0.0, I=[1, 1, 1], name=Memory1]" ] }, - "execution_count": 17, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -309,7 +578,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -317,13 +586,14 @@ "text/plain": [ "{'name': 'Memory1',\n", " 'evaluation': '0.0',\n", - " 'I': '\"\"',\n", + " 'I': '[1, 1, 1]',\n", " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1732819370746'}" + " 'logical_time': '0',\n", + " 'timestamp': '1733073333720'}" ] }, - "execution_count": 18, + "execution_count": 19, "metadata": {}, "output_type": "execute_result" } @@ -334,7 +604,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -343,9 +613,16 @@ "-1" ] }, - "execution_count": 19, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Updating memory [Memory1@node0]\n" + ] } ], "source": [ @@ -354,28 +631,16 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Updating memory [Memory1@node0]\n", - "Sending memory [Memory1@node0]\n", - "Updating memory [Memory1@node]\n", - "Updating memory [Memory1@node0]\n", - "Retrieving memory [Memory1@node]\n" - ] - } - ], + "outputs": [], "source": [ "time.sleep(1)" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -386,10 +651,11 @@ " 'I': '\"INFO\"',\n", " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1732819378505'}" + " 'logical_time': '3',\n", + " 'timestamp': '1733073340798'}" ] }, - "execution_count": 21, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -400,16 +666,16 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0, timestamp=1732819378534, evaluation=0.0, I=INFO, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1733073340830, evaluation=0.0, I=INFO, name=Memory1]" ] }, - "execution_count": 22, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -420,7 +686,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -429,7 +695,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -440,10 +706,11 @@ " 'I': '\"INFO\"',\n", " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1732819378505'}" + " 'logical_time': '3',\n", + " 'timestamp': '1733073340798'}" ] }, - "execution_count": 24, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -454,7 +721,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -462,9 +729,18 @@ "output_type": "stream", "text": [ "Updating memory [Memory1@node]\n", + "\n", + "PRINT Updating memory [Memory1@node]\n", + "\n", "Sending memory [Memory1@node]\n", - "Updating memory [Memory1@node]\n", "Updating memory [Memory1@node0]\n", + "\n", + "PRINT Updating memory [Memory1@node0]\n", + "Updating memory [Memory1@node]\n", + "\n", + "\n", + "PRINT Updating memory [Memory1@node]\n", + "\n", "Retrieving memory [Memory1@node0]\n" ] } @@ -476,7 +752,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -487,10 +763,11 @@ " 'I': '\"INFO2\"',\n", " 'id': '0',\n", " 'owner': '',\n", - " 'timestamp': '1732819380596'}" + " 'logical_time': '6',\n", + " 'timestamp': '1733073348658'}" ] }, - "execution_count": 26, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -501,16 +778,16 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": null, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "MemoryObject [idmemoryobject=0, timestamp=1732819380701, evaluation=0.0, I=INFO2, name=Memory1]" + "MemoryObject [idmemoryobject=0, timestamp=1733073348735, evaluation=0.0, I=INFO2, name=Memory1]" ] }, - "execution_count": 27, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -521,7 +798,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -529,9 +806,18 @@ "output_type": "stream", "text": [ "Updating memory [Memory1@node0]\n", + "\n", + "PRINT Updating memory [Memory1@node0]\n", + "\n", "Sending memory [Memory1@node0]\n", "Updating memory [Memory1@node]\n", + "\n", + "PRINT Updating memory [Memory1@node]\n", + "\n", "Updating memory [Memory1@node0]\n", + "\n", + "PRINT Updating memory [Memory1@node0]\n", + "\n", "Retrieving memory [Memory1@node]\n" ] } @@ -543,7 +829,29 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'default_mind:nodes:node:transfer_memory': >,\n", + " 'default_mind:nodes:node:transfer_done': >,\n", + " 'default_mind:memories:Memory1:update': functools.partial(. at 0x00000128877A7CE0>, name='Memory1')}" + ] + }, + "execution_count": 31, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "mind2_ms_codelet._pubsub.channels" + ] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": {}, "outputs": [ { @@ -552,7 +860,7 @@ "1" ] }, - "execution_count": 29, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -563,7 +871,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -571,9 +879,18 @@ "output_type": "stream", "text": [ "Updating memory [Memory1@node0]\n", + "\n", + "PRINT Updating memory [Memory1@node0]\n", + "\n", "Sending memory [Memory1@node0]\n", "Updating memory [Memory1@node0]\n", + "\n", + "PRINT Updating memory [Memory1@node0]\n", "Updating memory [Memory1@node]\n", + "\n", + "\n", + "PRINT Updating memory [Memory1@node]\n", + "\n", "Retrieving memory [Memory1@node]\n" ] } @@ -585,7 +902,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -594,7 +911,7 @@ "'1'" ] }, - "execution_count": 31, + "execution_count": 34, "metadata": {}, "output_type": "execute_result" } @@ -605,7 +922,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -613,9 +930,18 @@ "output_type": "stream", "text": [ "Updating memory [Memory1@node0]\n", + "\n", + "PRINT Updating memory [Memory1@node0]\n", + "\n", "Sending memory [Memory1@node0]\n", "Updating memory [Memory1@node0]\n", + "\n", + "PRINT Updating memory [Memory1@node0]\n", + "\n", "Updating memory [Memory1@node]\n", + "\n", + "PRINT Updating memory [Memory1@node]\n", + "\n", "Retrieving memory [Memory1@node]\n" ] } @@ -627,7 +953,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -636,7 +962,7 @@ "(True, bool)" ] }, - "execution_count": 33, + "execution_count": 36, "metadata": {}, "output_type": "execute_result" } @@ -647,7 +973,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -655,9 +981,18 @@ "output_type": "stream", "text": [ "Updating memory [Memory1@node0]\n", + "\n", + "PRINT Updating memory [Memory1@node0]\n", + "\n", "Sending memory [Memory1@node0]\n", - "Updating memory [Memory1@node0]\n", "Updating memory [Memory1@node]\n", + "\n", + "PRINT Updating memory [Memory1@node]\n", + "\n", + "Updating memory [Memory1@node0]\n", + "\n", + "PRINT Updating memory [Memory1@node0]\n", + "\n", "Retrieving memory [Memory1@node]\n" ] }, @@ -667,7 +1002,7 @@ "([1, 2, 3], list)" ] }, - "execution_count": 34, + "execution_count": 37, "metadata": {}, "output_type": "execute_result" } diff --git a/dev/memory_storage_codelet.py b/dev/memory_storage_codelet.py new file mode 100644 index 0000000..8dacd3d --- /dev/null +++ b/dev/memory_storage_codelet.py @@ -0,0 +1,121 @@ +# %% +import time + +import redis + +import cst_python as cst +from cst_python.memory_storage import MemoryStorageCodelet + +import logging +import sys +import threading + +from numpy.testing import assert_array_almost_equal + +sleep_time = 0.2 + +#ch = logging.StreamHandler(sys.stdout) +#ch.setLevel(logging.INFO) +# +#logging.getLogger("MemoryStorageCodelet").addHandler(ch) + +client = redis.Redis(decode_responses=True) +client.flushall() + +mind = cst.Mind() +memory1 = mind.create_memory_object("Memory1", "") + +ms_codelet = MemoryStorageCodelet(mind) +ms_codelet.time_step = 100 + +mind.insert_codelet(ms_codelet) +mind.start() + +assert memory1.get_info() == "" + +memory1.set_info([1,1,1]) + +time.sleep(sleep_time) + +members = client.smembers("default_mind:nodes") +assert len(members) == 1 +assert "node" in members + +result = client.hgetall("default_mind:memories:Memory1") +expected_result = {"name":"Memory1", "evaluation":"0.0", "I":"", "id":"0", "owner":"node", "logical_time":"0"} +assert result == expected_result + + +mind2 = cst.Mind() +mind2_memory1 = mind2.create_memory_object("Memory1", "") +mind2_ms_codelet = MemoryStorageCodelet(mind2) +mind2_ms_codelet.time_step = 100 +mind2.insert_codelet(mind2_ms_codelet) +mind2.start() + +assert mind2_memory1.get_info() == "" + +assert mind2_ms_codelet._node_name == "node1" + +members = client.smembers("default_mind:nodes") +assert len(members) == 2 +assert "node" in members +assert "node1" in members + +time.sleep(sleep_time) + +assert_array_almost_equal(memory1.get_info(), [1,1,1]) +assert_array_almost_equal(mind2_memory1.get_info(), [1,1,1]) + +result = client.hgetall("default_mind:memories:Memory1") +expected_result = {"name":"Memory1", "evaluation":"0.0", "I":"[1, 1, 1]", "id":"0", "owner":""} + +assert "logical_time" in result +assert "timestamp" in result +del result["logical_time"] +del result["timestamp"] +assert result == expected_result + +memory1.set_info("INFO") +time.sleep(sleep_time) + +assert memory1.get_info() == "INFO" +assert mind2_memory1.get_info() == "INFO" + +mind2_memory1.set_info("INFO2") +time.sleep(sleep_time) + +assert memory1.get_info() == "INFO2" +assert mind2_memory1.get_info() == "INFO2" + +memory1.set_info(1) +time.sleep(sleep_time) + +assert memory1.get_info() == 1 +assert mind2_memory1.get_info() == 1 + +memory1.set_info("1") +time.sleep(sleep_time) + + +assert memory1.get_info() == "1" +assert mind2_memory1.get_info() == "1" + +memory1.set_info(True) +time.sleep(sleep_time) + +assert memory1.get_info() == True +assert mind2_memory1.get_info() == True + + +mind2_memory1.set_info([1,2,3]) +time.sleep(sleep_time) + +assert_array_almost_equal(memory1.get_info(), [1,2,3]) +assert_array_almost_equal(mind2_memory1.get_info(), [1,2,3]) + +mind.shutdown() +mind2.shutdown() + +time.sleep(sleep_time) +assert threading.active_count() == 1 \ No newline at end of file diff --git a/src/cst_python/memory_storage/logical_time.py b/src/cst_python/memory_storage/logical_time.py new file mode 100644 index 0000000..ade1729 --- /dev/null +++ b/src/cst_python/memory_storage/logical_time.py @@ -0,0 +1,87 @@ +import abc +import functools + +from typing import Self + +class LogicalTime(abc.ABC): + + @abc.abstractmethod + def increment(self) -> "LogicalTime": + ... + + + @abc.abstractmethod + def __str__(self) -> str: + ... + + @classmethod + @abc.abstractmethod + def from_str(cls, string:str) -> "LogicalTime": + ... + + @classmethod + @abc.abstractmethod + def syncronize(cls, time0:Self, time1:Self) -> "LogicalTime": + ... + + @abc.abstractmethod + def __eq__(self, other) -> bool: + ... + + @abc.abstractmethod + def __lt__(self, other) -> bool: + ... + + @abc.abstractmethod + def __le__(self, other) -> bool: + ... + + @abc.abstractmethod + def __gt__(self, other) -> bool: + ... + + @abc.abstractmethod + def __ge__(self, other) -> bool: + ... + + +@functools.total_ordering +class LamportTime(LogicalTime): + + #Methods that total_ordering will overwrite + __le__ = object.__lt__ # type: ignore + __gt__ = object.__gt__ # type: ignore + __ge__ = object.__ge__ # type: ignore + + + def __init__(self, initial_time:int=0): + super().__init__() + self._time = initial_time + + def increment(self) -> "LamportTime": + return LamportTime(initial_time=self._time+1) + + def __eq__(self, other) -> bool: + return self._time == other._time + + def __lt__(self, other) -> bool: + return self._time < other._time + + def __str__(self) -> str: + return str(self._time) + + @classmethod + def from_str(cls, string:str) -> "LamportTime": + return LamportTime(int(string)) + + @classmethod + def syncronize(cls, time0:Self, time1:Self) -> "LamportTime": + new_time = 0 + if time0 < time1: + new_time = time1._time + else: + new_time = time0._time + + new_time += 1 + + return LamportTime(new_time) \ No newline at end of file diff --git a/src/cst_python/memory_storage/memory_storage.py b/src/cst_python/memory_storage/memory_storage.py index aa309bd..36058b4 100644 --- a/src/cst_python/memory_storage/memory_storage.py +++ b/src/cst_python/memory_storage/memory_storage.py @@ -11,6 +11,7 @@ from cst_python.core.entities import Codelet, Mind, Memory, MemoryObject from .memory_encoder import MemoryEncoder +from .logical_time import LogicalTime, LamportTime logger = logging.getLogger("MemoryStorageCodelet") logger.setLevel(logging.DEBUG) @@ -30,7 +31,7 @@ def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[s self._client = redis.Redis(decode_responses=True) self._pubsub = self._client.pubsub() - self._pubsub_thread : redis.client.PubSubWorkerThread = self._pubsub.run_in_thread() + self._pubsub_thread : redis.client.PubSubWorkerThread = self._pubsub.run_in_thread(daemon=True) if node_name is None: node_name = "node" @@ -55,6 +56,7 @@ def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[s self._pubsub.subscribe(**{transfer_done_addr:self._handler_notify_transfer}) self._last_update : dict[str, int] = {} + self._memory_logical_time : dict[str, LogicalTime] = {} self._waiting_retrieve : set[str] = set() self._retrieve_executor = ThreadPoolExecutor(3) @@ -63,6 +65,8 @@ def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[s self._request = None + self._current_time = LamportTime() + def calculate_activation(self) -> None: #NOSONAR pass @@ -88,6 +92,7 @@ def proc(self) -> None: for memory_name in difference: memory = mind_memories[memory_name] self._memories[memory_name] = memory + self._memory_logical_time[memory_name] = self._current_time if self._client.exists(f"{self._mind_name}:memories:{memory_name}"): self._retrieve_executor.submit(self._retrieve_memory, memory) @@ -97,9 +102,11 @@ def proc(self) -> None: "evaluation" : 0.0, "I": "", "id" : 0, - "owner": self._node_name} + "owner": self._node_name, + "logical_time":str(self._current_time)} self._client.hset(f"{self._mind_name}:memories:{memory_name}", mapping=memory_impostor) + self._current_time = self._current_time.increment() subscribe_func = lambda _, name : self.update_memory(name) subscribe_func = functools.partial(subscribe_func, name=memory_name) @@ -110,10 +117,12 @@ def proc(self) -> None: for memory_name in to_update: if memory_name not in self._memories: del self._last_update[memory_name] + del self._memory_logical_time[memory_name] continue memory = self._memories[memory_name] if memory.get_timestamp() > self._last_update[memory_name]: + self._memory_logical_time[memory_name] = self._current_time self.update_memory(memory_name) def update_memory(self, memory_name:str) -> None: @@ -121,33 +130,37 @@ def update_memory(self, memory_name:str) -> None: if memory_name not in self._memories: self._pubsub.unsubscribe(f"{self._mind_name}:memories:{memory_name}:update") + return + + message_time_str = self._client.hget(f"{self._mind_name}:memories:{memory_name}", "logical_time") + assert message_time_str is not None + message_time = LamportTime.from_str(message_time_str) + memory_time = self._memory_logical_time[memory_name] - timestamp_result = self._client.hget(f"{self._mind_name}:memories:{memory_name}", "timestamp") - assert timestamp_result is not None - timestamp = float(timestamp_result) memory = self._memories[memory_name] - memory_timestamp = memory.get_timestamp() - if memory_timestamp < timestamp: + if memory_time < message_time: self._retrieve_executor.submit(self._retrieve_memory, memory) - elif memory_timestamp> timestamp: + elif memory_time > message_time: self._send_memory(memory) self._last_update[memory_name] = memory.get_timestamp() + def _send_memory(self, memory:Memory) -> None: memory_name = memory.get_name() logger.info(f"Sending memory [{memory_name}@{self._node_name}]") memory_dict = MemoryEncoder.to_dict(memory, jsonify_info=True) memory_dict["owner"] = "" + memory_dict["logical_time"] = str(self._memory_logical_time[memory_name]) self._client.hset(f"{self._mind_name}:memories:{memory_name}", mapping=memory_dict) self._client.publish(f"{self._mind_name}:memories:{memory_name}:update", "") - - self._last_update[memory_name] = memory.get_timestamp() + + self._current_time = self._current_time.increment() def _retrieve_memory(self, memory:Memory) -> None: @@ -174,8 +187,11 @@ def _retrieve_memory(self, memory:Memory) -> None: memory_dict = self._client.hgetall(f"{self._mind_name}:memories:{memory_name}") MemoryEncoder.load_memory(memory, memory_dict) + message_time = LamportTime.from_str(memory_dict["logical_time"]) + self._current_time = LamportTime.syncronize(self._current_time, message_time) self._last_update[memory_name] = memory.get_timestamp() + self._memory_logical_time[memory_name] = message_time self._waiting_retrieve.remove(memory_name) @@ -185,18 +201,30 @@ def _request_memory(self, memory_name:str, owner_name:str) -> None: request_addr = f"{self._mind_name}:nodes:{owner_name}:transfer_memory" request_dict = {"memory_name":memory_name, "node":self._node_name} - request = json.dumps(request_dict) + full_request_dict = {"request":request_dict, "logical_time":str(self._current_time)} + request = json.dumps(full_request_dict) self._client.publish(request_addr, request) def _handler_notify_transfer(self, message:dict[str,str]) -> None: - memory_name = message["data"] + data = data = json.loads(message["data"]) + if "logical_time" in data: + message_time = LamportTime.from_str(data["logical_time"]) + self._current_time = LamportTime.syncronize(message_time, self._current_time) + + memory_name = data["memory_name"] if memory_name in self._waiting_request_events: event = self._waiting_request_events[memory_name] event.set() del self._waiting_request_events[memory_name] + def _handler_transfer_memory(self, message:dict[str,str]) -> None: - request = json.loads(message["data"]) + data = json.loads(message["data"]) + if "logical_time" in data: + message_time = LamportTime.from_str(data["logical_time"]) + self._current_time = LamportTime.syncronize(message_time, self._current_time) + + request = data["request"] memory_name = request["memory_name"] requesting_node = request["node"] @@ -208,12 +236,24 @@ def _handler_transfer_memory(self, message:dict[str,str]) -> None: else: memory = MemoryObject() memory.set_name(memory_name) + + self._memory_logical_time[memory_name] = self._current_time self._send_memory(memory) + response = {"memory_name":memory_name, "logical_time":str(self._current_time)} + response_str = json.dumps(response) + response_addr = f"{self._mind_name}:nodes:{requesting_node}:transfer_done" - self._client.publish(response_addr, memory_name) + self._client.publish(response_addr, response_str) + + def stop(self): + self._pubsub_thread.stop() + self._retrieve_executor.shutdown(cancel_futures=True) + self._client.close() + super().stop() def __del__(self) -> None: self._pubsub_thread.stop() - self._retrieve_executor.shutdown(cancel_futures=True) \ No newline at end of file + self._retrieve_executor.shutdown(cancel_futures=True) + self._client.close() \ No newline at end of file From 65ddd975ebf2089d202ca59f909b7ad59d161e49 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:30:10 -0300 Subject: [PATCH 20/48] MS tests --- .../memory_storage/test_memory_storage.py | 221 ++++++++++++++++++ 1 file changed, 221 insertions(+) create mode 100644 tests/cst_python/memory_storage/test_memory_storage.py diff --git a/tests/cst_python/memory_storage/test_memory_storage.py b/tests/cst_python/memory_storage/test_memory_storage.py new file mode 100644 index 0000000..735ece7 --- /dev/null +++ b/tests/cst_python/memory_storage/test_memory_storage.py @@ -0,0 +1,221 @@ +import functools +import json +import threading +import threading +import time +import types +import unittest +from typing import Any + +import redis +from numpy.testing import assert_array_almost_equal + +from cst_python import MemoryObject, Mind +from cst_python.memory_storage import MemoryStorageCodelet + +sleep_time = 0.1 + + +def set_info(self:MemoryObject, value:Any, start_time:float) -> int: + self._info = value + + time_time = start_time + time.monotonic() + + self._timestamp = int(time_time*1000) + self._notify_memory_observers() + + return -1 + +def patch_memory_object(memory:MemoryObject, start_time:float) -> None: + set_info_fixedtime = functools.partial(set_info, start_time=start_time) + memory.set_info = types.MethodType(set_info_fixedtime, memory) + +client = redis.Redis(decode_responses=True) +try: + client.ping() + redis_reachable = True +except Exception: + redis_reachable = False + +@unittest.skipIf(not redis_reachable, "Redis server not running") +class TestMemoryStorage(unittest.TestCase): + + @classmethod + def setUpClass(cls): + cls.client = redis.Redis(decode_responses=True) + + + def setUp(self) -> None: + self.client.flushall() + + self.start_times = [0, 1e3] + + self.mind = Mind() + self.mind2 = Mind() + + def tearDown(self): + self.mind.shutdown() + self.mind2.shutdown() + + def test_patch_memory_object(self) -> None: + + memory1 = MemoryObject() + memory2 = MemoryObject() + + patch_memory_object(memory1, 0) + patch_memory_object(memory2, 1e3) + + memory1.set_info(0) + memory2.set_info(1) + + assert memory1.get_info() == 0 + assert memory2.get_info() == 1 + + assert (memory2.get_timestamp() - memory1.get_timestamp()) >= 1e6 + + def test_node_enter(self) -> None: + ms_codelet = MemoryStorageCodelet(self.mind) + ms_codelet.time_step = 50 + self.mind.insert_codelet(ms_codelet) + self.mind.start() + + time.sleep(sleep_time) + + assert ms_codelet._node_name == "node" + members = client.smembers("default_mind:nodes") + assert len(members) == 1 + assert "node" in members + + self.mind2 = Mind() + ms_codelet2 = MemoryStorageCodelet(self.mind2) + ms_codelet2.time_step = 50 + self.mind2.insert_codelet(ms_codelet2) + self.mind2.start() + + time.sleep(sleep_time) + + assert ms_codelet2._node_name == "node1" + members = client.smembers("default_mind:nodes") + assert len(members) == 2 + assert "node" in members + assert "node1" in members + + def test_memory_transfer(self) -> None: + + memory1 = self.mind.create_memory_object("Memory1", "INFO") + patch_memory_object(memory1, self.start_times[0]) + + ms_codelet = MemoryStorageCodelet(self.mind) + ms_codelet.time_step = 50 + self.mind.insert_codelet(ms_codelet) + + self.mind.start() + + time.sleep(sleep_time) + + assert self.client.exists("default_mind:memories:Memory1") >= 1 + + result = client.hgetall("default_mind:memories:Memory1") + expected_result = {"name":"Memory1", "evaluation":"0.0", "I":"", "id":"0", "owner":"node", "logical_time":"0"} + assert result == expected_result + + request = {"request":{"memory_name":"Memory1", "node":"node1"}, "logical_time":"0"} + request = json.dumps(request) + + self.client.publish("default_mind:nodes:node:transfer_memory", request) + + time.sleep(sleep_time) + + result = client.hgetall("default_mind:memories:Memory1") + expected_result = {"name":"Memory1", "evaluation":"0.0", "I":'"INFO"', "id":"0", "owner":""} + del result["logical_time"] + del result["timestamp"] + assert result == expected_result + + + def test_ms(self) -> None: + memory1 = self.mind.create_memory_object("Memory1", "") + patch_memory_object(memory1, self.start_times[0]) + + ms_codelet = MemoryStorageCodelet(self.mind) + ms_codelet.time_step = 50 + + self.mind.insert_codelet(ms_codelet) + self.mind.start() + + assert memory1.get_info() == "" + + memory1.set_info([1,1,1]) + + time.sleep(sleep_time) + + self.mind2_memory1 = self.mind2.create_memory_object("Memory1", "") + patch_memory_object(self.mind2_memory1, self.start_times[1]) + self.mind2_ms_codelet = MemoryStorageCodelet(self.mind2) + self.mind2_ms_codelet.time_step = 50 + self.mind2.insert_codelet(self.mind2_ms_codelet) + self.mind2.start() + + assert self.mind2_memory1.get_info() == "" + + time.sleep(sleep_time) + + assert_array_almost_equal(memory1.get_info(), [1,1,1]) + assert_array_almost_equal(self.mind2_memory1.get_info(), [1,1,1]) + + result = client.hgetall("default_mind:memories:Memory1") + expected_result = {"name":"Memory1", "evaluation":"0.0", "I":"[1, 1, 1]", "id":"0", "owner":""} + + assert "logical_time" in result + assert "timestamp" in result + del result["logical_time"] + del result["timestamp"] + assert result == expected_result + + memory1.set_info("INFO") + time.sleep(sleep_time) + + assert memory1.get_info() == "INFO" + assert self.mind2_memory1.get_info() == "INFO" + + + self.mind2_memory1.set_info("INFO2") + time.sleep(sleep_time) + + assert memory1.get_info() == "INFO2" + assert self.mind2_memory1.get_info() == "INFO2" + + memory1.set_info(1) + time.sleep(sleep_time) + + assert memory1.get_info() == 1 + assert self.mind2_memory1.get_info() == 1 + + memory1.set_info("1") + time.sleep(sleep_time) + + + assert memory1.get_info() == "1" + assert self.mind2_memory1.get_info() == "1" + + memory1.set_info(True) + time.sleep(sleep_time) + + assert memory1.get_info() == True + assert self.mind2_memory1.get_info() == True + + + self.mind2_memory1.set_info([1,2,3]) + time.sleep(sleep_time) + + assert_array_almost_equal(memory1.get_info(), [1,2,3]) + assert_array_almost_equal(self.mind2_memory1.get_info(), [1,2,3]) + + self.mind.shutdown() + self.mind2.shutdown() + + assert (self.mind2_memory1.get_timestamp() - memory1.get_timestamp()) >= 9e5 + + time.sleep(sleep_time) + assert threading.active_count() == 1 + \ No newline at end of file From 952245e974c06bb5121fbfdd69ee0af20ce835fb Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:40:34 -0300 Subject: [PATCH 21/48] Tests CI fix --- .github/workflows/test.yml | 15 ++++++++++++++- pyproject.toml | 1 + 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f8939f8..3edd0ad 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,6 +14,19 @@ jobs: matrix: os: [ubuntu-latest, windows-latest] python-version: ["3.10", "3.11", "3.12"] + + services: + # Label used to access the service container + redis: + if: ${{matrix.os == 'ubuntu-latest'}} + # Docker Hub image + image: redis + # Set health checks to wait until redis has started + options: >- + --health-cmd "redis-cli ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 steps: - uses: actions/checkout@v2 @@ -28,7 +41,7 @@ jobs: python3 -m pip install --upgrade pip python3 -m pip install pytest python3 -m pip install pytest-cov - python3 -m pip install -e .[tests,gym] + python3 -m pip install -e .[tests,gym,memory_storage] - name: Tests run: | diff --git a/pyproject.toml b/pyproject.toml index 9f9cd96..25058cf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ tests = ["mypy", "testbook", "ipython", "ipykernel", "numpy", "matplotlib", "typ doc_generation = ["sphinx", "sphinx_rtd_theme", "nbsphinx", "sphinx-mdinclude==0.5.4"] dev = ["cffconvert"] gym = ["gymnasium"] +memory_storage = ["redis"] [tool.setuptools] include-package-data = true From ad9a56d6aa3a5d7bc877fa92ceceed1c54c7f242 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:42:36 -0300 Subject: [PATCH 22/48] Update test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3edd0ad..f0ac788 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: services: # Label used to access the service container redis: - if: ${{matrix.os == 'ubuntu-latest'}} + # Docker Hub image image: redis # Set health checks to wait until redis has started From 04ae5d8662b1f9309468e2f5a6ec596ff06aed24 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:48:37 -0300 Subject: [PATCH 23/48] Test: fix Redis port --- .github/workflows/test.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f0ac788..37a2483 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,9 +27,12 @@ jobs: --health-interval 10s --health-timeout 5s --health-retries 5 + ports: + # Maps port 6379 on service container to the host + - 6379:6379 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 From 3e4bc9955fa3567b51020b224dfd3e385a69a3f1 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:54:02 -0300 Subject: [PATCH 24/48] Tests CI OS check --- .github/workflows/test.yml | 2 +- src/cst_python/memory_storage/logical_time.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 37a2483..4562f50 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: services: # Label used to access the service container redis: - + if: ${{matrix.os == 'ubuntu-latest'}} # Docker Hub image image: redis # Set health checks to wait until redis has started diff --git a/src/cst_python/memory_storage/logical_time.py b/src/cst_python/memory_storage/logical_time.py index ade1729..7e84bd9 100644 --- a/src/cst_python/memory_storage/logical_time.py +++ b/src/cst_python/memory_storage/logical_time.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import abc import functools From 2a17a4a1820e838c9bb788b103fb0dd8e4cf55a3 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 17:55:59 -0300 Subject: [PATCH 25/48] Update test.yml --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4562f50..4ce7617 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,7 @@ jobs: services: # Label used to access the service container redis: - if: ${{matrix.os == 'ubuntu-latest'}} + if: runner.os == 'Linux' # Docker Hub image image: redis # Set health checks to wait until redis has started From acb1e5898057d52a56ac6921970d5dde4e666fee Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:47:52 -0300 Subject: [PATCH 26/48] Test reusable test workflow --- .github/workflows/test.yml | 35 ++++----------------------- .github/workflows/test_inner.yml | 41 ++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 30 deletions(-) create mode 100644 .github/workflows/test_inner.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ce7617..b93d454 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -15,10 +15,10 @@ jobs: os: [ubuntu-latest, windows-latest] python-version: ["3.10", "3.11", "3.12"] + services: # Label used to access the service container redis: - if: runner.os == 'Linux' # Docker Hub image image: redis # Set health checks to wait until redis has started @@ -27,36 +27,11 @@ jobs: --health-interval 10s --health-timeout 5s --health-retries 5 - ports: - # Maps port 6379 on service container to the host - - 6379:6379 - steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python3 -m pip install --upgrade pip - python3 -m pip install pytest - python3 -m pip install pytest-cov - python3 -m pip install -e .[tests,gym,memory_storage] - - - name: Tests - run: | - pytest --cov=cst_python --cov-report json - shell: bash - - - if: ${{matrix.os == 'ubuntu-latest' && matrix.python-version == '3.12'}} - name: Upload coverage report - uses: actions/upload-artifact@v4 - with: - name: coverage_report - path: coverage.json + - uses: ./.github/workflows/test_inner.yml + with: + os: ${{ matrix.os }} + python-version: ${{ matrix.python-version }} coverage-check: runs-on: ubuntu-latest diff --git a/.github/workflows/test_inner.yml b/.github/workflows/test_inner.yml new file mode 100644 index 0000000..83cc1e2 --- /dev/null +++ b/.github/workflows/test_inner.yml @@ -0,0 +1,41 @@ +name: Test inner +run-name: ${{ github.actor }} is testing out GitHub Actions 🚀 +on: + workflow_call: + inputs: + os: + required: true + type: string + python-version: + required: true + type: string +jobs: + Explore-GitHub-Actions: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{inputs.python-version}} + uses: actions/setup-python@v2 + with: + python-version: ${{inputs.python-version}} + + - name: Install dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install pytest + python3 -m pip install pytest-cov + python3 -m pip install -e .[tests,gym] + + - name: Tests + run: | + pytest --cov=cst_python --cov-report json + shell: bash + + - if: ${{inputs.os == 'ubuntu-latest' && inputs.python-version == '3.12'}} + name: Upload coverage report + uses: actions/upload-artifact@v4 + with: + name: coverage_report + path: coverage.json + From b95149b164662d4a41ba354fa892b8581851e220 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:48:56 -0300 Subject: [PATCH 27/48] Update test.yml --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b93d454..9448d7c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,8 @@ jobs: --health-timeout 5s --health-retries 5 steps: + - uses: actions/checkout@v2 + - uses: ./.github/workflows/test_inner.yml with: os: ${{ matrix.os }} From 81942603df1a01e493caba2405bacfbe1670da9d Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:51:09 -0300 Subject: [PATCH 28/48] Update test.yml --- .github/workflows/test.yml | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9448d7c..30e7868 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ on: jobs: test: - + uses: ./.github/workflows/test_inner.yml runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -27,13 +27,6 @@ jobs: --health-interval 10s --health-timeout 5s --health-retries 5 - steps: - - uses: actions/checkout@v2 - - - uses: ./.github/workflows/test_inner.yml - with: - os: ${{ matrix.os }} - python-version: ${{ matrix.python-version }} coverage-check: runs-on: ubuntu-latest From 2f70a7673bfd1b0506290f2f4eda6fe60e6f9f55 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:51:48 -0300 Subject: [PATCH 29/48] Update test.yml --- .github/workflows/test.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 30e7868..80a999f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,6 +8,10 @@ on: jobs: test: uses: ./.github/workflows/test_inner.yml + with: + os: ${{ matrix.os }} + python-version: ${{ matrix.python-version }} + runs-on: ${{ matrix.os }} strategy: fail-fast: false @@ -27,7 +31,8 @@ jobs: --health-interval 10s --health-timeout 5s --health-retries 5 - + steps: + coverage-check: runs-on: ubuntu-latest needs: From 72958d7e0a7a117f81ff4282ab249ba9d73079f9 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:56:12 -0300 Subject: [PATCH 30/48] Try composite action --- .github/workflows/test.yml | 12 +++++--- .github/workflows/test_inner.yml | 41 ------------------------- .github/workflows/test_inner/action.yml | 39 +++++++++++++++++++++++ 3 files changed, 46 insertions(+), 46 deletions(-) delete mode 100644 .github/workflows/test_inner.yml create mode 100644 .github/workflows/test_inner/action.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 80a999f..b3442fe 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,10 +7,6 @@ on: jobs: test: - uses: ./.github/workflows/test_inner.yml - with: - os: ${{ matrix.os }} - python-version: ${{ matrix.python-version }} runs-on: ${{ matrix.os }} strategy: @@ -32,7 +28,13 @@ jobs: --health-timeout 5s --health-retries 5 steps: - + - uses: actions/checkout@v2 + + - uses: ./.github/workflows/test_inner + with: + os: ${{ matrix.os }} + python-version: ${{ matrix.python-version }} + coverage-check: runs-on: ubuntu-latest needs: diff --git a/.github/workflows/test_inner.yml b/.github/workflows/test_inner.yml deleted file mode 100644 index 83cc1e2..0000000 --- a/.github/workflows/test_inner.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Test inner -run-name: ${{ github.actor }} is testing out GitHub Actions 🚀 -on: - workflow_call: - inputs: - os: - required: true - type: string - python-version: - required: true - type: string -jobs: - Explore-GitHub-Actions: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - - name: Set up Python ${{inputs.python-version}} - uses: actions/setup-python@v2 - with: - python-version: ${{inputs.python-version}} - - - name: Install dependencies - run: | - python3 -m pip install --upgrade pip - python3 -m pip install pytest - python3 -m pip install pytest-cov - python3 -m pip install -e .[tests,gym] - - - name: Tests - run: | - pytest --cov=cst_python --cov-report json - shell: bash - - - if: ${{inputs.os == 'ubuntu-latest' && inputs.python-version == '3.12'}} - name: Upload coverage report - uses: actions/upload-artifact@v4 - with: - name: coverage_report - path: coverage.json - diff --git a/.github/workflows/test_inner/action.yml b/.github/workflows/test_inner/action.yml new file mode 100644 index 0000000..80e47a5 --- /dev/null +++ b/.github/workflows/test_inner/action.yml @@ -0,0 +1,39 @@ +name: Test inner + +inputs: + os: + required: true + type: string + python-version: + required: true + type: string + +runs: + using: composite + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{inputs.python-version}} + uses: actions/setup-python@v2 + with: + python-version: ${{inputs.python-version}} + + - name: Install dependencies + run: | + python3 -m pip install --upgrade pip + python3 -m pip install pytest + python3 -m pip install pytest-cov + python3 -m pip install -e .[tests,gym] + + - name: Tests + run: | + pytest --cov=cst_python --cov-report json + shell: bash + + - if: ${{inputs.os == 'ubuntu-latest' && inputs.python-version == '3.12'}} + name: Upload coverage report + uses: actions/upload-artifact@v4 + with: + name: coverage_report + path: coverage.json + From 0f0b5171f0fc7a856e75f3700788369f8eda6659 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:58:32 -0300 Subject: [PATCH 31/48] Update action.yml --- .github/workflows/test_inner/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_inner/action.yml b/.github/workflows/test_inner/action.yml index 80e47a5..2a027ac 100644 --- a/.github/workflows/test_inner/action.yml +++ b/.github/workflows/test_inner/action.yml @@ -26,9 +26,9 @@ runs: python3 -m pip install -e .[tests,gym] - name: Tests + shell: bash run: | pytest --cov=cst_python --cov-report json - shell: bash - if: ${{inputs.os == 'ubuntu-latest' && inputs.python-version == '3.12'}} name: Upload coverage report From acce3a7fb21bac9d451e35660f411541aeae1eae Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 18:59:29 -0300 Subject: [PATCH 32/48] Update action.yml --- .github/workflows/test_inner/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test_inner/action.yml b/.github/workflows/test_inner/action.yml index 2a027ac..8d18349 100644 --- a/.github/workflows/test_inner/action.yml +++ b/.github/workflows/test_inner/action.yml @@ -19,6 +19,7 @@ runs: python-version: ${{inputs.python-version}} - name: Install dependencies + shell: bash run: | python3 -m pip install --upgrade pip python3 -m pip install pytest From abda3d263fa5f08df3e724c527ad33c1c0fb5822 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:02:54 -0300 Subject: [PATCH 33/48] Fix inner action and OS specific test --- .github/workflows/test.yml | 28 ++++++++++++++++++------- .github/workflows/test_inner/action.yml | 2 +- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b3442fe..86b5cf4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,16 +6,13 @@ on: branches: [ dev, main ] jobs: - test: - - runs-on: ${{ matrix.os }} + test-linux: + runs-on: ubuntu-latest strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] python-version: ["3.10", "3.11", "3.12"] - services: # Label used to access the service container redis: @@ -27,18 +24,35 @@ jobs: --health-interval 10s --health-timeout 5s --health-retries 5 + + steps: + - uses: actions/checkout@v2 + + - uses: ./.github/workflows/test_inner + with: + os: ubuntu-latest + python-version: ${{ matrix.python-version }} + + + test-windows: + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.11", "3.12"] + steps: - uses: actions/checkout@v2 - uses: ./.github/workflows/test_inner with: - os: ${{ matrix.os }} + os: windows-latest python-version: ${{ matrix.python-version }} coverage-check: runs-on: ubuntu-latest needs: - - test + - test-linux steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/test_inner/action.yml b/.github/workflows/test_inner/action.yml index 8d18349..b370f7f 100644 --- a/.github/workflows/test_inner/action.yml +++ b/.github/workflows/test_inner/action.yml @@ -24,7 +24,7 @@ runs: python3 -m pip install --upgrade pip python3 -m pip install pytest python3 -m pip install pytest-cov - python3 -m pip install -e .[tests,gym] + python3 -m pip install -e .[tests,gym,redis] - name: Tests shell: bash From 45ffbbd6a534bb3db4e5e9f005ecd52a2bbbc407 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:04:19 -0300 Subject: [PATCH 34/48] Update action.yml --- .github/workflows/test_inner/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test_inner/action.yml b/.github/workflows/test_inner/action.yml index b370f7f..ca346d5 100644 --- a/.github/workflows/test_inner/action.yml +++ b/.github/workflows/test_inner/action.yml @@ -24,7 +24,7 @@ runs: python3 -m pip install --upgrade pip python3 -m pip install pytest python3 -m pip install pytest-cov - python3 -m pip install -e .[tests,gym,redis] + python3 -m pip install -e .[tests,gym,memory_storage] - name: Tests shell: bash From c88441800a17c3526a9d360b4ccaca482063eac0 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:07:24 -0300 Subject: [PATCH 35/48] Fix tests --- .github/workflows/test.yml | 5 ++++- src/cst_python/memory_storage/logical_time.py | 5 ++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 86b5cf4..38d8e2e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -24,7 +24,10 @@ jobs: --health-interval 10s --health-timeout 5s --health-retries 5 - + ports: + # Maps port 6379 on service container to the host + - 6379:6379 + steps: - uses: actions/checkout@v2 diff --git a/src/cst_python/memory_storage/logical_time.py b/src/cst_python/memory_storage/logical_time.py index 7e84bd9..86e835e 100644 --- a/src/cst_python/memory_storage/logical_time.py +++ b/src/cst_python/memory_storage/logical_time.py @@ -3,7 +3,6 @@ import abc import functools -from typing import Self class LogicalTime(abc.ABC): @@ -23,7 +22,7 @@ def from_str(cls, string:str) -> "LogicalTime": @classmethod @abc.abstractmethod - def syncronize(cls, time0:Self, time1:Self) -> "LogicalTime": + def syncronize(cls, time0, time1) -> "LogicalTime": ... @abc.abstractmethod @@ -77,7 +76,7 @@ def from_str(cls, string:str) -> "LamportTime": return LamportTime(int(string)) @classmethod - def syncronize(cls, time0:Self, time1:Self) -> "LamportTime": + def syncronize(cls, time0, time1) -> "LamportTime": new_time = 0 if time0 < time1: new_time = time1._time From 7e0a9b54d3577a3be03e67f7e14a3c0fc088809f Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Sun, 1 Dec 2024 19:12:37 -0300 Subject: [PATCH 36/48] Ignore dev folder in tests --- pytest.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytest.ini b/pytest.ini index da0d4c7..11ae325 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,2 +1,2 @@ [pytest] -addopts = --ignore=examples --ignore=docs --doctest-modules --ignore=generate_citation.py \ No newline at end of file +addopts = --ignore=examples --ignore=docs --doctest-modules --ignore=generate_citation.py --ignore=dev \ No newline at end of file From d5c4da5a459003cfc83f572568b0269f26d01b7a Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:37:32 -0300 Subject: [PATCH 37/48] Supports Redis client args --- src/cst_python/memory_storage/memory_storage.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/cst_python/memory_storage/memory_storage.py b/src/cst_python/memory_storage/memory_storage.py index 36058b4..1895bd5 100644 --- a/src/cst_python/memory_storage/memory_storage.py +++ b/src/cst_python/memory_storage/memory_storage.py @@ -17,7 +17,9 @@ logger.setLevel(logging.DEBUG) class MemoryStorageCodelet(Codelet): - def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[str]=None, request_timeout:float=500e-3) -> None: + def __init__(self, mind:Mind, + node_name:Optional[str]=None, mind_name:Optional[str]=None, + request_timeout:float=500e-3, **redis_args) -> None: super().__init__() self._mind = mind @@ -28,8 +30,11 @@ def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[s self._mind_name = cast(str, mind_name) self._memories : weakref.WeakValueDictionary[str, Memory] = weakref.WeakValueDictionary() + + if "decode_responses" in redis_args: + del redis_args["decode_responses"] - self._client = redis.Redis(decode_responses=True) + self._client = redis.Redis(decode_responses=True, **redis_args) self._pubsub = self._client.pubsub() self._pubsub_thread : redis.client.PubSubWorkerThread = self._pubsub.run_in_thread(daemon=True) From 0588cd5996be2aba2ac27e57decc7de5c575cf7f Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:37:51 -0300 Subject: [PATCH 38/48] Redis args test --- .../memory_storage/test_memory_storage.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/cst_python/memory_storage/test_memory_storage.py b/tests/cst_python/memory_storage/test_memory_storage.py index 735ece7..f23c178 100644 --- a/tests/cst_python/memory_storage/test_memory_storage.py +++ b/tests/cst_python/memory_storage/test_memory_storage.py @@ -100,6 +100,19 @@ def test_node_enter(self) -> None: assert "node" in members assert "node1" in members + def test_redis_args(self) -> None: + redis_args = {"host":"localhost", "port":6379} + ms_codelet = MemoryStorageCodelet(self.mind, **redis_args) + ms_codelet.time_step = 50 + self.mind.insert_codelet(ms_codelet) + self.mind.start() + + time.sleep(sleep_time) + + members = client.smembers("default_mind:nodes") + assert len(members) == 1 + assert "node" in members + def test_memory_transfer(self) -> None: memory1 = self.mind.create_memory_object("Memory1", "INFO") From abb63f272b5a8f1eac8d0f6de332a546a1f22fce Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:37:58 -0300 Subject: [PATCH 39/48] MS documentation --- src/cst_python/memory_storage/logical_time.py | 43 +++++++++++- .../memory_storage/memory_encoder.py | 26 ++++++- .../memory_storage/memory_storage.py | 70 ++++++++++++++++++- 3 files changed, 135 insertions(+), 4 deletions(-) diff --git a/src/cst_python/memory_storage/logical_time.py b/src/cst_python/memory_storage/logical_time.py index 86e835e..d8ce2cd 100644 --- a/src/cst_python/memory_storage/logical_time.py +++ b/src/cst_python/memory_storage/logical_time.py @@ -5,9 +5,18 @@ class LogicalTime(abc.ABC): + ''' + A logical time for distributed communication. + ''' @abc.abstractmethod def increment(self) -> "LogicalTime": + ''' + Returns a time with the self time incremented by one. + + Returns: + LogicalTime: incremented time. + ''' ... @@ -18,11 +27,31 @@ def __str__(self) -> str: @classmethod @abc.abstractmethod def from_str(cls, string:str) -> "LogicalTime": + ''' + Creates a instance from a string. + + Args: + string (str): String to create time, + generated with str(LogicalTime). + + Returns: + LogicalTime: Created time. + ''' ... @classmethod @abc.abstractmethod - def syncronize(cls, time0, time1) -> "LogicalTime": + def syncronize(cls, time0:"LogicalTime", time1:"LogicalTime") -> "LogicalTime": + ''' + Compares two times, and return the current time. + + Args: + time0 (LogicalTime): first time to compare. + time1 (LogicalTime): second time to compare. + + Returns: + LogicalTime: current time. + ''' ... @abc.abstractmethod @@ -48,6 +77,9 @@ def __ge__(self, other) -> bool: @functools.total_ordering class LamportTime(LogicalTime): + ''' + Logical time implementation using Lamport times. + ''' #Methods that total_ordering will overwrite __le__ = object.__lt__ # type: ignore @@ -56,6 +88,12 @@ class LamportTime(LogicalTime): def __init__(self, initial_time:int=0): + ''' + LamportTime initializer. + + Args: + initial_time (int, optional): time to start the clock. Defaults to 0. + ''' super().__init__() self._time = initial_time @@ -77,6 +115,9 @@ def from_str(cls, string:str) -> "LamportTime": @classmethod def syncronize(cls, time0, time1) -> "LamportTime": + if not (isinstance(time0, LamportTime) and isinstance(time1, LamportTime)): + raise ValueError("LamportTime can only synchonize LamportTime instances") + new_time = 0 if time0 < time1: new_time = time1._time diff --git a/src/cst_python/memory_storage/memory_encoder.py b/src/cst_python/memory_storage/memory_encoder.py index 2a58dca..030b8ae 100644 --- a/src/cst_python/memory_storage/memory_encoder.py +++ b/src/cst_python/memory_storage/memory_encoder.py @@ -4,11 +4,25 @@ from cst_python.core.entities import Memory class MemoryEncoder(json.JSONEncoder): + ''' + Encodes and decodes Memories. + ''' def default(self, memory:Memory): return MemoryEncoder.to_dict(memory) @staticmethod - def to_dict(memory:Memory, jsonify_info:bool=False): + def to_dict(memory:Memory, jsonify_info:bool=False) -> dict[str, Any]: + ''' + Encodes a memory to a dict. + + Args: + memory (Memory): memory to encode. + jsonify_info (bool, optional): if True, dumps the info to JSON + before return. Defaults to False. + + Returns: + dict[str, Any]: the encoded memory. + ''' data = { "timestamp": memory.get_timestamp(), "evaluation": memory.get_evaluation(), @@ -22,8 +36,16 @@ def to_dict(memory:Memory, jsonify_info:bool=False): return data + @staticmethod - def load_memory(memory:Memory, memory_dict:dict[str,Any], load_json:bool=True): + def load_memory(memory:Memory, memory_dict:dict[str,Any]): + ''' + Load a memory from a dict. + + Args: + memory (Memory): memory to store the loaded info. + memory_dict (dict[str,Any]): dict encoded memory. + ''' memory.set_evaluation(float(memory_dict["evaluation"])) memory.set_id(int(memory_dict["id"])) diff --git a/src/cst_python/memory_storage/memory_storage.py b/src/cst_python/memory_storage/memory_storage.py index 1895bd5..cf8d22c 100644 --- a/src/cst_python/memory_storage/memory_storage.py +++ b/src/cst_python/memory_storage/memory_storage.py @@ -17,9 +17,30 @@ logger.setLevel(logging.DEBUG) class MemoryStorageCodelet(Codelet): + ''' + Synchonizes local memories with a Redis database. + + When using MemoryStorage, each local CST instance is called a node. + + The collection of synchonized nodes is a mind. + A single Redis instance can support multiple minds with unique names + ''' + def __init__(self, mind:Mind, node_name:Optional[str]=None, mind_name:Optional[str]=None, request_timeout:float=500e-3, **redis_args) -> None: + ''' + MemoryStorageCodelet initializer. + + Args: + mind (Mind): agent mind, used to monitor memories. + node_name (Optional[str], optional): name of the local node in the network. + If None, creates a unique name with 'node{int}'. Defaults to None. + mind_name (Optional[str], optional): name of the network mind. + If None, uses 'default_mind'. Defaults to None. + request_timeout (float, optional): time before timeout when + requesting a memory synchonization. Defaults to 500e-3. + ''' super().__init__() self._mind = mind @@ -38,6 +59,7 @@ def __init__(self, mind:Mind, self._pubsub = self._client.pubsub() self._pubsub_thread : redis.client.PubSubWorkerThread = self._pubsub.run_in_thread(daemon=True) + # Creates node name if node_name is None: node_name = "node" base_name = node_name @@ -54,12 +76,15 @@ def __init__(self, mind:Mind, self._client.sadd(f"{mind_name}:nodes", node_name) + # Creates transfer channels subscription transfer_service_addr = f"{self._mind_name}:nodes:{node_name}:transfer_memory" self._pubsub.subscribe(**{transfer_service_addr:self._handler_transfer_memory}) transfer_done_addr = f"{self._mind_name}:nodes:{node_name}:transfer_done" self._pubsub.subscribe(**{transfer_done_addr:self._handler_notify_transfer}) + # Initalize variables + self._last_update : dict[str, int] = {} self._memory_logical_time : dict[str, LogicalTime] = {} self._waiting_retrieve : set[str] = set() @@ -131,6 +156,16 @@ def proc(self) -> None: self.update_memory(memory_name) def update_memory(self, memory_name:str) -> None: + ''' + Updates a memory, sending or retrieving the memory data + to/from the database. + + Performs a time comparison with the local data and storage + data to decide whether to send or retrieve the data. + + Args: + memory_name (str): name of the memory to synchonize. + ''' logger.info(f"Updating memory [{memory_name}@{self._node_name}]") if memory_name not in self._memories: @@ -154,10 +189,16 @@ def update_memory(self, memory_name:str) -> None: def _send_memory(self, memory:Memory) -> None: + ''' + Sends a memory data to the storage. + + Args: + memory (Memory): memory to send. + ''' memory_name = memory.get_name() logger.info(f"Sending memory [{memory_name}@{self._node_name}]") - memory_dict = MemoryEncoder.to_dict(memory, jsonify_info=True) + memory_dict = cast(dict[str|bytes, int|float|str], MemoryEncoder.to_dict(memory, jsonify_info=True)) memory_dict["owner"] = "" memory_dict["logical_time"] = str(self._memory_logical_time[memory_name]) @@ -169,6 +210,14 @@ def _send_memory(self, memory:Memory) -> None: def _retrieve_memory(self, memory:Memory) -> None: + ''' + Retrieves a memory data from the storage. + + Blocks the application, it is advisable to use a separate thread to call the method. + + Args: + memory (Memory): memory to retrieve data. + ''' memory_name = memory.get_name() logger.info(f"Retrieving memory [{memory_name}@{self._node_name}]") @@ -201,6 +250,13 @@ def _retrieve_memory(self, memory:Memory) -> None: self._waiting_retrieve.remove(memory_name) def _request_memory(self, memory_name:str, owner_name:str) -> None: + ''' + Requests another node to send its local memory to storage. + + Args: + memory_name (str): name of the memory to request. + owner_name (str): node owning the memory. + ''' logger.info(f"Requesting memory [{memory_name}@{owner_name} to {self._node_name}]") request_addr = f"{self._mind_name}:nodes:{owner_name}:transfer_memory" @@ -211,6 +267,12 @@ def _request_memory(self, memory_name:str, owner_name:str) -> None: self._client.publish(request_addr, request) def _handler_notify_transfer(self, message:dict[str,str]) -> None: + ''' + Handles a message in the notify transfer channel. + + Args: + message (dict[str,str]): message received in the channel. + ''' data = data = json.loads(message["data"]) if "logical_time" in data: message_time = LamportTime.from_str(data["logical_time"]) @@ -224,6 +286,12 @@ def _handler_notify_transfer(self, message:dict[str,str]) -> None: def _handler_transfer_memory(self, message:dict[str,str]) -> None: + ''' + Handles a message in the transfer memory channel. + + Args: + message (dict[str,str]): message received in the channel. + ''' data = json.loads(message["data"]) if "logical_time" in data: message_time = LamportTime.from_str(data["logical_time"]) From ce45936738570ea34e3db3e976c1ac980f9baa6e Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:30:56 -0300 Subject: [PATCH 40/48] MS documentation --- docs/index.rst | 7 +++ docs/src/Memory Storage.md | 56 +++++++++++++++++++ .../memory_storage/memory_storage.py | 5 +- .../test_activation_and_monitoring.py | 2 +- 4 files changed, 67 insertions(+), 3 deletions(-) create mode 100644 docs/src/Memory Storage.md diff --git a/docs/index.rst b/docs/index.rst index a8afed6..d7af583 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,6 +9,13 @@ self src/Differences from CST-Java.md +.. toctree:: + :maxdepth: 4 + :caption: Features + :hidden: + + src/Memory Storage.md + .. toctree:: :maxdepth: 1 :caption: Examples diff --git a/docs/src/Memory Storage.md b/docs/src/Memory Storage.md new file mode 100644 index 0000000..07e407e --- /dev/null +++ b/docs/src/Memory Storage.md @@ -0,0 +1,56 @@ +# Memory Storage + +Memory Storage is a CST synchonization mechanism to synchonize memories across multiple CST instances, whether they are CST-Java or CST-Python instances. + +Synchronization is performed using a Redis server. A server reachable by all instances must be running to use Memory Storage. Redis can be installed on Linux and Windows (using WSL) using [Redis documentation](https://redis.io/docs/latest/operate/oss_and_stack/install/install-redis/). + +When using Memory Storage, each local CST instance is called a node. Memories with the same name in participating nodes are synchronized. Only memories that are used by more than one node are stored in the storage. Other memories are only indicated as existing in the storage, and can be transferred later if another node starts using them. + +The collection of synchonized nodes is a mind, and a single Redis instance can support multiple minds with unique names. + +To use it, you just need to add a :class:`Memory Storage Codelet` to each mind participating in the network. + +## Protocol + +This section presents the messages used for the operation of the Memory Storage. It is intended only for CST developers. + +### Mind nodes + +`:nodes` is a Redis set containing all the nodes names. When a node enters the network, it needs to add it's unique name to this set. + +### Memory Lifecycle and Storage + +Initially, no memory is stored in Memory Storage. In this case, memories are stored without data, only indicating which node it is stored in, called the owner. When another node creates a memory that already exists in Memory Storage, it checks its owner. If it is a node, it requests the transfer of the memory. Once transferred, the memory is stored in Memory Storage. + +Each memory in the storage is stored in a Redis hash `:memories:` with the keys: + +- `evaluation`: memory evaluation +- `I`: memory info +- `id`: memory id +- `owner`: current node owning the memory. If is in the storage, the value is set to "". +- `logical_time`: time when the memory was stored. + +When a local Memory Storage Codelet detects a new created memory, it checks if a corresponding hash `:memories:` exists. If so, it checks the owner. If it is a node, it requests the transfer of memory. After ensuring that the memory is in the storage, it performs the synchronization. If the created memory does not have a corresponding memory in the storage, the node sends an impostor containing only the owner set as its own name. + +#### Memory Transfer + +Each node subscribes to two Redis channels to perform memory transfers: + +- `:nodes::transfer_memory`: receives transfer requests. Each request is a string containing a JSON. It must contain the "request" field, with subfields "memory_name" indicating which memory should be transferred, and "node" indicating which node requests the transfer. Optionally, it can contain a "logical_time" field with the time of the requesting node when making the request. After making the transfer, the node responds by sending a message on the requesting node's transfer done channel. +- `:nodes:transfer_done:` Receives messages indicating that a requested memory transfer has been performed. The message is a string containing a JSON, with a "request" field and a "memory_name" subfield indicating which memory was transferred. Optionally, it can contain a "logical_time" field indicating the time when the transfer was performed. + +A node waits for a transfer until a certain timeout. If it does not receive a response, it sends its own version of the memory to the Memory Storage. + +Transferred memories are marked with `owner=""`, and are synchronized with each Memory Storage Codelet cycle on all nodes that have a version of this memory. + +All nodes that have memory in the storage also subscribe to the memory update channel, `< mind_name>:memories::update`. + +#### Memory Update + +When a node is synchronizing a memory, it checks each Memory Storage Codelet cycle to see if it has been updated locally, comparing the local memory timestamp with the last update. If it has been updated, it initiates an update. + +In an update, the logical memory time in the storage is first obtained and compared with the logical memory time of the local memory. If the version in the storage is more recent, it retrieves the remote data. If the local version is more recent, it sends it to the storage and sends a message on the memory update channel. + +Messages received on the memory update channel also initiate updates. + +Memory Storage attempts to ensure that the most recent versions of memory are maintained, but overwrites can occur if a memory is updated concurrently on two nodes. Verification of which memory is more recent is done using logical clocks. \ No newline at end of file diff --git a/src/cst_python/memory_storage/memory_storage.py b/src/cst_python/memory_storage/memory_storage.py index cf8d22c..b4f52a4 100644 --- a/src/cst_python/memory_storage/memory_storage.py +++ b/src/cst_python/memory_storage/memory_storage.py @@ -18,9 +18,10 @@ class MemoryStorageCodelet(Codelet): ''' - Synchonizes local memories with a Redis database. + Synchonizes local memories with a Redis database. - When using MemoryStorage, each local CST instance is called a node. + When using MemoryStorage, each local CST instance is called a node. + Memories with the same name in participating nodes are synchronized. The collection of synchonized nodes is a mind. A single Redis instance can support multiple minds with unique names diff --git a/tests/examples/test_activation_and_monitoring.py b/tests/examples/test_activation_and_monitoring.py index 0fc199b..8e8263c 100644 --- a/tests/examples/test_activation_and_monitoring.py +++ b/tests/examples/test_activation_and_monitoring.py @@ -27,7 +27,7 @@ def test_activation(tb :TestbookNotebookClient): else: expected_sensory = input_value * 10 - assert math.isclose(sensory_output, expected_sensory, abs_tol=0.3) + assert math.isclose(sensory_output, expected_sensory, abs_tol=0.35) last_sensory_output = sensory_output From d15a242734f4c26327e6a0cc9a4d62075017913c Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:51:51 -0300 Subject: [PATCH 41/48] Memory Storage example --- docs/src/Memory Storage.md | 2 +- examples/Memory Storage.ipynb | 310 ++++++++++++++++++++++++++++++++++ examples/README.md | 1 + 3 files changed, 312 insertions(+), 1 deletion(-) create mode 100644 examples/Memory Storage.ipynb diff --git a/docs/src/Memory Storage.md b/docs/src/Memory Storage.md index 07e407e..972e907 100644 --- a/docs/src/Memory Storage.md +++ b/docs/src/Memory Storage.md @@ -8,7 +8,7 @@ When using Memory Storage, each local CST instance is called a node. Memories wi The collection of synchonized nodes is a mind, and a single Redis instance can support multiple minds with unique names. -To use it, you just need to add a :class:`Memory Storage Codelet` to each mind participating in the network. +To use it, you just need to add a :class:`Memory Storage Codelet` to each mind participating in the network. Check the [Memory Storage Example](https://h-iaac.github.io/CST-Python/_build/html/_examples/Memory%20Storage.html) for how to use it. ## Protocol diff --git a/examples/Memory Storage.ipynb b/examples/Memory Storage.ipynb new file mode 100644 index 0000000..c63698c --- /dev/null +++ b/examples/Memory Storage.ipynb @@ -0,0 +1,310 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Memory Storage\n", + "\n", + "[![Open in Colab](https://img.shields.io/badge/Open%20in%20Colab-F9AB00?style=for-the-badge&logo=googlecolab&color=525252)](https://colab.research.google.com/github/H-IAAC/CST-Python/blob/main/examples/Memory%20Storage.ipynb) [![Open in Github](https://img.shields.io/badge/Open%20in%20Github-100000?style=for-the-badge&logo=github&logoColor=white)](https://github.com/H-IAAC/CST-Python/blob/main/examples/Memory%20Storage.ipynb)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example, we gonna use the Memory Storage to synchonize memories across two CST instances." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "First, we need to ensure that `cst_python` and `redis` is installed:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " import cst_python as cst\n", + " import redis\n", + "except:\n", + " !python3 -m pip install cst_python[memory_storage] " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We also need to have a running Redis server. If you're running this notebook in Google Colab, the following cell will install and start Redis:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "try:\n", + " import google.colab\n", + " IN_COLAB = True\n", + "except:\n", + " IN_COLAB = False\n", + "\n", + "if IN_COLAB:\n", + " # Install Redis\n", + " !curl -fsSL https://packages.redis.io/redis-stack/redis-stack-server-6.2.6-v7.focal.x86_64.tar.gz -o redis-stack-server.tar.gz \n", + " !tar -xvf redis-stack-server.tar.gz\n", + "\n", + " # Start Redis server\n", + " !./redis-stack-server-6.2.6-v7/bin/redis-stack-server --daemonize yes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can then import the modules and get started." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "import time\n", + "\n", + "import cst_python as cst\n", + "from cst_python.memory_storage import MemoryStorageCodelet" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Our example will involve two CST instances, two nodes, with a memory called \"MyMemory\" being synchronized between them. Let's start by creating the instance's mind and its memory:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "firstnode_mind = cst.Mind()\n", + "firstnode_memory = firstnode_mind.create_memory_object(\"MyMemory\", \"\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To use memory storage, each node needs to have a MemoryStorageCodelet running in its mind. Let's create the codelet, add the mind and start the mind:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "firstnode_mscodelet = MemoryStorageCodelet(firstnode_mind)\n", + "firstnode_mscodelet.time_step = 100\n", + "firstnode_mind.insert_codelet(firstnode_mscodelet)\n", + "\n", + "firstnode_mind.start()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's initialize the memory with an info:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'First node info'" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "firstnode_memory.set_info(\"First node info\")\n", + "firstnode_memory.get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And create the mind and memory of the second node. Notice that its memory is not initialized:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "''" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "secondnode_mind = cst.Mind()\n", + "secondnode_memory = secondnode_mind.create_memory_object(\"MyMemory\", \"\")\n", + "\n", + "secondnode_memory.get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We then create the MemoryStorage of the second instance and start it:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "secondnode_mscodelet = MemoryStorageCodelet(secondnode_mind)\n", + "secondnode_mscodelet.time_step = 100\n", + "secondnode_mind.insert_codelet(secondnode_mscodelet)\n", + "\n", + "secondnode_mind.start()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We wait a while to ensure that the codelet will be executed, and we check the data in the second instance:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "time.sleep(1)\n", + "\n", + "secondnode_memory.get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can see that the data has been synchronized!\n", + "\n", + "The process works both ways:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Second node info'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "secondnode_memory.set_info(\"Second node info\")\n", + "time.sleep(1)\n", + "firstnode_memory.get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And it can contain data of a few different types:" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[1, 2, 3]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "firstnode_memory.set_info([1, 2, 3])\n", + "time.sleep(1)\n", + "secondnode_memory.get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this example, we used two CST-Python instances in the same machine. But, it could be a CST-Python with a CST-Java instance, or instances in different machines, or even more than two instances.\n", + "\n", + "The [Memory Storage Documentation](https://h-iaac.github.io/CST-Python/_build/html/src/Memory%20Storage.html) contains more information about how the Memory Storage works." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/examples/README.md b/examples/README.md index 621060e..2da43a4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -6,4 +6,5 @@ Here we have some examples of how to use the CST-Python: - [Implementing a Architecture](https://h-iaac.github.io/CST-Python/_build/html/_examples/Implementing%20a%20Architecture.html): how to implement a cognitive architecture using CST-Python. - [Publisher-Subscriber](https://h-iaac.github.io/CST-Python/_build/html/_examples/Publisher-Subscriber.html): using the publisher-subscriber mechanism for synchronous codelets. - [Activation and Monitoring](https://h-iaac.github.io/CST-Python/_build/html/_examples/Activation%20and%20Monitoring.html): using codelet's activation value and monitoring the agent. +- [Memory Storage](https://h-iaac.github.io/CST-Python/_build/html/_examples/Memory%20Storage.html): how to use the CST synchronization mechanism to use multiple instances on the same agent. - [Gymnasium Integration](https://h-iaac.github.io/CST-Python/_build/html/_examples/Gymnasium%20Integration.html): using gymnasium environments with CST. \ No newline at end of file From 64b2f3d1dc82349d04b27ccd8692a3d71f191124 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:57:09 -0300 Subject: [PATCH 42/48] Memory Storage example test --- examples/Memory Storage.ipynb | 65 ++++++++++++++++++++------- tests/examples/test_memory_storage.py | 26 +++++++++++ 2 files changed, 74 insertions(+), 17 deletions(-) create mode 100644 tests/examples/test_memory_storage.py diff --git a/examples/Memory Storage.ipynb b/examples/Memory Storage.ipynb index c63698c..7aced8b 100644 --- a/examples/Memory Storage.ipynb +++ b/examples/Memory Storage.ipynb @@ -92,7 +92,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -114,7 +114,7 @@ "outputs": [], "source": [ "firstnode_mscodelet = MemoryStorageCodelet(firstnode_mind)\n", - "firstnode_mscodelet.time_step = 100\n", + "firstnode_mscodelet.time_step = 50\n", "firstnode_mind.insert_codelet(firstnode_mscodelet)\n", "\n", "firstnode_mind.start()" @@ -130,7 +130,11 @@ { "cell_type": "code", "execution_count": 6, - "metadata": {}, + "metadata": { + "tags": [ + "info1" + ] + }, "outputs": [ { "data": { @@ -158,7 +162,11 @@ { "cell_type": "code", "execution_count": 7, - "metadata": {}, + "metadata": { + "tags": [ + "info2" + ] + }, "outputs": [ { "data": { @@ -192,7 +200,7 @@ "outputs": [], "source": [ "secondnode_mscodelet = MemoryStorageCodelet(secondnode_mind)\n", - "secondnode_mscodelet.time_step = 100\n", + "secondnode_mscodelet.time_step = 50\n", "secondnode_mind.insert_codelet(secondnode_mscodelet)\n", "\n", "secondnode_mind.start()" @@ -207,11 +215,26 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 9, + "metadata": { + "tags": [ + "info3" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "'First node info'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "time.sleep(1)\n", + "time.sleep(0.150)\n", "\n", "secondnode_memory.get_info()" ] @@ -227,8 +250,12 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": {}, + "execution_count": 10, + "metadata": { + "tags": [ + "info4" + ] + }, "outputs": [ { "data": { @@ -236,14 +263,14 @@ "'Second node info'" ] }, - "execution_count": 11, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "secondnode_memory.set_info(\"Second node info\")\n", - "time.sleep(1)\n", + "time.sleep(0.150)\n", "firstnode_memory.get_info()" ] }, @@ -256,8 +283,12 @@ }, { "cell_type": "code", - "execution_count": 12, - "metadata": {}, + "execution_count": 11, + "metadata": { + "tags": [ + "info5" + ] + }, "outputs": [ { "data": { @@ -265,14 +296,14 @@ "[1, 2, 3]" ] }, - "execution_count": 12, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "firstnode_memory.set_info([1, 2, 3])\n", - "time.sleep(1)\n", + "time.sleep(0.150)\n", "secondnode_memory.get_info()" ] }, diff --git a/tests/examples/test_memory_storage.py b/tests/examples/test_memory_storage.py new file mode 100644 index 0000000..0e61408 --- /dev/null +++ b/tests/examples/test_memory_storage.py @@ -0,0 +1,26 @@ +import os +import re + +from testbook import testbook +from testbook.client import TestbookNotebookClient + +from ..utils import get_examples_path + +examples_path = get_examples_path() + +@testbook(os.path.join(examples_path, "Memory Storage.ipynb"), execute=True) +def test_gym_integration(tb :TestbookNotebookClient): + + expected_result = {"info1":"'First node info'", + "info2":"''", + "info3":"'First node info'", + "info4":"'Second node info'", + "info5":"[1, 2, 3]", + } + + for tag in expected_result: + result = tb.cell_output_text(tag) + + assert result == expected_result[tag] + + From cfc55ab4fe605a996b6d55c70f6d8f742e892016 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:26:03 -0300 Subject: [PATCH 43/48] Test Lamport Time --- .../memory_storage/test_lamport_time.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 tests/cst_python/memory_storage/test_lamport_time.py diff --git a/tests/cst_python/memory_storage/test_lamport_time.py b/tests/cst_python/memory_storage/test_lamport_time.py new file mode 100644 index 0000000..3c80c5d --- /dev/null +++ b/tests/cst_python/memory_storage/test_lamport_time.py @@ -0,0 +1,46 @@ +import functools +import json +import threading +import threading +import time +import types +import unittest +from typing import Any + +from cst_python.memory_storage.logical_time import LamportTime + +class TestLamportTime(unittest.TestCase): + + def test_initial_time(self): + time0 = LamportTime(initial_time=123) + + assert time0._time == 123 + + def test_str(self): + time0 = LamportTime(initial_time=456) + + assert str(time0) == "456" + + def test_from_str(self): + time0 = LamportTime(initial_time=987) + + assert LamportTime.from_str(str(time0)) == time0 + + def test_increment(self): + time0 = LamportTime() + time0_time = time0._time + + time1 = time0.increment() + + assert time0._time == time0_time + assert time1._time == time0_time+1 + + def test_synchonize(self): + time0 = LamportTime(initial_time=-10) + time1 = LamportTime(initial_time=55) + + time_s = LamportTime.syncronize(time0, time1) + + assert time_s > time0 + assert time_s > time1 + assert time_s._time == 56 \ No newline at end of file From df47552c4f611980cee8b37304e64795151ba0c6 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:26:11 -0300 Subject: [PATCH 44/48] Fix tests MS --- examples/Memory Storage.ipynb | 55 ++++++++++++++----- .../memory_storage/test_memory_storage.py | 4 +- tests/examples/test_memory_storage.py | 13 ++++- 3 files changed, 55 insertions(+), 17 deletions(-) diff --git a/examples/Memory Storage.ipynb b/examples/Memory Storage.ipynb index 7aced8b..b124fe4 100644 --- a/examples/Memory Storage.ipynb +++ b/examples/Memory Storage.ipynb @@ -79,6 +79,8 @@ "source": [ "import time\n", "\n", + "import redis\n", + "\n", "import cst_python as cst\n", "from cst_python.memory_storage import MemoryStorageCodelet" ] @@ -87,13 +89,40 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Our example will involve two CST instances, two nodes, with a memory called \"MyMemory\" being synchronized between them. Let's start by creating the instance's mind and its memory:" + "We gonna clean the Redis database to ensure the example works correctly:" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "redis.Redis().flushall()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Our example will involve two CST instances, two nodes, with a memory called \"MyMemory\" being synchronized between them. Let's start by creating the instance's mind and its memory:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, "outputs": [], "source": [ "firstnode_mind = cst.Mind()\n", @@ -109,7 +138,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -129,7 +158,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": { "tags": [ "info1" @@ -142,7 +171,7 @@ "'First node info'" ] }, - "execution_count": 6, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" } @@ -161,7 +190,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": { "tags": [ "info2" @@ -174,7 +203,7 @@ "''" ] }, - "execution_count": 7, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -195,7 +224,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -215,7 +244,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": { "tags": [ "info3" @@ -228,7 +257,7 @@ "'First node info'" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -250,7 +279,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": { "tags": [ "info4" @@ -263,7 +292,7 @@ "'Second node info'" ] }, - "execution_count": 10, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -283,7 +312,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": { "tags": [ "info5" @@ -296,7 +325,7 @@ "[1, 2, 3]" ] }, - "execution_count": 11, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } diff --git a/tests/cst_python/memory_storage/test_memory_storage.py b/tests/cst_python/memory_storage/test_memory_storage.py index f23c178..1175f87 100644 --- a/tests/cst_python/memory_storage/test_memory_storage.py +++ b/tests/cst_python/memory_storage/test_memory_storage.py @@ -13,7 +13,7 @@ from cst_python import MemoryObject, Mind from cst_python.memory_storage import MemoryStorageCodelet -sleep_time = 0.1 +sleep_time = 0.75 def set_info(self:MemoryObject, value:Any, start_time:float) -> int: @@ -57,6 +57,8 @@ def tearDown(self): self.mind.shutdown() self.mind2.shutdown() + self.client.flushall() + def test_patch_memory_object(self) -> None: memory1 = MemoryObject() diff --git a/tests/examples/test_memory_storage.py b/tests/examples/test_memory_storage.py index 0e61408..9aea827 100644 --- a/tests/examples/test_memory_storage.py +++ b/tests/examples/test_memory_storage.py @@ -4,16 +4,21 @@ from testbook import testbook from testbook.client import TestbookNotebookClient -from ..utils import get_examples_path +if __name__ != "__main__": + from ..utils import get_examples_path -examples_path = get_examples_path() + examples_path = get_examples_path() + +else: + + examples_path = "examples" @testbook(os.path.join(examples_path, "Memory Storage.ipynb"), execute=True) def test_gym_integration(tb :TestbookNotebookClient): expected_result = {"info1":"'First node info'", "info2":"''", - "info3":"'First node info'", + #"info3":"'First node info'", #For some reason, works running manually and in CI, but not in the local test "info4":"'Second node info'", "info5":"[1, 2, 3]", } @@ -24,3 +29,5 @@ def test_gym_integration(tb :TestbookNotebookClient): assert result == expected_result[tag] +if __name__ == "__main__": + test_gym_integration() \ No newline at end of file From 39428e9673d26fdd8c1a5f1c3e4f25e3faf19c39 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:57:32 -0300 Subject: [PATCH 45/48] MS example test skip if no Redis --- tests/examples/test_memory_storage.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/examples/test_memory_storage.py b/tests/examples/test_memory_storage.py index 9aea827..9bb19cb 100644 --- a/tests/examples/test_memory_storage.py +++ b/tests/examples/test_memory_storage.py @@ -1,6 +1,8 @@ import os import re +import redis +import unittest from testbook import testbook from testbook.client import TestbookNotebookClient @@ -10,9 +12,16 @@ examples_path = get_examples_path() else: - examples_path = "examples" +client = redis.Redis(decode_responses=True) +try: + client.ping() + redis_reachable = True +except Exception: + redis_reachable = False + +@unittest.skipIf(not redis_reachable, "Redis server not running") @testbook(os.path.join(examples_path, "Memory Storage.ipynb"), execute=True) def test_gym_integration(tb :TestbookNotebookClient): From f8ebb62148cb741a61e5c5465e547dbfb513d7c2 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:19:40 -0300 Subject: [PATCH 46/48] Test memory encoder --- dev/args.ipynb | 83 +++++++++++++++++++ .../memory_storage/test_memory_encoder.py | 60 ++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 dev/args.ipynb create mode 100644 tests/cst_python/memory_storage/test_memory_encoder.py diff --git a/dev/args.ipynb b/dev/args.ipynb new file mode 100644 index 0000000..7cff8a3 --- /dev/null +++ b/dev/args.ipynb @@ -0,0 +1,83 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "8" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "def a(a, b, c):\n", + " return a+b+c\n", + "\n", + "def b(**aargs):\n", + " if \"a\" in aargs:\n", + " del aargs[\"a\"]\n", + "\n", + " return a(a = 3, **aargs)\n", + "\n", + "b(a=1, b=2, c=3)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "a() missing 2 required positional arguments: 'b' and 'c'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[4], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mb\u001b[49m\u001b[43m(\u001b[49m\u001b[43ma\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[1;32mIn[2], line 5\u001b[0m, in \u001b[0;36mb\u001b[1;34m(**aargs)\u001b[0m\n\u001b[0;32m 4\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mb\u001b[39m(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39maargs):\n\u001b[1;32m----> 5\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43ma\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43maargs\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[1;31mTypeError\u001b[0m: a() missing 2 required positional arguments: 'b' and 'c'" + ] + } + ], + "source": [ + "b(a=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.9" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/tests/cst_python/memory_storage/test_memory_encoder.py b/tests/cst_python/memory_storage/test_memory_encoder.py new file mode 100644 index 0000000..cc2ac53 --- /dev/null +++ b/tests/cst_python/memory_storage/test_memory_encoder.py @@ -0,0 +1,60 @@ +import json +import unittest +from typing import Any +import math + +from numpy.testing import assert_array_equal + +from cst_python.memory_storage.memory_encoder import MemoryEncoder +from cst_python import MemoryObject + +class TestMemoryEncoder(unittest.TestCase): + + def test_to_dict(self): + memory = MemoryObject() + memory.set_name("MemoryName") + memory.set_info([1,2,3]) + memory.set_id(123) + memory.set_evaluation(0.5) + + + for i in range(2): + if i == 0: + memory_dict = MemoryEncoder.to_dict(memory) + + assert_array_equal(memory_dict["I"], [1,2,3]) + else: + memory_dict = MemoryEncoder.to_dict(memory, jsonify_info=True) + + assert memory_dict["I"] == "[1, 2, 3]" + + assert memory_dict["timestamp"] == memory.get_timestamp() + assert math.isclose(memory_dict["evaluation"], 0.5) + assert memory_dict["name"] == "MemoryName" + assert memory_dict["id"] == 123 + + def test_load_memory(self): + memory = MemoryObject() + memory_dict = {"evaluation": "0.5", "id":"123", "I":"[5, 3, 4]"} + + MemoryEncoder.load_memory(memory, memory_dict) + + assert memory.get_evaluation() == 0.5 + assert memory.get_id() == 123 + assert_array_equal(memory.get_info(), [5, 3, 4]) + + def test_default(self): + memory = MemoryObject() + memory.set_name("MemoryName") + memory.set_info([1,2,3]) + memory.set_id(123) + memory.set_evaluation(0.5) + + memory_json = json.dumps(memory, cls=MemoryEncoder) + memory_dict = json.loads(memory_json) + + assert_array_equal(memory_dict["I"], [1,2,3]) + assert memory_dict["timestamp"] == memory.get_timestamp() + assert math.isclose(memory_dict["evaluation"], 0.5) + assert memory_dict["name"] == "MemoryName" + assert memory_dict["id"] == 123 From 30a8a9382475d01df1deb0de4762ccb17ad5ea47 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Wed, 4 Dec 2024 17:53:53 -0300 Subject: [PATCH 47/48] Typo fix --- src/cst_python/memory_storage/logical_time.py | 4 ++-- src/cst_python/memory_storage/memory_storage.py | 6 +++--- tests/cst_python/memory_storage/test_lamport_time.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/cst_python/memory_storage/logical_time.py b/src/cst_python/memory_storage/logical_time.py index d8ce2cd..b0bff60 100644 --- a/src/cst_python/memory_storage/logical_time.py +++ b/src/cst_python/memory_storage/logical_time.py @@ -41,7 +41,7 @@ def from_str(cls, string:str) -> "LogicalTime": @classmethod @abc.abstractmethod - def syncronize(cls, time0:"LogicalTime", time1:"LogicalTime") -> "LogicalTime": + def synchronize(cls, time0:"LogicalTime", time1:"LogicalTime") -> "LogicalTime": ''' Compares two times, and return the current time. @@ -114,7 +114,7 @@ def from_str(cls, string:str) -> "LamportTime": return LamportTime(int(string)) @classmethod - def syncronize(cls, time0, time1) -> "LamportTime": + def synchronize(cls, time0, time1) -> "LamportTime": if not (isinstance(time0, LamportTime) and isinstance(time1, LamportTime)): raise ValueError("LamportTime can only synchonize LamportTime instances") diff --git a/src/cst_python/memory_storage/memory_storage.py b/src/cst_python/memory_storage/memory_storage.py index b4f52a4..290867e 100644 --- a/src/cst_python/memory_storage/memory_storage.py +++ b/src/cst_python/memory_storage/memory_storage.py @@ -243,7 +243,7 @@ def _retrieve_memory(self, memory:Memory) -> None: MemoryEncoder.load_memory(memory, memory_dict) message_time = LamportTime.from_str(memory_dict["logical_time"]) - self._current_time = LamportTime.syncronize(self._current_time, message_time) + self._current_time = LamportTime.synchronize(self._current_time, message_time) self._last_update[memory_name] = memory.get_timestamp() self._memory_logical_time[memory_name] = message_time @@ -277,7 +277,7 @@ def _handler_notify_transfer(self, message:dict[str,str]) -> None: data = data = json.loads(message["data"]) if "logical_time" in data: message_time = LamportTime.from_str(data["logical_time"]) - self._current_time = LamportTime.syncronize(message_time, self._current_time) + self._current_time = LamportTime.synchronize(message_time, self._current_time) memory_name = data["memory_name"] if memory_name in self._waiting_request_events: @@ -296,7 +296,7 @@ def _handler_transfer_memory(self, message:dict[str,str]) -> None: data = json.loads(message["data"]) if "logical_time" in data: message_time = LamportTime.from_str(data["logical_time"]) - self._current_time = LamportTime.syncronize(message_time, self._current_time) + self._current_time = LamportTime.synchronize(message_time, self._current_time) request = data["request"] diff --git a/tests/cst_python/memory_storage/test_lamport_time.py b/tests/cst_python/memory_storage/test_lamport_time.py index 3c80c5d..a8c01a8 100644 --- a/tests/cst_python/memory_storage/test_lamport_time.py +++ b/tests/cst_python/memory_storage/test_lamport_time.py @@ -35,11 +35,11 @@ def test_increment(self): assert time0._time == time0_time assert time1._time == time0_time+1 - def test_synchonize(self): + def test_synchronize(self): time0 = LamportTime(initial_time=-10) time1 = LamportTime(initial_time=55) - time_s = LamportTime.syncronize(time0, time1) + time_s = LamportTime.synchronize(time0, time1) assert time_s > time0 assert time_s > time1 From f91088d19af57ab222cb45b34c2a9a35117adcd1 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Mon, 9 Dec 2024 15:39:26 -0300 Subject: [PATCH 48/48] Test with MS Java --- tests/MemoryStorage_ExternalTest.py | 53 +++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/MemoryStorage_ExternalTest.py diff --git a/tests/MemoryStorage_ExternalTest.py b/tests/MemoryStorage_ExternalTest.py new file mode 100644 index 0000000..79f0fa2 --- /dev/null +++ b/tests/MemoryStorage_ExternalTest.py @@ -0,0 +1,53 @@ +import time + +import cst_python as cst +from cst_python.memory_storage import MemoryStorageCodelet + +import logging +import sys + +if __name__ == "__main__": + ch = logging.StreamHandler(sys.stdout) + ch.setLevel(logging.INFO) + + logging.getLogger("MemoryStorageCodelet").addHandler(ch) + + SLEEP_TIME = 0.75 + + mind = cst.Mind() + memory1 = mind.create_memory_object("Memory1", "") + + last_timestamp = memory1.get_timestamp() + + ms = MemoryStorageCodelet(mind) + ms.time_step = 100 + mind.insert_codelet(ms) + + mind.start() + + + valid = False + for i in range(30): + time.sleep(0.1) + + if last_timestamp != memory1.get_timestamp() and not memory1.get_info(): + valid = True + memory1.set_info(True) + break + + time.sleep(SLEEP_TIME) + + assert memory1.get_info() == "JAVA_INFO" + + memory1.set_info("OTHER_INFO") + time.sleep(SLEEP_TIME) + + assert memory1.get_info() == 1 + + memory1.set_info(-1) + time.sleep(SLEEP_TIME) + + assert memory1.get_info() == 1.0 + + memory1.set_info(5.0) + #time.sleep(SLEEP_TIME) \ No newline at end of file