From e60d45a5ca2f24c617cff936eaf8401647b72282 Mon Sep 17 00:00:00 2001 From: Elton Cardoso do Nascimento <43186596+EltonCN@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:32:05 -0300 Subject: [PATCH] Publisher Subscriber example --- examples/Publisher-Subscriber.ipynb | 502 ++++++++++++++++++ .../Publisher-Subscriber/.$diagram.drawio.bkp | 34 ++ examples/Publisher-Subscriber/diagram.drawio | 66 +++ examples/Publisher-Subscriber/diagram.png | Bin 0 -> 70838 bytes src/cst_python/core/entities/codelet.py | 3 +- .../core/entities/memory_observer.py | 7 +- src/cst_python/core/entities/raw_memory.py | 1 - tests/examples/test_publisher_subscriber.py | 19 + 8 files changed, 625 insertions(+), 7 deletions(-) create mode 100644 examples/Publisher-Subscriber.ipynb create mode 100644 examples/Publisher-Subscriber/.$diagram.drawio.bkp create mode 100644 examples/Publisher-Subscriber/diagram.drawio create mode 100644 examples/Publisher-Subscriber/diagram.png create mode 100644 tests/examples/test_publisher_subscriber.py diff --git a/examples/Publisher-Subscriber.ipynb b/examples/Publisher-Subscriber.ipynb new file mode 100644 index 0000000..d820837 --- /dev/null +++ b/examples/Publisher-Subscriber.ipynb @@ -0,0 +1,502 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Publish-Subscribe\n", + "\n", + "Sometimes we wish that a codelet is only executed when its input value is changed. For that, we can use the publish-subscribe mechanism." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For exemplify that, we are going to implement a agent that computes the average of its input values:\n", + "\n", + "![](./Publisher-Subscriber/diagram.png)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Lets start by importing the necessary modules:" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import time # Sleep\n", + "\n", + "import cst_python as cst # CST-Python Module" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Naive" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The first implementation is going to be a naive aproach: we are going to store the last timestamp the input value was changed, and only compute the average when the timestamp increases:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "class NaiveAverageCodelet(cst.Codelet):\n", + " def __init__(self):\n", + " super().__init__()\n", + "\n", + " self._value_mo : cst.Codelet | None = None\n", + "\n", + " self._counter_mo : cst.Codelet | None = None\n", + " self._avg_mo : cst.Codelet | None = None\n", + "\n", + " self.last_timestamp = 0\n", + "\n", + " def access_memory_objects(self):\n", + " self._value_mo = self.get_input(name=\"Value\")\n", + "\n", + " self._counter_mo = self.get_output(name=\"Counter\")\n", + " self._avg_mo = self.get_output(name=\"Average\")\n", + "\n", + " def calculate_activation(self):\n", + " pass\n", + "\n", + " def proc(self):\n", + " \n", + " # Check if have a new value\n", + " if self._value_mo.get_timestamp() != 0 and self._value_mo.get_timestamp() <= self.last_timestamp:\n", + " return\n", + " self.last_timestamp = self._value_mo.get_timestamp()\n", + "\n", + " counter : int = self._counter_mo.get_info()\n", + " avg : float = self._avg_mo.get_info()\n", + "\n", + " # Retrieve the previous sum\n", + " avg *= counter\n", + "\n", + " # Update the values\n", + " avg += self._value_mo.get_info()\n", + " counter += 1\n", + " avg /= counter\n", + " \n", + " self._avg_mo.set_info(avg)\n", + " self._counter_mo.set_info(counter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `prepare_mind` function creates a new mind with all the necessary memories:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def prepare_mind(average_codelet:cst.Codelet):\n", + " mind = cst.Mind()\n", + "\n", + " avg_mo = mind.create_memory_object(\"Average\", 0.0)\n", + " counter_mo = mind.create_memory_object(\"Counter\", 0)\n", + " value_mo = mind.create_memory_object(\"Value\", 0.0)\n", + " \n", + " average_codelet.add_input(value_mo)\n", + "\n", + " average_codelet.add_output(avg_mo)\n", + " average_codelet.add_output(counter_mo)\n", + "\n", + " # Avoid the naive codelet using the first value in the computation\n", + " average_codelet.last_timestamp = value_mo.get_timestamp() \n", + " \n", + " average_codelet.time_step = 10\n", + " mind.insert_codelet(average_codelet)\n", + "\n", + " return mind, value_mo, avg_mo" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We than create the codelet, prepare and start the mind:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "naive_codelet = NaiveAverageCodelet()\n", + "\n", + "mind, value_mo, avg_mo = prepare_mind(naive_codelet)\n", + "\n", + "mind.start()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For testing, we can set the \"Value\" memory info and check the current average:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "tags": [ + "check_average0" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10.0" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "value_mo.set_info(10)\n", + "\n", + "time.sleep(0.020)\n", + "\n", + "avg_mo.get_info()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "tags": [ + "check_average1" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "15.0" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "value_mo.set_info(20)\n", + "\n", + "time.sleep(0.020)\n", + "\n", + "avg_mo.get_info()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We then stops the executing mind:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "mind.shutdown()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Memory Observer" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this case, the codelet are not going to check if the input value changed, the `proc` method is more clean and really peforms only the codelet operation. Also, the codelet becomes stateless:" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "class PubSubAverageCodelet(cst.Codelet):\n", + " def __init__(self):\n", + " super().__init__()\n", + "\n", + " self._value_mo : cst.Codelet | None = None\n", + "\n", + " self._counter_mo : cst.Codelet | None = None\n", + " self._avg_mo : cst.Codelet | None = None\n", + " \n", + "\n", + " def access_memory_objects(self):\n", + " self._value_mo = self.get_input(name=\"Value\")\n", + "\n", + " self._counter_mo = self.get_output(name=\"Counter\")\n", + " self._avg_mo = self.get_output(name=\"Average\")\n", + "\n", + " def calculate_activation(self):\n", + " pass\n", + "\n", + " def proc(self):\n", + " counter : int = self._counter_mo.get_info()\n", + " avg : float = self._avg_mo.get_info()\n", + "\n", + " # Retrieve the previous sum\n", + " avg *= counter\n", + "\n", + " # Update the values\n", + " avg += self._value_mo.get_info()\n", + " counter += 1\n", + " avg /= counter\n", + " \n", + " self._avg_mo.set_info(avg)\n", + " self._counter_mo.set_info(counter)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We than create the codelet and mind as before:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "average_codelet = PubSubAverageCodelet()\n", + "\n", + "mind, value_mo, avg_mo = prepare_mind(average_codelet)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In this case, we configure the codelet as a \"memory observer\": it is going to execute the `proc` method only when the observed memory is changed. Than we set the codelet as a observer of the \"Value\" memory and starts the mind:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "average_codelet.is_memory_observer = True\n", + "value_mo.add_memory_observer(average_codelet)\n", + "\n", + "mind.start()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We test the codelet with the same example as before:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "tags": [ + "check_average2" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10.0" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "value_mo.set_info(10)\n", + "\n", + "time.sleep(0.020)\n", + "\n", + "avg_mo.get_info()" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "tags": [ + "check_average3" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "15.0" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "value_mo.set_info(20)\n", + "\n", + "time.sleep(0.020)\n", + "\n", + "avg_mo.get_info()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "mind.shutdown()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Publisher-Subscriber\n", + "\n", + "The previous example shows how to create a codelet that that selectively waits for some input to change.\n", + "\n", + "Sometimes, we wanna the codelet to run when any input (or the only input) changes. In this case, we can use a \"publish-subscriber\" codelet:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "average_codelet = PubSubAverageCodelet()\n", + "\n", + "mind, value_mo, avg_mo = prepare_mind(average_codelet)\n", + "\n", + "average_codelet.set_publish_subscribe(True)\n", + "\n", + "mind.start()" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "tags": [ + "check_average4" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "10.0" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "value_mo.set_info(10)\n", + "\n", + "time.sleep(0.020)\n", + "\n", + "avg_mo.get_info()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": { + "tags": [ + "check_average5" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "15.0" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "value_mo.set_info(20)\n", + "\n", + "time.sleep(0.020)\n", + "\n", + "avg_mo.get_info()" + ] + } + ], + "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/Publisher-Subscriber/.$diagram.drawio.bkp b/examples/Publisher-Subscriber/.$diagram.drawio.bkp new file mode 100644 index 0000000..3c614fe --- /dev/null +++ b/examples/Publisher-Subscriber/.$diagram.drawio.bkp @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Publisher-Subscriber/diagram.drawio b/examples/Publisher-Subscriber/diagram.drawio new file mode 100644 index 0000000..788d5d4 --- /dev/null +++ b/examples/Publisher-Subscriber/diagram.drawio @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/Publisher-Subscriber/diagram.png b/examples/Publisher-Subscriber/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..ea875c87815d70657d91f86bfacbdc492798d4a9 GIT binary patch literal 70838 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 literal 0 HcmV?d00001 diff --git a/src/cst_python/core/entities/codelet.py b/src/cst_python/core/entities/codelet.py index bead8ff..ada9aa0 100644 --- a/src/cst_python/core/entities/codelet.py +++ b/src/cst_python/core/entities/codelet.py @@ -9,11 +9,12 @@ from cst_python.python import alias from .memory import Memory from .memory_buffer import MemoryBuffer +from .memory_observer import MemoryObserver #TODO: Profile, Broadcast, impending access, correct exception types #@alias.aliased -class Codelet(abc.ABC): +class Codelet(MemoryObserver): #(abc.ABC) is not necessary _last_id = 0 def __init__(self) -> None: diff --git a/src/cst_python/core/entities/memory_observer.py b/src/cst_python/core/entities/memory_observer.py index 6980135..66a8ef4 100644 --- a/src/cst_python/core/entities/memory_observer.py +++ b/src/cst_python/core/entities/memory_observer.py @@ -2,11 +2,8 @@ from cst_python.python import alias -class MemoryObserver(abc.ABC): - def __init__(self) -> None: - raise NotImplementedError() - +class MemoryObserver(abc.ABC): #@alias.alias("notifyCodelet") @abc.abstractmethod def notify_codelet(self): - raise NotImplementedError() \ No newline at end of file + ... \ No newline at end of file diff --git a/src/cst_python/core/entities/raw_memory.py b/src/cst_python/core/entities/raw_memory.py index 99359bb..c270498 100644 --- a/src/cst_python/core/entities/raw_memory.py +++ b/src/cst_python/core/entities/raw_memory.py @@ -72,7 +72,6 @@ def create_memory_object(self, name:str, info:Optional[Any]=None) -> MemoryObjec mo = MemoryObject() mo.set_info(info) - mo.timestamp = time.time() mo.set_evaluation(0.0) mo.set_name(name) diff --git a/tests/examples/test_publisher_subscriber.py b/tests/examples/test_publisher_subscriber.py new file mode 100644 index 0000000..ca75b15 --- /dev/null +++ b/tests/examples/test_publisher_subscriber.py @@ -0,0 +1,19 @@ +import os +import math + +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, "Publisher-Subscriber.ipynb"), execute=True) +def test_publisher_subscriber(tb :TestbookNotebookClient): + expected_results : list[list] = [10, 15, 10, 15, 10, 15] + + for i, excepted_result in enumerate(expected_results): + result = tb.cell_output_text(f"check_average{i}") + result = eval(result) + + assert math.isclose(result, excepted_result) \ No newline at end of file