{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "-cy4CItVIKRK" }, "source": [ "# Hello distributed world\n", "\n", "The goal of this tutorial is to setup a simulation and run it by using a minimal example.\n" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "id": "bQX5hbAmIKRO", "outputId": "10242ef7-4936-4970-9711-0379033b9d30" }, "outputs": [], "source": [ "%matplotlib inline\n", "\n", "from pydistsim import NetworkGenerator, Simulation\n", "from pydistsim.demo_algorithms.santoro2007.yoyo import YoYo\n", "\n", "\n", "# Create a network with 30 nodes and undirected edges\n", "net_gen = NetworkGenerator(30, directed=False)\n", "net = net_gen.generate_random_network()\n", "\n", "# Create a simulation object\n", "sim = Simulation(net)\n", "\n", "# Assign a simple algorithm to the network, must be a tuple\n", "sim.algorithms = (YoYo,)\n", "\n", "# Create and run the simulation\n", "sim.run()" ] }, { "cell_type": "markdown", "metadata": { "id": "d3s4_XDsIKRQ" }, "source": [ "That's it! For more elaborate description please continue reading.\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# For more detailed output, set the log level to INFO\n", "from pydistsim.logging import set_log_level, LogLevels, enable_logger\n", "\n", "set_log_level(LogLevels.INFO)\n", "enable_logger()" ] }, { "cell_type": "markdown", "metadata": { "id": "UnQizoRsIKRQ" }, "source": [ "In the example given above, the goal was to simulate distributed algorithm `YoYo` on arbitrary network. Algorithm `YoYo` solves _Election_ problem, i.e. the goal of the algorithm is to find the node with the lowest ID in the network.\n" ] }, { "cell_type": "markdown", "metadata": { "id": "25N240CZIKRQ" }, "source": [ "### Generating network\n", "\n", "Networks are fundamental objects in the PyDistSim and they can be created by instantiating `Network` class or by using `NetworkGenerator` class. `NetworkGenerator` can receive different parameters such as number of nodes (exact, min, max), average number of neighbors per node etc.\n", "\n", "In this example, for simplicity, we use a simpler generator so the only parameter that we want to have in control is number of nodes. which is set to 11.\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "id": "dUU7kZ1wIKRR" }, "outputs": [], "source": [ "net = NetworkGenerator.generate_star_network(11)" ] }, { "cell_type": "markdown", "metadata": { "id": "VG18aRU-IKRS" }, "source": [ "The result is an instance of the BidirectionalNetwork class:\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "id": "-vesDvtnIKRS", "outputId": "2f347168-f241-4d1e-c3c3-38ac871f9e24" }, "outputs": [ { "data": { "text/plain": [ "" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net" ] }, { "cell_type": "markdown", "metadata": { "id": "kmUQN53NIKRS" }, "source": [ "which can be visualized with its `show()` method:\n" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "id": "q0JVUYBcIKRS", "outputId": "16c3bf7f-07f3-4a14-ea51-7717abb88237" }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/mnt/d/Proyectos/pymote/docs/notebooks/../../pydistsim/network/network.py:560: UserWarning: FigureCanvasAgg is non-interactive, and thus cannot be shown\n", " fig.show()\n" ] }, { "data": { "image/png": "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", "text/plain": [ "
" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "net.show()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we create the simulation object and set the network to the one we just created:\n" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[32m2024-10-22 23:40:48.975\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mpydistsim.simulation\u001b[0m:\u001b[36m__init__\u001b[0m:\u001b[36m59\u001b[0m - \u001b[1mSimulation 0x7f91cd195b10 created successfully.\u001b[0m\n" ] } ], "source": [ "sim = Simulation(net)" ] }, { "cell_type": "markdown", "metadata": { "id": "sEYOElVXIKRS" }, "source": [ "### Algorithm and Simulation\n", "\n", "To demonstrate the simulation, we will use a simple `Echo` algorithm. The algorithm is a simple flooding algorithm that sends a message to all neighbors and waits for the response from all of them. Once every neighbor responds, the algorithms terminates.\n", "\n", "Implementation of `Echo` algorithm is given below:\n" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "id": "wwaAh2VeIKRS" }, "outputs": [], "source": [ "from pydistsim.algorithm.node_algorithm import NodeAlgorithm, StatusValues\n", "from pydistsim.algorithm.node_wrapper import NodeAccess\n", "from pydistsim.message import Message\n", "from pydistsim.restrictions.communication import BidirectionalLinks\n", "from pydistsim.restrictions.reliability import TotalReliability\n", "from pydistsim.restrictions.topological import Connectivity, UniqueInitiator\n", "\n", "\n", "class Echo(NodeAlgorithm):\n", " default_params = {\n", " \"echo_message\": \"Hello world!\",\n", " }\n", "\n", " class Status(StatusValues):\n", " INITIATOR = \"INITIATOR\"\n", " AWAITING_ECHO = \"AWAITING_ECHO\"\n", " AWAITING_ECHO_RESPONSE = \"AWAITING_ECHO_RESPONSE\"\n", " DONE = \"DONE\"\n", "\n", " S_init = (Status.INITIATOR, Status.AWAITING_ECHO)\n", " S_term = (Status.DONE,)\n", "\n", " # Here we define under which restrictions the algorithm is designed to work\n", " algorithm_restrictions = (\n", " BidirectionalLinks, # The algorithm requires bidirectional links, so that the response can be sent back\n", " TotalReliability, # The algorithm requires that all messages are delivered without loss\n", " Connectivity, # The algorithm requires that the network is connected\n", " UniqueInitiator, # The algorithm requires that only one node is the initiator\n", " )\n", "\n", " def initializer(self):\n", " # Set all nodes to AWAITING_ECHO\n", " for node in self.network.nodes():\n", " node.status = self.Status.AWAITING_ECHO\n", "\n", " # Choose the initiator\n", " ini_node = self.network.nodes_sorted()[0]\n", "\n", " # Send the initial message to the initiator\n", " ini_node.push_to_inbox(Message(meta_header=NodeAlgorithm.INI, destination=ini_node))\n", "\n", " # Set the initiator status to INITIATOR and store the initial information\n", " ini_node.status = self.Status.INITIATOR\n", " ini_node.memory[\"message\"] = self.echo_message\n", "\n", " @Status.INITIATOR\n", " def spontaneously(self, node: NodeAccess, message: Message):\n", " self.send(\n", " node,\n", " data=node.memory[\"message\"],\n", " destination=list(node.neighbors()), # send to all neighbors\n", " header=\"Echo\",\n", " )\n", " node.memory[\"responseCount\"] = 0\n", " node.status = self.Status.AWAITING_ECHO_RESPONSE\n", "\n", " @Status.AWAITING_ECHO_RESPONSE\n", " def receiving(self, node: NodeAccess, message: Message):\n", " if message.header == \"Echo response\":\n", " # Count the number of responses received, only if the response is the same as the original message\n", " node.memory[\"responseCount\"] += 1 if message.data == node.memory[\"message\"] else 0\n", "\n", " # If all responses have been received, set the node status to DONE\n", " if node.memory[\"responseCount\"] == len(list(node.neighbors())):\n", " node.status = self.Status.DONE\n", "\n", " @Status.AWAITING_ECHO\n", " def receiving(self, node: NodeAccess, message: Message):\n", " if message.header == \"Echo\":\n", " node.memory[\"message\"] = message.data\n", "\n", " self.send(\n", " node,\n", " data=message.data,\n", " destination=message.source,\n", " header=\"Echo response\",\n", " )\n", " node.status = self.Status.DONE\n", "\n", " @Status.DONE\n", " def default(self, *args, **kwargs):\n", " \"Do nothing, for all inputs.\"\n", " pass" ] }, { "cell_type": "markdown", "metadata": { "id": "wl8UmzwUIKRT" }, "source": [ "#### Assigning algorithm to the simulation\n", "\n", "Two things should be noted:\n", "\n", "- there can be multiple algorithms that are being assigned to the simulation so specific algorithms are elements of `tuple`. Since in this example there is only one algorithm we must append `,` so that Python knows it's a one element `tuple` i.e. `(1)` is `int`, but `(1,)` is `tuple`.\n", "- every algorithm element is `tuple` itself, consisting of two elements. Former is an algorithm class, in this example `Echo`. Later is a `dict` of keyword parameters, i.e. `Echo` must be given 'echo_message' as an optional parameter: the message to be sent.\n" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[32m2024-10-22 23:40:51.184\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mpydistsim.simulation\u001b[0m:\u001b[36m__init__\u001b[0m:\u001b[36m59\u001b[0m - \u001b[1mSimulation 0x7f91cd27f910 created successfully.\u001b[0m\n" ] } ], "source": [ "sim = Simulation(net)\n", "MESSAGE = \"Hello distributed world\"\n", "sim.algorithms = ((Echo, {\"echo_message\": MESSAGE}),)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Running\n" ] }, { "cell_type": "markdown", "metadata": { "id": "CBbV5L7UIKRT" }, "source": [ "Finally, run simulation:\n" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[32m2024-10-22 23:40:55.964\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mpydistsim.simulation\u001b[0m:\u001b[36m_run_algorithm\u001b[0m:\u001b[36m148\u001b[0m - \u001b[1m[Echo] Algorithm finished\u001b[0m\n" ] } ], "source": [ "sim.run()" ] }, { "cell_type": "markdown", "metadata": { "id": "m4tSzxrTIKRT" }, "source": [ "After the algorithm(s) execution is done we can check if the resullt is as expected, that is every node should have information in their memory:\n" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "id": "H2rsruiUIKRT", "outputId": "32e92d06-26d8-4892-d661-a84dabfe411e" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Everything is OK!\n" ] } ], "source": [ "for node in net.nodes():\n", " assert node.memory[\"message\"] == MESSAGE, \"Oh...\"\n", " assert node.status == Echo.Status.DONE, \"Oh...\"\n", "\n", "print(\"Everything is OK!\")" ] }, { "cell_type": "markdown", "metadata": { "id": "q18Q_tQYIKRT" }, "source": [ "During simulation execution, network and its nodes are changing their status, memory content etc. To memorize the currently running algorithm and current step of the simulation, network has a special attribute `algorithmsState`:\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "id": "mcc7XWTRIKRU", "outputId": "d437f0f2-9edd-4a33-c988-3be908afde73" }, "outputs": [ { "data": { "text/plain": [ "{'index': 0, 'step': 17, 'finished': True}" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sim.algorithmState" ] }, { "cell_type": "markdown", "metadata": { "id": "JrQR8ftTIKRU" }, "source": [ "If simulation is going to be run again, it must be reset:\n" ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "id": "gprEPr49IKRU", "outputId": "439d8dbd-65e5-41f4-f5f7-2d9bce9eb7bd" }, "outputs": [], "source": [ "sim.reset()" ] }, { "cell_type": "markdown", "metadata": { "id": "b_SEZU08IKRU" }, "source": [ "whereby:\n", "\n", "- `algorithmState` is returned to the initial value:\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "id": "jMTwzLRkIKRU", "outputId": "a1948897-04cd-4bc1-cb97-a45723f30493" }, "outputs": [ { "data": { "text/plain": [ "{'index': 0, 'step': 1, 'finished': False}" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "sim.algorithmState" ] }, { "cell_type": "markdown", "metadata": { "id": "5zgTK1TMIKRU" }, "source": [ "- memory content of all node's is deleted (as long as some other attributes)\n" ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "id": "3AgVUCXcIKRU", "outputId": "e4dc9056-b8d0-435a-bd33-9892b9fc7e10" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "All memories are empty!\n" ] } ], "source": [ "for node in net.nodes():\n", " assert len(node.memory) == 0\n", "print(\"All memories are empty!\")" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "id": "wG4Jq_s7IKRU", "outputId": "940c51e0-35c6-41ea-b511-81b6a51acf9f" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[32m2024-10-22 23:41:11.618\u001b[0m | \u001b[1mINFO \u001b[0m | \u001b[36mpydistsim.simulation\u001b[0m:\u001b[36m_run_algorithm\u001b[0m:\u001b[36m148\u001b[0m - \u001b[1m[Echo] Algorithm finished\u001b[0m\n" ] } ], "source": [ "sim.run()" ] } ], "metadata": { "colab": { "provenance": [] }, "kernelspec": { "display_name": "venv_pydistim_dev", "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.0rc1" } }, "nbformat": 4, "nbformat_minor": 0 }