From 3079add30144bb38f2eca12b20e68745786b358f Mon Sep 17 00:00:00 2001 From: corentinlger Date: Mon, 25 Nov 2024 15:05:54 +0100 Subject: [PATCH 1/8] Add helper attributes for accessing agents, clean comments --- notebooks/sessions/session_5_bonus.ipynb | 193 ++++++++++++++++---- vivarium/controllers/notebook_controller.py | 37 ++-- 2 files changed, 175 insertions(+), 55 deletions(-) diff --git a/notebooks/sessions/session_5_bonus.ipynb b/notebooks/sessions/session_5_bonus.ipynb index 686fe80..aab7061 100644 --- a/notebooks/sessions/session_5_bonus.ipynb +++ b/notebooks/sessions/session_5_bonus.ipynb @@ -22,7 +22,15 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n" + ] + } + ], "source": [ "import numpy as np\n", "\n", @@ -36,19 +44,41 @@ "metadata": {}, "outputs": [ { - "ename": "FileNotFoundError", - "evalue": "[WinError 2] The system cannot find the file specified", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mFileNotFoundError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[1;32mIn[2], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m \u001b[43mstart_server_and_interface\u001b[49m\u001b[43m(\u001b[49m\u001b[43mscene_name\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43msession_5\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32mc:\\users\\coren\\desktop\\code\\vivarium\\vivarium\\utils\\handle_server_interface.py:42\u001b[0m, in \u001b[0;36mstart_server_and_interface\u001b[1;34m(scene_name, notebook_mode, wait_time)\u001b[0m\n\u001b[0;32m 36\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"Start the server and interface for the given scene\u001b[39;00m\n\u001b[0;32m 37\u001b[0m \n\u001b[0;32m 38\u001b[0m \u001b[38;5;124;03m:param scene_name: scene name\u001b[39;00m\n\u001b[0;32m 39\u001b[0m \u001b[38;5;124;03m:param notebook_mode: notebook_mode to adapt the interface, defaults to True\u001b[39;00m\n\u001b[0;32m 40\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m 41\u001b[0m \u001b[38;5;66;03m# first ensure no interface or server is running\u001b[39;00m\n\u001b[1;32m---> 42\u001b[0m \u001b[43mstop_server_and_interface\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 43\u001b[0m \u001b[38;5;66;03m# find the path to the server and interface scripts\u001b[39;00m\n\u001b[0;32m 44\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mos\u001b[38;5;241m.\u001b[39mpath\u001b[38;5;241m.\u001b[39mdirname(\u001b[38;5;18m__file__\u001b[39m)\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m)\n", - "File \u001b[1;32mc:\\users\\coren\\desktop\\code\\vivarium\\vivarium\\utils\\handle_server_interface.py:84\u001b[0m, in \u001b[0;36mstop_server_and_interface\u001b[1;34m()\u001b[0m\n\u001b[0;32m 82\u001b[0m stopped \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[0;32m 83\u001b[0m \u001b[38;5;66;03m# interface_process_name = \"vivarium/scripts/run_interface.py\"\u001b[39;00m\n\u001b[1;32m---> 84\u001b[0m interface_pid \u001b[38;5;241m=\u001b[39m \u001b[43mget_process_pid\u001b[49m\u001b[43m(\u001b[49m\u001b[43mINTERFACE_PROCESS_NAME\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 85\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m interface_pid \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 86\u001b[0m kill_process(interface_pid)\n", - "File \u001b[1;32mc:\\users\\coren\\desktop\\code\\vivarium\\vivarium\\utils\\handle_server_interface.py:19\u001b[0m, in \u001b[0;36mget_process_pid\u001b[1;34m(process_name)\u001b[0m\n\u001b[0;32m 13\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mget_process_pid\u001b[39m(process_name: \u001b[38;5;28mstr\u001b[39m):\n\u001b[0;32m 14\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Get the process ID of a running process by name\u001b[39;00m\n\u001b[0;32m 15\u001b[0m \n\u001b[0;32m 16\u001b[0m \u001b[38;5;124;03m :param process_name: process name\u001b[39;00m\n\u001b[0;32m 17\u001b[0m \u001b[38;5;124;03m :return: process ID\u001b[39;00m\n\u001b[0;32m 18\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[1;32m---> 19\u001b[0m process \u001b[38;5;241m=\u001b[39m \u001b[43msubprocess\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mPopen\u001b[49m\u001b[43m(\u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mps\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43maux\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstdout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43msubprocess\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mPIPE\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 20\u001b[0m out, err \u001b[38;5;241m=\u001b[39m process\u001b[38;5;241m.\u001b[39mcommunicate()\n\u001b[0;32m 21\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m line \u001b[38;5;129;01min\u001b[39;00m out\u001b[38;5;241m.\u001b[39msplitlines():\n", - "File \u001b[1;32mC:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\subprocess.py:971\u001b[0m, in \u001b[0;36mPopen.__init__\u001b[1;34m(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask, pipesize)\u001b[0m\n\u001b[0;32m 967\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mtext_mode:\n\u001b[0;32m 968\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstderr \u001b[38;5;241m=\u001b[39m io\u001b[38;5;241m.\u001b[39mTextIOWrapper(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstderr,\n\u001b[0;32m 969\u001b[0m encoding\u001b[38;5;241m=\u001b[39mencoding, errors\u001b[38;5;241m=\u001b[39merrors)\n\u001b[1;32m--> 971\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_execute_child\u001b[49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexecutable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpreexec_fn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mclose_fds\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 972\u001b[0m \u001b[43m \u001b[49m\u001b[43mpass_fds\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcwd\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43menv\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 973\u001b[0m \u001b[43m \u001b[49m\u001b[43mstartupinfo\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcreationflags\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mshell\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 974\u001b[0m \u001b[43m \u001b[49m\u001b[43mp2cread\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mp2cwrite\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 975\u001b[0m \u001b[43m \u001b[49m\u001b[43mc2pread\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mc2pwrite\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 976\u001b[0m \u001b[43m \u001b[49m\u001b[43merrread\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merrwrite\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 977\u001b[0m \u001b[43m \u001b[49m\u001b[43mrestore_signals\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 978\u001b[0m \u001b[43m \u001b[49m\u001b[43mgid\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgids\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43muid\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mumask\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 979\u001b[0m \u001b[43m \u001b[49m\u001b[43mstart_new_session\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 980\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m:\n\u001b[0;32m 981\u001b[0m \u001b[38;5;66;03m# Cleanup if the child failed starting.\u001b[39;00m\n\u001b[0;32m 982\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m f \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mfilter\u001b[39m(\u001b[38;5;28;01mNone\u001b[39;00m, (\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstdin, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstdout, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstderr)):\n", - "File \u001b[1;32mC:\\Program Files\\WindowsApps\\PythonSoftwareFoundation.Python.3.10_3.10.3056.0_x64__qbz5n2kfra8p0\\lib\\subprocess.py:1456\u001b[0m, in \u001b[0;36mPopen._execute_child\u001b[1;34m(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, unused_restore_signals, unused_gid, unused_gids, unused_uid, unused_umask, unused_start_new_session)\u001b[0m\n\u001b[0;32m 1454\u001b[0m \u001b[38;5;66;03m# Start the process\u001b[39;00m\n\u001b[0;32m 1455\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m-> 1456\u001b[0m hp, ht, pid, tid \u001b[38;5;241m=\u001b[39m \u001b[43m_winapi\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mCreateProcess\u001b[49m\u001b[43m(\u001b[49m\u001b[43mexecutable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 1457\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;66;43;03m# no special security\u001b[39;49;00m\n\u001b[0;32m 1458\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 1459\u001b[0m \u001b[43m \u001b[49m\u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mclose_fds\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 1460\u001b[0m \u001b[43m \u001b[49m\u001b[43mcreationflags\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 1461\u001b[0m \u001b[43m \u001b[49m\u001b[43menv\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 1462\u001b[0m \u001b[43m \u001b[49m\u001b[43mcwd\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 1463\u001b[0m \u001b[43m \u001b[49m\u001b[43mstartupinfo\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 1464\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 1465\u001b[0m \u001b[38;5;66;03m# Child is launched. Close the parent's copy of those pipe\u001b[39;00m\n\u001b[0;32m 1466\u001b[0m \u001b[38;5;66;03m# handles that only the child should have open. You need\u001b[39;00m\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 1469\u001b[0m \u001b[38;5;66;03m# pipe will not close when the child process exits and the\u001b[39;00m\n\u001b[0;32m 1470\u001b[0m \u001b[38;5;66;03m# ReadFile will hang.\u001b[39;00m\n\u001b[0;32m 1471\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_close_pipe_fds(p2cread, p2cwrite,\n\u001b[0;32m 1472\u001b[0m c2pread, c2pwrite,\n\u001b[0;32m 1473\u001b[0m errread, errwrite)\n", - "\u001b[1;31mFileNotFoundError\u001b[0m: [WinError 2] The system cannot find the file specified" + "name": "stdout", + "output_type": "stream", + "text": [ + "/home/cleger/Desktop/code/vivarium/vivarium/utils\n", + "STARTING SERVER\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[2024-11-25 14:48:18,993][__main__][INFO] - Scene running: session_5\n", + "[2024-11-25 14:48:21,668][vivarium.simulator.simulator][INFO] - Simulator initialized\n", + "\n", + "STARTING INTERFACE\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-11-25 14:48:23,580 Starting Bokeh server version 3.3.4 (running on Tornado 6.4)\n", + "2024-11-25 14:48:23,580 User authentication hooks NOT provided (default user enabled)\n", + "2024-11-25 14:48:23,582 Bokeh app running at: http://localhost:5006/run_interface\n", + "2024-11-25 14:48:23,582 Starting Bokeh server with process id: 33727\n", + "2024-11-25 14:48:36,107 An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n", + "2024-11-25 14:48:38,248 WebSocket connection opened\n", + "2024-11-25 14:48:38,298 ServerConnection created\n" ] } ], @@ -63,15 +93,6 @@ "Start running the controller and check the subtypes of this session" ] }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "controller = NotebookController()" - ] - }, { "cell_type": "code", "execution_count": 3, @@ -81,18 +102,28 @@ "name": "stderr", "output_type": "stream", "text": [ - "Error while executing routine: name 'offspring_diameter' is not defined, removing routine sexual_reproduction\n" + "/home/cleger/Desktop/code/vivarium/venv/lib/python3.10/site-packages/jax/_src/ops/scatter.py:96: FutureWarning: scatter inputs have incompatible types: cannot safely cast value from dtype=float32 to dtype=int32 with jax_numpy_dtype_promotion='standard'. In future JAX releases this will result in an error.\n", + " warnings.warn(\n" ] } ], "source": [ - "controller.run()" + "controller = NotebookController()" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, + "outputs": [], + "source": [ + "controller.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -116,7 +147,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": { "collapsed": true }, @@ -162,7 +193,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [ { @@ -187,7 +218,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -221,7 +252,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -239,16 +270,16 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 9, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -267,7 +298,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -285,9 +316,17 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Entity 5 already removed\n" + ] + } + ], "source": [ "agent_idx = 5 # idx of an alive agent\n", "controller.remove_entity(agent_idx)" @@ -297,8 +336,86 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now you have the basics to make agents spwan and die ! \n", + "Now you have the basics to make agents spwan and die ! " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's see how to manipulate dead and alive agents lists now, you can either get these lists manually:" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Getting existing agents manually: \n", + "0\n", + "1\n", + "2\n", + "3\n" + ] + } + ], + "source": [ + "print(\"Getting existing agents manually: \")\n", + "for agent in controller.agents:\n", + " if agent.exists == True:\n", + " print(agent.idx)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "But you also have helper attributes to do that :" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Getting existing agents with an attribute: \n", + "0\n", + "1\n", + "2\n", + "3\n", + "\n", + "Getting non existing agents with an attribute: \n", + "4\n", + "5\n", + "6\n", + "7\n", + "8\n", + "9\n" + ] + } + ], + "source": [ + "print(\"Getting existing agents with an attribute: \")\n", + "for agent in controller.existing_agents:\n", + " print(agent.idx)\n", "\n", + "print(\"\\nGetting non existing agents with an attribute: \")\n", + "for agent in controller.non_existing_agents:\n", + " print(agent.idx)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ "Attach behaviors to existing agents (you can technically also attach them to dead agents but it won't have any effect):" ] }, @@ -1280,7 +1397,7 @@ ], "metadata": { "kernelspec": { - "display_name": "myvenv", + "display_name": "venv", "language": "python", "name": "python3" }, @@ -1294,7 +1411,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.11" + "version": "3.10.12" } }, "nbformat": 4, diff --git a/vivarium/controllers/notebook_controller.py b/vivarium/controllers/notebook_controller.py index e409e43..20762dd 100644 --- a/vivarium/controllers/notebook_controller.py +++ b/vivarium/controllers/notebook_controller.py @@ -106,7 +106,6 @@ def print_infos(self): return print("\n".join(info_lines)) - # TODO : Add this in tests def print_routines(self): """Print the entity's routines """ @@ -237,7 +236,6 @@ def stop_behavior(self, name): n = name.__name__ if hasattr(name, "__name__") else name del self.active_behaviors[n] - # TODO : add interval execution for behaviors def print_behaviors(self): """Print the behaviors and active behaviors of the agent """ @@ -327,20 +325,6 @@ def print_infos(self, full_infos=False): info_lines.append(f"Sensors: Left={sensors[0]:.2f}, Right={sensors[1]:.2f}") info_lines.append(f"Motors: Left={self.left_motor:.2f}, Right={self.right_motor:.2f}") - # TODO : see if we print behaviors and eating infos by default - # List behaviors - # if self.behaviors: - # info_lines.append("Behaviors:") - # for name, (behavior_fn, weight) in self.behaviors.items(): - # info_lines.append(f" - {name}: Function={behavior_fn.__name__}, Weight={weight}") - # else: - # info_lines.append("Behaviors: None") - - # See if we print that by default - # info_lines.append('') # add a space between other infos and eating infos atm - # info_lines.append(f"Diet: {self.diet}") - # info_lines.append(f"Eating range: {self.eating_range}") - dict_infos = self.config.to_dict() if full_infos: @@ -529,8 +513,10 @@ def _run(self, num_steps=math.inf): # execute routines of the controller self.controller_routine_step(self.time) - # execute routines of the entities + # execute routines of the existing entities for entity in self.all_entities: + if not entity.exists: + continue entity.routine_step(self.time) entity.set_events() # execute behaviors of agents @@ -635,6 +621,23 @@ def box_size(self): """ return self.configs[StateType.SIMULATOR][0].box_size + # TODO: Test this in notebooks + @property + def existing_agents(self): + """Return the list of existing agents + + :return: existing agents + """ + return [agent for agent in self.agents if agent.exists] + + @property + def non_existing_agents(self): + """Return the list of non existing agents + + :return: non existing agents + """ + return [agent for agent in self.agents if not agent.exists] + def spawn_entity_routine_fn(controller, entity_type=None, position_range=None): """Spawn entities of type entity_type every period seconds within a given position range From c3125955c0c12230b100ec566b9d3729a75082be Mon Sep 17 00:00:00 2001 From: corentinlger Date: Mon, 25 Nov 2024 16:44:49 +0100 Subject: [PATCH 2/8] Add error catching option to routines --- vivarium/controllers/notebook_controller.py | 20 +++++++------- vivarium/controllers/utils.py | 30 ++++++++++++++------- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/vivarium/controllers/notebook_controller.py b/vivarium/controllers/notebook_controller.py index 20762dd..80f1485 100644 --- a/vivarium/controllers/notebook_controller.py +++ b/vivarium/controllers/notebook_controller.py @@ -79,11 +79,11 @@ def detach_all_routines(self): """ self.routine_handler.detach_all_routines() - def routine_step(self, time): + def routine_step(self, time, catch_errors): """Execute the entity's routines with their corresponding execution intervals """ # Give self object as parameter to the routine function so it executes functions on the entity - self.routine_handler.routine_step(self, time) + self.routine_handler.routine_step(self, time, catch_errors) def print_infos(self): """Print the entity's infos @@ -485,7 +485,7 @@ def set_all_user_events(self): for e in self.all_entities: e.set_events() - def run(self, threaded=True, num_steps=math.inf): + def run(self, threaded=True, num_steps=math.inf, catch_errors=False): """Run the simulation :param threaded: wether to run the simulation in a thread or not, defaults to True @@ -497,13 +497,13 @@ def run(self, threaded=True, num_steps=math.inf): return self._is_running = True if threaded: - run_thread = threading.Thread(target=self._run, args=(num_steps,)) + run_thread = threading.Thread(target=self._run, args=(num_steps, catch_errors)) run_thread.daemon = True run_thread.start() else: self._run(num_steps=num_steps) - def _run(self, num_steps=math.inf): + def _run(self, num_steps=math.inf, catch_errors=False): """run the simulation for a given number of steps :param num_steps: num_steps, defaults to math.inf @@ -511,14 +511,14 @@ def _run(self, num_steps=math.inf): while self.time < num_steps and self._is_running: with self.batch_set_state(): # execute routines of the controller - self.controller_routine_step(self.time) + self.controller_routine_step(self.time, catch_errors=catch_errors) # execute routines of the existing entities for entity in self.all_entities: + entity.set_events() if not entity.exists: continue - entity.routine_step(self.time) - entity.set_events() + entity.routine_step(self.time, catch_errors=catch_errors) # execute behaviors of agents if entity.etype == EntityType.AGENT: entity.behave(self.time) @@ -564,10 +564,10 @@ def detach_all_routines(self): """ self.routine_handler.detach_all_routines() - def controller_routine_step(self, time): + def controller_routine_step(self, time, catch_errors): """Execute the simulator routines """ - self.routine_handler.routine_step(self, time) + self.routine_handler.routine_step(self, time, catch_errors) def print_subtypes_list(self): """Return the list of subtypes diff --git a/vivarium/controllers/utils.py b/vivarium/controllers/utils.py index 0d6f1b0..e2382d9 100644 --- a/vivarium/controllers/utils.py +++ b/vivarium/controllers/utils.py @@ -39,6 +39,7 @@ def clear(self): self.logs = {} +# TODO : add a lock to the detach behavior and to the behave function to prevent the simulator from crashing --> DO IT DIRECTLY HERE class RoutineHandler(object): """RoutineHandler class that handles routines for the NotebookController and its Entities """ @@ -47,6 +48,7 @@ def __init__(self): """ self._routines = {} + # TODO : add start routines + start_all routines def attach_routine(self, routine_fn, name=None, interval=1): """Attach a routine to the entity @@ -64,29 +66,37 @@ def detach_routine(self, name): """ del self._routines[name] + # TODO : call the detach_routine function for all routines to make use of the lock def detach_all_routines(self): """Detach all routines from the entity """ - self._routines = {} + for name in list(self._routines.keys()): + self.detach_routine(name) - def routine_step(self, entity, time): + # TODO : add a flag to prevent or allow automatic error catching in behavior execution + def routine_step(self, entity, time, catch_errors=True): """Execute the entity's routines with their corresponding execution intervals, and remove the ones that cause errors """ to_remove = [] # iterate over the routines and check if they work for name, (fn, interval) in self._routines.items(): if time % interval == 0: - try: - # execute the function on the entity object if the routine works + # if the catch_errors flag is set to True, catch the errors and remove the routine if it fails + if catch_errors: + try: + # execute the function on the entity object if the routine works + fn(entity) + except Exception as e: + # else plot an error message and remove the routine + lg.error(f"Error while executing routine: {e}, removing routine {name}") + to_remove.append(name) + else: fn(entity) - except Exception as e: - # else plot an error message and remove the routine - lg.error(f"Error while executing routine: {e}, removing routine {name}") - to_remove.append(name) # remove all problematic routines at the end to prevent spamming error messages and crashing the program - for name in to_remove: - del self._routines[name] + if catch_errors: + for name in to_remove: + del self._routines[name] def print_routines(self): """Print the routines attached to the entity From a03e0a03f27b651a7042a71b6808abe4f1b08809 Mon Sep 17 00:00:00 2001 From: corentinlger Date: Mon, 25 Nov 2024 16:57:18 +0100 Subject: [PATCH 3/8] Add locking mechanism to routines --- vivarium/controllers/utils.py | 43 +++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/vivarium/controllers/utils.py b/vivarium/controllers/utils.py index e2382d9..dcd3e19 100644 --- a/vivarium/controllers/utils.py +++ b/vivarium/controllers/utils.py @@ -1,4 +1,5 @@ import logging +import threading lg = logging.getLogger(__name__) @@ -39,7 +40,6 @@ def clear(self): self.logs = {} -# TODO : add a lock to the detach behavior and to the behave function to prevent the simulator from crashing --> DO IT DIRECTLY HERE class RoutineHandler(object): """RoutineHandler class that handles routines for the NotebookController and its Entities """ @@ -47,8 +47,8 @@ def __init__(self): """Initialize the RoutineHandler """ self._routines = {} + self._lock = threading.Lock() - # TODO : add start routines + start_all routines def attach_routine(self, routine_fn, name=None, interval=1): """Attach a routine to the entity @@ -57,41 +57,43 @@ def attach_routine(self, routine_fn, name=None, interval=1): :param interval: routine execution interval, defaults to 1 """ assert isinstance(interval, int) and interval > 0, "Interval must be a positive integer" - self._routines[name or routine_fn.__name__] = (routine_fn, interval) + with self._lock: + self._routines[name or routine_fn.__name__] = (routine_fn, interval) def detach_routine(self, name): """Detach a routine from the entity :param name: routine name """ - del self._routines[name] + with self._lock: + del self._routines[name] - # TODO : call the detach_routine function for all routines to make use of the lock def detach_all_routines(self): """Detach all routines from the entity """ for name in list(self._routines.keys()): self.detach_routine(name) - # TODO : add a flag to prevent or allow automatic error catching in behavior execution def routine_step(self, entity, time, catch_errors=True): """Execute the entity's routines with their corresponding execution intervals, and remove the ones that cause errors """ to_remove = [] - # iterate over the routines and check if they work - for name, (fn, interval) in self._routines.items(): - if time % interval == 0: - # if the catch_errors flag is set to True, catch the errors and remove the routine if it fails - if catch_errors: - try: - # execute the function on the entity object if the routine works + with self._lock: + # iterate over the routines and check if they work + for name, (fn, interval) in self._routines.items(): + if time % interval == 0: + # if the catch_errors flag is set to True, catch the errors and remove the routine if it fails + if catch_errors: + try: + # execute the function on the entity object if the routine works + fn(entity) + except Exception as e: + # else plot an error message and remove the routine + lg.error(f"Error while executing routine: {e}, removing routine {name}") + to_remove.append(name) + # else just execute the routines normally + else: fn(entity) - except Exception as e: - # else plot an error message and remove the routine - lg.error(f"Error while executing routine: {e}, removing routine {name}") - to_remove.append(name) - else: - fn(entity) # remove all problematic routines at the end to prevent spamming error messages and crashing the program if catch_errors: @@ -101,4 +103,5 @@ def routine_step(self, entity, time, catch_errors=True): def print_routines(self): """Print the routines attached to the entity """ - print(f"Attached routines: {list(self._routines.keys())}") \ No newline at end of file + with self._lock: + print(f"Attached routines: {list(self._routines.keys())}") \ No newline at end of file From 1c798f9ef9bea3c4e634dc7668d5e6ebb7e89cdd Mon Sep 17 00:00:00 2001 From: corentinlger Date: Tue, 26 Nov 2024 11:40:14 +0100 Subject: [PATCH 4/8] Update eating mechanism --- notebooks/sessions/session_3.ipynb | 390 +++++++++++++------- vivarium/controllers/notebook_controller.py | 68 +++- 2 files changed, 308 insertions(+), 150 deletions(-) diff --git a/notebooks/sessions/session_3.ipynb b/notebooks/sessions/session_3.ipynb index 5311795..509b5c0 100644 --- a/notebooks/sessions/session_3.ipynb +++ b/notebooks/sessions/session_3.ipynb @@ -66,6 +66,7 @@ "name": "stdout", "output_type": "stream", "text": [ + "Server and Interface Stopped\n", "/home/cleger/Desktop/code/vivarium/vivarium/utils\n", "STARTING SERVER\n" ] @@ -81,29 +82,23 @@ "name": "stdout", "output_type": "stream", "text": [ - "[2024-11-08 15:57:45,674][__main__][INFO] - Scene running: session_3\n", + "[2024-11-26 11:24:11,155][__main__][INFO] - Scene running: session_3\n", + "[2024-11-26 11:24:13,681][vivarium.simulator.simulator][INFO] - Simulator initialized\n", "\n", "STARTING INTERFACE\n" ] }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[2024-11-08 15:57:49,030][vivarium.simulator.simulator][INFO] - Simulator initialized\n" - ] - }, { "name": "stderr", "output_type": "stream", "text": [ - "2024-11-08 15:57:49,636 Starting Bokeh server version 3.3.4 (running on Tornado 6.4)\n", - "2024-11-08 15:57:49,636 User authentication hooks NOT provided (default user enabled)\n", - "2024-11-08 15:57:49,638 Bokeh app running at: http://localhost:5006/run_interface\n", - "2024-11-08 15:57:49,638 Starting Bokeh server with process id: 26738\n", - "2024-11-08 15:57:51,717 An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n", - "2024-11-08 15:57:53,828 WebSocket connection opened\n", - "2024-11-08 15:57:53,863 ServerConnection created\n" + "2024-11-26 11:24:15,495 Starting Bokeh server version 3.3.4 (running on Tornado 6.4)\n", + "2024-11-26 11:24:15,495 User authentication hooks NOT provided (default user enabled)\n", + "2024-11-26 11:24:15,497 Bokeh app running at: http://localhost:5006/run_interface\n", + "2024-11-26 11:24:15,497 Starting Bokeh server with process id: 18344\n", + "2024-11-26 11:24:18,309 An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n", + "2024-11-26 11:24:19,982 WebSocket connection opened\n", + "2024-11-26 11:24:20,008 ServerConnection created\n" ] } ], @@ -113,7 +108,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [ { @@ -138,9 +133,43 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Exception in thread Thread-5 (_run):\n", + "Traceback (most recent call last):\n", + " File \"/usr/lib/python3.10/threading.py\", line 1016, in _bootstrap_inner\n", + " self.run()\n", + " File \"/home/cleger/Desktop/code/vivarium/venv/lib/python3.10/site-packages/ipykernel/ipkernel.py\", line 766, in run_closure\n", + " _threading_Thread_run(self)\n", + " File \"/usr/lib/python3.10/threading.py\", line 953, in run\n", + " self._target(*self._args, **self._kwargs)\n", + " File \"/home/cleger/Desktop/code/vivarium/vivarium/controllers/notebook_controller.py\", line 513, in _run\n", + " with self.batch_set_state():\n", + " File \"/usr/lib/python3.10/contextlib.py\", line 142, in __exit__\n", + " next(self.gen)\n", + " File \"/home/cleger/Desktop/code/vivarium/vivarium/controllers/simulator_controller.py\", line 72, in batch_set_state\n", + " self.push_state(*self._event_list)\n", + " File \"/home/cleger/Desktop/code/vivarium/vivarium/controllers/simulator_controller.py\", line 52, in push_state\n", + " self.client.set_state(**sc._asdict())\n", + " File \"/home/cleger/Desktop/code/vivarium/vivarium/simulator/grpc_server/simulator_client.py\", line 41, in set_state\n", + " self.stub.SetState(state_change)\n", + " File \"/home/cleger/Desktop/code/vivarium/venv/lib/python3.10/site-packages/grpc/_channel.py\", line 1160, in __call__\n", + " return _end_unary_response_blocking(state, call, False, None)\n", + " File \"/home/cleger/Desktop/code/vivarium/venv/lib/python3.10/site-packages/grpc/_channel.py\", line 1003, in _end_unary_response_blocking\n", + " raise _InactiveRpcError(state) # pytype: disable=not-instantiable\n", + "grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:\n", + "\tstatus = StatusCode.UNAVAILABLE\n", + "\tdetails = \"Socket closed\"\n", + "\tdebug_error_string = \"UNKNOWN:Error received from peer {grpc_message:\"Socket closed\", grpc_status:14, created_time:\"2024-11-26T11:26:48.927417525+01:00\"}\"\n", + ">\n" + ] + } + ], "source": [ "controller.run()" ] @@ -158,7 +187,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -167,7 +196,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "metadata": {}, "outputs": [ { @@ -180,9 +209,9 @@ "Subtype: robots\n", "Idx: 0\n", "Exists: True\n", - "Position: x=102.98, y=107.84\n", + "Position: x=68.65, y=71.90\n", "\n", - "Sensors: Left=0.69, Right=0.25\n", + "Sensors: Left=0.79, Right=0.50\n", "Motors: Left=0.00, Right=0.00\n", "\n" ] @@ -194,7 +223,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -203,14 +232,14 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "0.690665602684021 0.24877935647964478\n" + "0.7939733862876892 0.4993416666984558\n" ] } ], @@ -221,21 +250,21 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "measuring the number of steps per second in the controller for 2 seconds\n" + "measuring the FPS (number of steps per second) in the controller during 2 seconds\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "FPS: 28.50 (number of steps per second in the controller)\n" + "FPS: 34.50\n" ] } ], @@ -252,7 +281,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -276,14 +305,14 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "0.3124312162399292 0\n" + "0.0424196720123291 0.44503527879714966\n" ] } ], @@ -308,7 +337,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "0.7224568724632263 0\n" + "0.05865830183029175 0.4533795714378357\n" ] } ], @@ -329,13 +358,6 @@ "execution_count": 13, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "FPS: 27.50 (number of steps per second in the controller)\n" - ] - }, { "ename": "AssertionError", "evalue": "Please specify valid sensed entities among {'robots', 'resources', 'obstacles'}", @@ -344,7 +366,7 @@ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", "Cell \u001b[0;32mIn[13], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m left, right \u001b[38;5;241m=\u001b[39m \u001b[43magent\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msensors\u001b[49m\u001b[43m(\u001b[49m\u001b[43msensed_entities\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mnon_existing_type\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/Desktop/code/vivarium/vivarium/controllers/notebook_controller.py:145\u001b[0m, in \u001b[0;36mAgent.sensors\u001b[0;34m(self, sensed_entities)\u001b[0m\n\u001b[1;32m 142\u001b[0m left, right \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconfig\u001b[38;5;241m.\u001b[39mleft_prox, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconfig\u001b[38;5;241m.\u001b[39mright_prox\n\u001b[1;32m 143\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m sensed_entities \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 144\u001b[0m \u001b[38;5;66;03m# transform the strings of sensed entities into ints (this fn can surely be optimized)\u001b[39;00m\n\u001b[0;32m--> 145\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mall\u001b[39m(ent_subtype \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvalid_subtypes \u001b[38;5;28;01mfor\u001b[39;00m ent_subtype \u001b[38;5;129;01min\u001b[39;00m sensed_entities), \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPlease specify valid sensed entities among \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvalid_subtypes\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 146\u001b[0m sensed_entities \u001b[38;5;241m=\u001b[39m [\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_subtype_label_to_idx[label] \u001b[38;5;28;01mfor\u001b[39;00m label \u001b[38;5;129;01min\u001b[39;00m sensed_entities]\n\u001b[1;32m 147\u001b[0m sensed_type_left, sensed_type_right \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprox_sensed_ent_type\n", + "File \u001b[0;32m~/Desktop/code/vivarium/vivarium/controllers/notebook_controller.py:149\u001b[0m, in \u001b[0;36mAgent.sensors\u001b[0;34m(self, sensed_entities)\u001b[0m\n\u001b[1;32m 146\u001b[0m left, right \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconfig\u001b[38;5;241m.\u001b[39mleft_prox, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconfig\u001b[38;5;241m.\u001b[39mright_prox\n\u001b[1;32m 147\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m sensed_entities \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 148\u001b[0m \u001b[38;5;66;03m# transform the strings of sensed entities into ints (this fn can surely be optimized)\u001b[39;00m\n\u001b[0;32m--> 149\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mall\u001b[39m(ent_subtype \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvalid_subtypes \u001b[38;5;28;01mfor\u001b[39;00m ent_subtype \u001b[38;5;129;01min\u001b[39;00m sensed_entities), \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPlease specify valid sensed entities among \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvalid_subtypes\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 150\u001b[0m sensed_entities \u001b[38;5;241m=\u001b[39m [\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_subtype_label_to_idx[label] \u001b[38;5;28;01mfor\u001b[39;00m label \u001b[38;5;129;01min\u001b[39;00m sensed_entities]\n\u001b[1;32m 151\u001b[0m sensed_type_left, sensed_type_right \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprox_sensed_ent_type\n", "\u001b[0;31mAssertionError\u001b[0m: Please specify valid sensed entities among {'robots', 'resources', 'obstacles'}" ] } @@ -397,14 +419,14 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "0.27164679765701294 0\n" + "0.5305280685424805 0\n" ] } ], @@ -423,17 +445,6 @@ "*Tip:* it is similar to the `shyness` behavior of [Braitenberg vehicles](https://docs.google.com/presentation/d/1s6ibk_ACiJb9CERJ_8L_b4KFu9d04ZG_htUbb_YSYT4/edit#slide=id.g31e1b425a3_0_0)." ] }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "def obstacle_avoidance(epuck):\n", - " # Write your code below\n", - " pass\n" - ] - }, { "cell_type": "code", "execution_count": 15, @@ -484,7 +495,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 18, "metadata": {}, "outputs": [ { @@ -497,12 +508,12 @@ "Subtype: robots\n", "Idx: 0\n", "Exists: True\n", - "Position: x=100.59, y=269.16\n", + "Position: x=63.00, y=65.96\n", "\n", - "Sensors: Left=0.07, Right=0.44\n", - "Motors: Left=0.57, Right=0.94\n", + "Sensors: Left=0.57, Right=0.68\n", + "Motors: Left=0.00, Right=0.00\n", "\n", - "Available behaviors: ['obstacle_avoidance'], Active behaviors: ['obstacle_avoidance']\n" + "No behaviors attached\n" ] } ], @@ -536,7 +547,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -555,7 +566,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -572,21 +583,21 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 21, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "measuring the number of steps per second in the controller for 2 seconds\n" + "measuring the FPS (number of steps per second) in the controller during 2 seconds\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "FPS: 25.50 (number of steps per second in the controller)\n" + "FPS: 34.00\n" ] } ], @@ -603,9 +614,17 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 22, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "new fn\n" + ] + } + ], "source": [ "seconds = 2\n", "fps = 28\n", @@ -616,7 +635,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 23, "metadata": {}, "outputs": [], "source": [ @@ -634,30 +653,35 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 24, "metadata": {}, "outputs": [], "source": [ - "controller.stop_ressources_apparition()" + "controller.stop_resources_apparition()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Remove all entities of a type" + "You can also remove all the entities of a certain type with this command:" ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Entity 4 already removed\n" + "Entity 6 already removed\n", + "Entity 7 already removed\n", + "Entity 8 already removed\n", + "Entity 10 already removed\n", + "Entity 11 already removed\n", + "Entity 12 already removed\n" ] } ], @@ -665,6 +689,13 @@ "controller.remove_entity_type(\"resources\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It will print an information message for all the non existing entities of this type we tried to remove (here, already non-existing ressources)." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -674,13 +705,106 @@ "--> Not implemented yet, bug with custom positions" ] }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "inf" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent.time_since_last_meal" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent.has_eaten_since(10)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "inf\n" + ] + } + ], + "source": [ + "agent.time_since_last_meal += 10\n", + "print(agent.time_since_last_meal)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "inf\n" + ] + } + ], + "source": [ + "agent.time_since_last_meal += 1000\n", + "print(agent.time_since_last_meal)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "new fn\n" + ] + } + ], + "source": [ + "controller.start_resources_apparition(interval=interval, position_range=((0, 50), (100, 200)))" + ] + }, { "cell_type": "code", "execution_count": 31, "metadata": {}, "outputs": [], "source": [ - "controller.start_resources_apparition(interval=interval, position_range=((0, 50), (100, 200)))" + "controller.stop_resources_apparition()" ] }, { @@ -729,21 +853,10 @@ "execution_count": 32, "metadata": {}, "outputs": [], - "source": [ - "def foraging(epuck):\n", - " # Write your code below\n", - " pass" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "metadata": {}, - "outputs": [], "source": [ "def foraging(agent):\n", " left, right = agent.sensors(sensed_entities=[\"resources\"])\n", - " return right, left\n" + " return right, left" ] }, { @@ -755,7 +868,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -769,7 +882,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 34, "metadata": {}, "outputs": [ { @@ -793,7 +906,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -810,7 +923,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 36, "metadata": {}, "outputs": [ { @@ -843,15 +956,6 @@ "which tells us that both behaviors are attached and started. In V-REP, you can see that the robot is now both foraging and avoiding obstacles. For doing so, the motor activation sent to each wheel corresponds to the average of the motor activation returned by each behavior (this averaging is implemented internally, you don't need to worry about it)." ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Comment : Deleted line following \n", - "\n", - "Delete line following and replaced it by an activity on agents sensors (modify their range, their angle, etc...)" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -863,18 +967,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This section explains how to deal with multiple robots and how to attach different behaviors to them.\n", - "\n", - "First close the current session:" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "metadata": {}, - "outputs": [], - "source": [ - "# TODO Later" + "This section explains how to deal with multiple robots and how to attach different behaviors to them." ] }, { @@ -899,7 +992,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -908,7 +1001,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -932,7 +1025,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -943,26 +1036,26 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 40, "metadata": {}, "outputs": [ { - "ename": "KeyError", - "evalue": "'spawn_entity_routine_fn'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[42], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mcontroller\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mstop_ressources_apparition\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 2\u001b[0m controller\u001b[38;5;241m.\u001b[39mstart_resources_apparition(interval\u001b[38;5;241m=\u001b[39minterval)\n", - "File \u001b[0;32m~/Desktop/code/vivarium/vivarium/controllers/notebook_controller.py:485\u001b[0m, in \u001b[0;36mstop_ressources_apparition\u001b[0;34m(self)\u001b[0m\n\u001b[1;32m 484\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mstop_ressources_apparition\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[0;32m--> 485\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Stop the resources apparition process\u001b[39;00m\n\u001b[1;32m 486\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[1;32m 487\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdetach_routine(spawn_entity_routine_fn\u001b[38;5;241m.\u001b[39m\u001b[38;5;18m__name__\u001b[39m)\n", - "File \u001b[0;32m~/Desktop/code/vivarium/vivarium/controllers/notebook_controller.py:564\u001b[0m, in \u001b[0;36mdetach_routine\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 561\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mdetach_routine\u001b[39m(\u001b[38;5;28mself\u001b[39m, name):\n\u001b[1;32m 562\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Detach a routine from the entity\u001b[39;00m\n\u001b[1;32m 563\u001b[0m \n\u001b[0;32m--> 564\u001b[0m \u001b[38;5;124;03m :param name: routine name\u001b[39;00m\n\u001b[1;32m 565\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[1;32m 566\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mroutine_handler\u001b[38;5;241m.\u001b[39mdetach_routine(name)\n", - "File \u001b[0;32m~/Desktop/code/vivarium/vivarium/controllers/utils.py:65\u001b[0m, in \u001b[0;36mRoutineHandler.detach_routine\u001b[0;34m(self, name)\u001b[0m\n\u001b[1;32m 60\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mdetach_routine\u001b[39m(\u001b[38;5;28mself\u001b[39m, name):\n\u001b[1;32m 61\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Detach a routine from the entity\u001b[39;00m\n\u001b[1;32m 62\u001b[0m \n\u001b[1;32m 63\u001b[0m \u001b[38;5;124;03m :param name: routine name\u001b[39;00m\n\u001b[1;32m 64\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[0;32m---> 65\u001b[0m \u001b[38;5;28;01mdel\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_routines[name]\n", - "\u001b[0;31mKeyError\u001b[0m: 'spawn_entity_routine_fn'" + "name": "stderr", + "output_type": "stream", + "text": [ + "Resources apparition is already stopped\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "new fn\n" ] } ], "source": [ - "controller.stop_ressources_apparition()\n", + "controller.stop_resources_apparition()\n", "controller.start_resources_apparition(interval=interval)" ] }, @@ -977,7 +1070,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -1007,7 +1100,26 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 42, + "metadata": {}, + "outputs": [], + "source": [ + "from vivarium.controllers.notebook_controller import NotebookController\n", + "controller = NotebookController()" + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [], + "source": [ + "controller.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ @@ -1017,7 +1129,17 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 45, + "metadata": {}, + "outputs": [], + "source": [ + "agent_0 = controller.agents[0]\n", + "agent_1 = controller.agents[1]" + ] + }, + { + "cell_type": "code", + "execution_count": 46, "metadata": {}, "outputs": [], "source": [ @@ -1032,7 +1154,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 47, "metadata": {}, "outputs": [], "source": [ @@ -1049,7 +1171,7 @@ "agent_1.diameter = 4.\n", "agent_1.color = 'cyan'\n", "# Make the little agent go faster\n", - "agent_1.wheel_diameter = 4.\n" + "agent_1.wheel_diameter = 4." ] }, { @@ -1074,7 +1196,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 50, "metadata": {}, "outputs": [], "source": [ @@ -1093,7 +1215,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 51, "metadata": {}, "outputs": [], "source": [ @@ -1115,7 +1237,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 52, "metadata": {}, "outputs": [ { @@ -1142,7 +1264,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "venv", "language": "python", "name": "python3" }, diff --git a/vivarium/controllers/notebook_controller.py b/vivarium/controllers/notebook_controller.py index 80f1485..9eb2211 100644 --- a/vivarium/controllers/notebook_controller.py +++ b/vivarium/controllers/notebook_controller.py @@ -126,7 +126,7 @@ def __init__(self, config): self.active_behaviors = {} self.eating_range = 10 self.diet = [] - self.ate = False + self.time_since_last_meal = np.inf self.simulation_entities = None self.logger = Logger() self.set_manual() @@ -281,14 +281,12 @@ def stop_motors(self): self.left_motor = 0 self.right_motor = 0 - def has_eaten(self): - """Check if the agent has eaten + def has_eaten_since(self, time): + """ Check if the agent has eaten since a given time - :return: True if the agent has eaten, False otherwise + :return: True if the agent has eaten since the given time, False otherwise """ - val = self.ate - self.ate = False - return val + return self.time_since_last_meal < time def add_log(self, log_field, data): """Add a log to the agent's logger (e.g robot.add_log("left_prox", left_prox_value)) @@ -327,7 +325,9 @@ def print_infos(self, full_infos=False): dict_infos = self.config.to_dict() if full_infos: - + info_lines.append('') # add a space between other infos and eating infos atm + info_lines.append(f"Diet: {self.diet}") + info_lines.append(f"Eating range: {self.eating_range}") info_lines.append("\nConfiguration Details:") for k, v in dict_infos.items(): if k not in ['x_position', 'y_position', 'behavior', 'left_motor', 'right_motor', 'params', 'sensed']: @@ -456,9 +456,9 @@ def start_entity_apparition(self, interval=50, entity_type: str = None, position """ entity_type_idx = self.get_idx_from_label_subtype(entity_type) - routine_fn = functools.partial(spawn_entity_routine_fn, entity_type=entity_type_idx, position_range=position_range) + routine_fn = functools.partial(spawn_entity_routine, entity_type=entity_type_idx, position_range=position_range) # add the name of the spawning routine function otherwise error in the routine handler - self.attach_routine(routine_fn, name=spawn_entity_routine_fn.__name__, interval=interval) + self.attach_routine(routine_fn, name=spawn_entity_routine.__name__, interval=interval) def start_resources_apparition(self, interval=50, position_range=None): """Start the resources apparition process @@ -469,13 +469,13 @@ def start_resources_apparition(self, interval=50, position_range=None): resources_type = "resources" self.start_entity_apparition(interval, entity_type=resources_type, position_range=position_range) # start the eating routine for agents - self.attach_routine(eating_routine_fn) + self.attach_routine(eating_routine) def stop_resources_apparition(self): """Stop the resources apparition process """ - if spawn_entity_routine_fn.__name__ in self.routine_handler._routines: - self.detach_routine(spawn_entity_routine_fn.__name__) + if spawn_entity_routine.__name__ in self.routine_handler._routines: + self.detach_routine(spawn_entity_routine.__name__) else: lg.warning("Resources apparition is already stopped") @@ -663,7 +663,7 @@ def spawn_entity_routine_fn(controller, entity_type=None, position_range=None): lg.info(f'All entities of type {entity_type} are spawned') -def eating_routine_fn(controller): +# TODO : Update old routine function for eating """make agents of the simulation eating the entities in their diet :param controller: NotebookController @@ -683,6 +683,42 @@ def eating_routine_fn(controller): # arr_idx is the index of the in_range array for arr_idx, ent_idx in enumerate(eatable_entities_idx): if in_range[arr_idx] and controller.all_entities[ent_idx].exists: - controller.remove_entity(ent_idx) - agent.ate = True + controller.remove_entity(ent_idx) + # TODO : check here + agent.time_since_last_meal = 0 + + +def eating_routine(controller): + """Routine to make agents eat entities in their diet. + They can only eat entities sensed by their proximeters, and that are in their eating range. + + :param controller: _description_ + """ + for agent in controller.existing_agents: + left_prox, right_prox = agent.sensors() + left_type, right_type = agent.prox_sensed_ent_type + + can_eat_left = ( + controller.subtypes_labels[left_type] in agent.diet and + (1. - left_prox) * agent.proxs_dist_max <= agent.eating_range + ) + + can_eat_right = ( + controller.subtypes_labels[right_type] in agent.diet and + (1. - right_prox) * agent.proxs_dist_max <= agent.eating_range + ) + + # if the agent can eat + if can_eat_left or can_eat_right: + # determine which side to eat + if can_eat_left and can_eat_right: + eating_choice = np.random.choice([0, 1]) + else: + eating_choice = 0 if can_eat_left else 1 + + controller.remove_entity(agent.prox_sensed_ent_idx[eating_choice]) + # TODO : update the mechanism to update the has eaten since time + agent.ate = True + else: + agent.ate = False From 3351e31c22ae076a3a025343de2345c3ba18941e Mon Sep 17 00:00:00 2001 From: corentinlger Date: Tue, 26 Nov 2024 15:42:43 +0100 Subject: [PATCH 5/8] Separate eating and entities spawning mechanisms --- notebooks/sessions/session_3.ipynb | 415 ++++++++++---------- vivarium/controllers/notebook_controller.py | 52 ++- 2 files changed, 245 insertions(+), 222 deletions(-) diff --git a/notebooks/sessions/session_3.ipynb b/notebooks/sessions/session_3.ipynb index 509b5c0..853db75 100644 --- a/notebooks/sessions/session_3.ipynb +++ b/notebooks/sessions/session_3.ipynb @@ -108,18 +108,9 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "/home/cleger/Desktop/code/vivarium/venv/lib/python3.10/site-packages/jax/_src/ops/scatter.py:96: FutureWarning: scatter inputs have incompatible types: cannot safely cast value from dtype=float32 to dtype=int32 with jax_numpy_dtype_promotion='standard'. In future JAX releases this will result in an error.\n", - " warnings.warn(\n" - ] - } - ], + "outputs": [], "source": [ "controller = NotebookController()" ] @@ -133,43 +124,9 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "Exception in thread Thread-5 (_run):\n", - "Traceback (most recent call last):\n", - " File \"/usr/lib/python3.10/threading.py\", line 1016, in _bootstrap_inner\n", - " self.run()\n", - " File \"/home/cleger/Desktop/code/vivarium/venv/lib/python3.10/site-packages/ipykernel/ipkernel.py\", line 766, in run_closure\n", - " _threading_Thread_run(self)\n", - " File \"/usr/lib/python3.10/threading.py\", line 953, in run\n", - " self._target(*self._args, **self._kwargs)\n", - " File \"/home/cleger/Desktop/code/vivarium/vivarium/controllers/notebook_controller.py\", line 513, in _run\n", - " with self.batch_set_state():\n", - " File \"/usr/lib/python3.10/contextlib.py\", line 142, in __exit__\n", - " next(self.gen)\n", - " File \"/home/cleger/Desktop/code/vivarium/vivarium/controllers/simulator_controller.py\", line 72, in batch_set_state\n", - " self.push_state(*self._event_list)\n", - " File \"/home/cleger/Desktop/code/vivarium/vivarium/controllers/simulator_controller.py\", line 52, in push_state\n", - " self.client.set_state(**sc._asdict())\n", - " File \"/home/cleger/Desktop/code/vivarium/vivarium/simulator/grpc_server/simulator_client.py\", line 41, in set_state\n", - " self.stub.SetState(state_change)\n", - " File \"/home/cleger/Desktop/code/vivarium/venv/lib/python3.10/site-packages/grpc/_channel.py\", line 1160, in __call__\n", - " return _end_unary_response_blocking(state, call, False, None)\n", - " File \"/home/cleger/Desktop/code/vivarium/venv/lib/python3.10/site-packages/grpc/_channel.py\", line 1003, in _end_unary_response_blocking\n", - " raise _InactiveRpcError(state) # pytype: disable=not-instantiable\n", - "grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with:\n", - "\tstatus = StatusCode.UNAVAILABLE\n", - "\tdetails = \"Socket closed\"\n", - "\tdebug_error_string = \"UNKNOWN:Error received from peer {grpc_message:\"Socket closed\", grpc_status:14, created_time:\"2024-11-26T11:26:48.927417525+01:00\"}\"\n", - ">\n" - ] - } - ], + "outputs": [], "source": [ "controller.run()" ] @@ -187,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -196,7 +153,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -223,7 +180,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -232,14 +189,14 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "0.7939733862876892 0.4993416666984558\n" + "0.8058567047119141 0.5096818208694458\n" ] } ], @@ -250,7 +207,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -264,7 +221,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "FPS: 34.50\n" + "FPS: 35.50\n" ] } ], @@ -281,7 +238,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -305,14 +262,14 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "0.0424196720123291 0.44503527879714966\n" + "0 0.4364292025566101\n" ] } ], @@ -330,14 +287,14 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "0.05865830183029175 0.4533795714378357\n" + "0.163119375705719 0.48004770278930664\n" ] } ], @@ -355,19 +312,19 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "metadata": {}, "outputs": [ { "ename": "AssertionError", - "evalue": "Please specify valid sensed entities among {'robots', 'resources', 'obstacles'}", + "evalue": "Please specify valid sensed entities among {'obstacles', 'robots', 'resources'}", "output_type": "error", "traceback": [ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[13], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m left, right \u001b[38;5;241m=\u001b[39m \u001b[43magent\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msensors\u001b[49m\u001b[43m(\u001b[49m\u001b[43msensed_entities\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mnon_existing_type\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/Desktop/code/vivarium/vivarium/controllers/notebook_controller.py:149\u001b[0m, in \u001b[0;36mAgent.sensors\u001b[0;34m(self, sensed_entities)\u001b[0m\n\u001b[1;32m 146\u001b[0m left, right \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconfig\u001b[38;5;241m.\u001b[39mleft_prox, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconfig\u001b[38;5;241m.\u001b[39mright_prox\n\u001b[1;32m 147\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m sensed_entities \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 148\u001b[0m \u001b[38;5;66;03m# transform the strings of sensed entities into ints (this fn can surely be optimized)\u001b[39;00m\n\u001b[0;32m--> 149\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mall\u001b[39m(ent_subtype \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvalid_subtypes \u001b[38;5;28;01mfor\u001b[39;00m ent_subtype \u001b[38;5;129;01min\u001b[39;00m sensed_entities), \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPlease specify valid sensed entities among \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvalid_subtypes\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 150\u001b[0m sensed_entities \u001b[38;5;241m=\u001b[39m [\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_subtype_label_to_idx[label] \u001b[38;5;28;01mfor\u001b[39;00m label \u001b[38;5;129;01min\u001b[39;00m sensed_entities]\n\u001b[1;32m 151\u001b[0m sensed_type_left, sensed_type_right \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprox_sensed_ent_type\n", - "\u001b[0;31mAssertionError\u001b[0m: Please specify valid sensed entities among {'robots', 'resources', 'obstacles'}" + "Cell \u001b[0;32mIn[12], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m left, right \u001b[38;5;241m=\u001b[39m \u001b[43magent\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43msensors\u001b[49m\u001b[43m(\u001b[49m\u001b[43msensed_entities\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43m[\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mnon_existing_type\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/Desktop/code/vivarium/vivarium/controllers/notebook_controller.py:151\u001b[0m, in \u001b[0;36mAgent.sensors\u001b[0;34m(self, sensed_entities)\u001b[0m\n\u001b[1;32m 148\u001b[0m left, right \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconfig\u001b[38;5;241m.\u001b[39mleft_prox, \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mconfig\u001b[38;5;241m.\u001b[39mright_prox\n\u001b[1;32m 149\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m sensed_entities \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 150\u001b[0m \u001b[38;5;66;03m# transform the strings of sensed entities into ints (this fn can surely be optimized)\u001b[39;00m\n\u001b[0;32m--> 151\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mall\u001b[39m(ent_subtype \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvalid_subtypes \u001b[38;5;28;01mfor\u001b[39;00m ent_subtype \u001b[38;5;129;01min\u001b[39;00m sensed_entities), \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPlease specify valid sensed entities among \u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mvalid_subtypes\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 152\u001b[0m sensed_entities \u001b[38;5;241m=\u001b[39m [\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_subtype_label_to_idx[label] \u001b[38;5;28;01mfor\u001b[39;00m label \u001b[38;5;129;01min\u001b[39;00m sensed_entities]\n\u001b[1;32m 153\u001b[0m sensed_type_left, sensed_type_right \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprox_sensed_ent_type\n", + "\u001b[0;31mAssertionError\u001b[0m: Please specify valid sensed entities among {'obstacles', 'robots', 'resources'}" ] } ], @@ -419,14 +376,14 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "0.5305280685424805 0\n" + "0 0.7445070743560791\n" ] } ], @@ -447,7 +404,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -465,7 +422,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -486,7 +443,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -495,7 +452,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -508,9 +465,9 @@ "Subtype: robots\n", "Idx: 0\n", "Exists: True\n", - "Position: x=63.00, y=65.96\n", + "Position: x=74.00, y=59.51\n", "\n", - "Sensors: Left=0.57, Right=0.68\n", + "Sensors: Left=0.72, Right=-0.00\n", "Motors: Left=0.00, Right=0.00\n", "\n", "No behaviors attached\n" @@ -540,9 +497,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### Comment : \n", + "## Comment \n", "\n", - "at the moment manually attach a routine so the agent eats resources" + "- to make sure it works well make the robot turn on himself with following cmd " ] }, { @@ -551,256 +508,304 @@ "metadata": {}, "outputs": [], "source": [ - "for ag in controller.agents:\n", - " ag.diet = [\"resources\"]" + "agent.left_motor = 0.8\n", + "agent.right_motor = 0.0" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Comment \n", + "We want to implement a mechanism where agents will eat only ressources. To do so, we have to do three things:\n", "\n", - "- to make sure it works well make the robot turn on himself with following cmd " - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "metadata": {}, - "outputs": [], - "source": [ - "agent.left_motor = 0.8\n", - "agent.right_motor = 0.0" + "- Add `resources` to the agents's diets\n", + "- Start the eating mechanism in the controller\n", + "- Start the apparition of `resources` in the environment" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Can start ressources apparition, will also start eating mechanism of the server where agents can eat resources when close to them. TO do so, define an interval of steps between each apparition of a resource with the 'interval' parameter. To get a good approximation of this interval, use the get fps function to know how many steps per seconds" + "### Manipulate the diet of agents" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "measuring the FPS (number of steps per second) in the controller during 2 seconds\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "FPS: 34.00\n" + "Different subtypes of entities:\n", + "['obstacles', 'robots', 'resources']\n", + "\n", + "Agent eating information:\n", + "Current agent diet: ['resources']\n", + "Current agent eating range: 10\n" ] } ], "source": [ - "controller.print_fps()" + "print(\"Different subtypes of entities:\")\n", + "controller.print_subtypes_list()\n", + "\n", + "print(f\"\\nAgent eating information:\")\n", + "print(f\"Current agent diet: {agent.diet}\")\n", + "print(f\"Current agent eating range: {agent.eating_range}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "e.g. if we want a resource to appear every 5 seconds, we will set the interval to 5*fps" + "You can also get theses informations by using the print_infos() function, with `full_infos` set to True: " ] }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 27, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "new fn\n" + "Entity Overview:\n", + "--------------------\n", + "Type: AGENT\n", + "Subtype: robots\n", + "Idx: 0\n", + "Exists: True\n", + "Position: x=74.03, y=56.30\n", + "\n", + "Sensors: Left=0.77, Right=0.04\n", + "Motors: Left=0.80, Right=0.00\n", + "\n", + "Diet: ['resources']\n", + "Eating range: 10\n", + "\n", + "Configuration Details:\n", + " - color: #0000ff\n", + " - diameter: 10.0\n", + " - exists: True\n", + " - friction: 0.10000000149011612\n", + " - idx: 0\n", + " - left_prox: 0.7707440853118896\n", + " - mass_center: 1.0\n", + " - mass_orientation: 0.125\n", + " - max_speed: 10.0\n", + " - orientation: -485.9183654785156\n", + " - prox_sensed_ent_idx: [ 4 14]\n", + " - prox_sensed_ent_type: [1 2]\n", + " - proximity_map_dist: [ 0. 82.69258881 99.6177597 87.17667389 13.75535393\n", + " 19.25267029 25.86472893 86.72187805 81.89463806 100.56718445\n", + " 29.92865944 101.04159546 92.33370972 115.61449432 57.42391205\n", + " 17.15402794 98.66919708 59.35287476 79.44650269 43.66890335\n", + " 110.7736969 77.92525482 62.45607758 76.61223602 96.63103485\n", + " 110.02033234 84.20428467 40.62639999 88.3372879 ]\n", + " - proximity_map_theta: [ 0. 487.40603638 486.29891968 485.28283691 484.81417847\n", + " 488.13751221 487.00082397 484.74783325 487.43701172 486.77337646\n", + " 488.5614624 486.86755371 483.80093384 486.61331177 488.98770142\n", + " 487.05633545 484.33291626 483.00845337 487.16018677 485.02035522\n", + " 486.97052002 484.73855591 488.51141357 486.75973511 486.68289185\n", + " 485.3923645 483.78356934 488.38848877 487.36938477]\n", + " - proxs_cos_min: 0.0\n", + " - proxs_dist_max: 60.0\n", + " - right_prox: 0.04293477535247803\n", + " - speed_mul: 1.0\n", + " - subtype: 0\n", + " - theta_mul: 1.0\n", + " - wheel_diameter: 2.0\n", + "\n" ] } ], "source": [ - "seconds = 2\n", - "fps = 28\n", - "interval = seconds * fps\n", - "\n", - "controller.start_resources_apparition(interval=interval)" + "agent.print_infos(full_infos=True)" ] }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ - "agent.detach_all_behaviors()\n", - "agent.attach_behavior(obstacle_avoidance)\n", - "agent.start_all_behaviors()" + "agent.diet = [\"resources\"]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "where `period` indicates the time interval at which spheres will appear (here every 5 seconds). In order to stop sphere apparition:" + "Now, if the eating mechanism is started, the agents will eat the resources. If we wanted them to eat other kind of entities such as obstacles, we would have to add 'obstacles' to the diet of the agents (make sure to respect the good ortograph of the entities with the controller.print_subtypes_list() function)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Start the eating mechanism\n", + "\n", + "You can start the eating of the controller mechanism with the command in the following cell. This will make the agents eat the resources in their diet and eating range. You also have an option to only eat the resources in your proximeters range ... don't know if it is useful to tell it here." ] }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ - "controller.stop_resources_apparition()" + "controller.start_eating_mechanism()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "You can also remove all the entities of a certain type with this command:" + "### Start spawning of resources" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Can start ressources apparition, will also start eating mechanism of the server where agents can eat resources when close to them. TO do so, define an interval of steps between each apparition of a resource with the 'interval' parameter. To get a good approximation of this interval, use the get fps function to know how many steps per seconds" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 30, "metadata": {}, "outputs": [ { - "name": "stderr", + "name": "stdout", + "output_type": "stream", + "text": [ + "measuring the FPS (number of steps per second) in the controller during 2 seconds\n" + ] + }, + { + "name": "stdout", "output_type": "stream", "text": [ - "Entity 6 already removed\n", - "Entity 7 already removed\n", - "Entity 8 already removed\n", - "Entity 10 already removed\n", - "Entity 11 already removed\n", - "Entity 12 already removed\n" + "FPS: 32.00\n" ] } ], "source": [ - "controller.remove_entity_type(\"resources\")" + "controller.print_fps()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "It will print an information message for all the non existing entities of this type we tried to remove (here, already non-existing ressources)." + "e.g. if we want a resource to appear every 5 seconds, we will set the interval to 5*fps" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": 34, "metadata": {}, + "outputs": [], "source": [ - "We can also specify in which area spheres appear through the `min_pos` and `max_pos` arguments:\n", + "seconds = 2\n", + "fps = 28\n", + "interval = seconds * fps\n", "\n", - "--> Not implemented yet, bug with custom positions" + "controller.start_resources_apparition(interval=interval)" ] }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 32, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "inf" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "agent.time_since_last_meal" + "agent.detach_all_behaviors()\n", + "agent.attach_behavior(obstacle_avoidance)\n", + "agent.start_all_behaviors()" ] }, { - "cell_type": "code", - "execution_count": 27, + "cell_type": "markdown", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "False" - ] - }, - "execution_count": 27, - "metadata": {}, - "output_type": "execute_result" - } - ], "source": [ - "agent.has_eaten_since(10)" + "where `period` indicates the time interval at which spheres will appear (here every 5 seconds). In order to stop sphere apparition:" ] }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 33, + "metadata": {}, + "outputs": [], + "source": [ + "controller.stop_resources_apparition()" + ] + }, + { + "cell_type": "markdown", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "inf\n" - ] - } - ], "source": [ - "agent.time_since_last_meal += 10\n", - "print(agent.time_since_last_meal)" + "You can also remove all the entities of a certain type with this command:" ] }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 35, + "metadata": {}, + "outputs": [], + "source": [ + "controller.remove_entity_type(\"resources\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If the resources apparition function is still attached to the controller, it will keep spawning resources even after removing them with the precedent function. It will print an information message for all the non existing entities of this type we tried to remove (here, already non-existing ressources)." + ] + }, + { + "cell_type": "markdown", "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "inf\n" - ] - } - ], "source": [ - "agent.time_since_last_meal += 1000\n", - "print(agent.time_since_last_meal)" + "### Custom position of resources spawning" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also control the position range where the resources will appear with the `position_range` parameter. This parameter is a list of 4 values: ((x_min, x_max), (y_min, y_max)) where x_min and x_max are the minimum and maximum x coordinates of the spawning area, and y_min and y_max are the minimum and maximum y coordinates of the spawning area. For example, to make resources appear only in the area between x=0 and x=50, and y=100 and y=200:" ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 37, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "new fn\n" - ] - } - ], + "outputs": [], "source": [ + "# first remove all the resources and stop the resources apparition\n", + "controller.remove_entity_type(\"resources\")\n", + "controller.stop_resources_apparition()\n", + "# then, start the spawning apparition in a specific area\n", "controller.start_resources_apparition(interval=interval, position_range=((0, 50), (100, 200)))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can play with this process a bit, and then stop it." + ] + }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -850,7 +855,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -868,7 +873,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -882,7 +887,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 41, "metadata": {}, "outputs": [ { @@ -906,7 +911,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 42, "metadata": {}, "outputs": [], "source": [ @@ -923,7 +928,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 43, "metadata": {}, "outputs": [ { @@ -938,17 +943,6 @@ "agent.print_behaviors()" ] }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Comment \n", - "\n", - "- problem is that there are a lot of resources\n", - "- so agent just follows the resources and we can't really see the obstacle avoidance\n", - "- might need to put really few resources but not sure it will work" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -1001,13 +995,14 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "# Make all agents spawn\n", + "# Make all agents spawn with the exists flag\n", "for ag in controller.agents:\n", " ag.exists = True\n", + " agent.diet = [\"resources\"]\n", "\n", "agent_0 = controller.agents[0]\n", "agent_1 = controller.agents[1]" diff --git a/vivarium/controllers/notebook_controller.py b/vivarium/controllers/notebook_controller.py index 9eb2211..d04d64f 100644 --- a/vivarium/controllers/notebook_controller.py +++ b/vivarium/controllers/notebook_controller.py @@ -126,6 +126,8 @@ def __init__(self, config): self.active_behaviors = {} self.eating_range = 10 self.diet = [] + # TODO : see if we keep both + self.ate = False self.time_since_last_meal = np.inf self.simulation_entities = None self.logger = Logger() @@ -281,6 +283,15 @@ def stop_motors(self): self.left_motor = 0 self.right_motor = 0 + def has_eaten(self): + """Check if the agent has eaten + + :return: True if the agent has eaten, False otherwise + """ + val = self.ate + self.ate = False + return val + def has_eaten_since(self, time): """ Check if the agent has eaten since a given time @@ -450,7 +461,7 @@ def remove_entity_type(self, entity_type): def start_entity_apparition(self, interval=50, entity_type: str = None, position_range=None): """Start the apparition process for entities of type entity_type every period seconds - :param period: period, defaults to 5 + :param interval: execution interval, defaults to 50 :param entity_type: entity_type, defaults to None :param position_range: position range where entities can spawn, defaults to None """ @@ -463,13 +474,20 @@ def start_entity_apparition(self, interval=50, entity_type: str = None, position def start_resources_apparition(self, interval=50, position_range=None): """Start the resources apparition process - :param period: period, defaults to 5 + :param interval: execution interval, defaults to 5 :param position_range: position_range, defaults to None """ resources_type = "resources" self.start_entity_apparition(interval, entity_type=resources_type, position_range=position_range) - # start the eating routine for agents - self.attach_routine(eating_routine) + + def start_eating_mechanism(self, interval=10, proximeters_mode=False): + """Start the eating mechanism for all agents + + :param interval: execution interval, defaults to 10 + :param proximeters_mode: wether to only eat entities sensed by proximeters or not, defaults to False + """ + eating_routine = eating_routine_proximeters if proximeters_mode else eating_routine_classic + self.attach_routine(eating_routine, interval=interval) def stop_resources_apparition(self): """Stop the resources apparition process @@ -478,6 +496,14 @@ def stop_resources_apparition(self): self.detach_routine(spawn_entity_routine.__name__) else: lg.warning("Resources apparition is already stopped") + + def stop_eating_mechanism(self): + if eating_routine_classic.__name__ in self.routine_handler._routines: + self.detach_routine(eating_routine_classic.__name__) + elif eating_routine_proximeters.__name__ in self.routine_handler._routines: + self.detach_routine(eating_routine_proximeters.__name__) + else: + lg.warning("Eating mechanism is already stopped") def set_all_user_events(self): """Set all user events from clients (interface or notebooks) for all entities @@ -639,7 +665,9 @@ def non_existing_agents(self): return [agent for agent in self.agents if not agent.exists] -def spawn_entity_routine_fn(controller, entity_type=None, position_range=None): +# Predefined routines that can be attached to the controller + +def spawn_entity_routine(controller, entity_type=None, position_range=None): """Spawn entities of type entity_type every period seconds within a given position range :param period: period @@ -663,8 +691,8 @@ def spawn_entity_routine_fn(controller, entity_type=None, position_range=None): lg.info(f'All entities of type {entity_type} are spawned') -# TODO : Update old routine function for eating - """make agents of the simulation eating the entities in their diet +def eating_routine_classic(controller): + """Make agents eat entities if they are in their diet and eating range :param controller: NotebookController """ @@ -684,15 +712,15 @@ def spawn_entity_routine_fn(controller, entity_type=None, position_range=None): for arr_idx, ent_idx in enumerate(eatable_entities_idx): if in_range[arr_idx] and controller.all_entities[ent_idx].exists: controller.remove_entity(ent_idx) - # TODO : check here + agent.ate = True + # TODO : Implement this mechanism too agent.time_since_last_meal = 0 -def eating_routine(controller): - """Routine to make agents eat entities in their diet. - They can only eat entities sensed by their proximeters, and that are in their eating range. +def eating_routine_proximeters(controller): + """Make agents eat entities if they are in their diet, eating range and sensed by their proximeters - :param controller: _description_ + :param controller: NotebookController """ for agent in controller.existing_agents: left_prox, right_prox = agent.sensors() From a92079ff855672f32adb77d68d3f726471166b90 Mon Sep 17 00:00:00 2001 From: corentinlger Date: Tue, 26 Nov 2024 16:26:23 +0100 Subject: [PATCH 6/8] Fix notebook controller details --- vivarium/controllers/notebook_controller.py | 28 ++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/vivarium/controllers/notebook_controller.py b/vivarium/controllers/notebook_controller.py index d04d64f..6513a3a 100644 --- a/vivarium/controllers/notebook_controller.py +++ b/vivarium/controllers/notebook_controller.py @@ -192,6 +192,7 @@ def attach_behavior(self, behavior_fn, name=None, interval=1, weight=1.): assert isinstance(weight, (int, float)) and weight > 0, "Weight must be a positive number" self.behaviors[name or behavior_fn.__name__] = (behavior_fn, interval, weight) + # TODO : add a lock to the detach behavior and to the behave function to prevent the simulator from crashing --> MEDIUM PRIORITY def detach_behavior(self, name, stop_motors=False): """Detach a behavior from the agent and stop the motors if needed @@ -409,15 +410,13 @@ def update_agents_attributes(self): agent._subtype_label_to_idx = self._subtype_label_to_idx agent.valid_subtypes = self.valid_subtypes - # TODO : implement this function at the simulator controller level (not only for notebooks) def update_entities_attributes(self): """Temporary fn to give their subtype labels to all entities """ for ent in self.all_entities: ent.subtype_label = self._subtype_idx_to_label[ent.subtype] - # TODO : Clean mechanism to clean entity apparition (at seems like the entity is moving from a position to another) - # TODO : maybe add a little time.sleep() + # TODO : Clean mechanism to clean entity apparition (at seems like the entity is moving from a position to another), maybe add a time.sleep() --> LOW PRIORITY def spawn_entity(self, entity_idx, position=None): """Spawn an entity at a given position @@ -511,7 +510,7 @@ def set_all_user_events(self): for e in self.all_entities: e.set_events() - def run(self, threaded=True, num_steps=math.inf, catch_errors=False): + def run(self, threaded=True, num_steps=math.inf, catch_errors=True): """Run the simulation :param threaded: wether to run the simulation in a thread or not, defaults to True @@ -529,12 +528,15 @@ def run(self, threaded=True, num_steps=math.inf, catch_errors=False): else: self._run(num_steps=num_steps) - def _run(self, num_steps=math.inf, catch_errors=False): + def _run(self, num_steps=math.inf, catch_errors=True): """run the simulation for a given number of steps :param num_steps: num_steps, defaults to math.inf + :param catch_errors: wether to catch errors or not, defaults to False """ - while self.time < num_steps and self._is_running: + # Add a local time for the run function independant from the controller time + run_time = 0 + while run_time < num_steps and self._is_running: with self.batch_set_state(): # execute routines of the controller self.controller_routine_step(self.time, catch_errors=catch_errors) @@ -548,11 +550,15 @@ def _run(self, num_steps=math.inf, catch_errors=False): # execute behaviors of agents if entity.etype == EntityType.AGENT: entity.behave(self.time) + # TODO: see if we do this in a dedicated function or not + # increment time since last meal for all alive agents + entity.time_since_last_meal += 1 # update the attributes of all entities and do a step on server side self.state = self.client.step() self.pull_configs() self.time += 1 + run_time += 1 # finally stop the simulation self.stop() @@ -621,7 +627,6 @@ def print_fps(self, record_time=2, server=False): print(f"measuring the FPS (number of steps per second) in the {'server' if server else 'controller'} during {record_time} seconds") start_time = self.time if not server else self.server_time - def calculate_fps(): time.sleep(record_time) end_time = self.time if not server else self.server_time @@ -647,7 +652,6 @@ def box_size(self): """ return self.configs[StateType.SIMULATOR][0].box_size - # TODO: Test this in notebooks @property def existing_agents(self): """Return the list of existing agents @@ -690,7 +694,6 @@ def spawn_entity_routine(controller, entity_type=None, position_range=None): else: lg.info(f'All entities of type {entity_type} are spawned') - def eating_routine_classic(controller): """Make agents eat entities if they are in their diet and eating range @@ -713,9 +716,7 @@ def eating_routine_classic(controller): if in_range[arr_idx] and controller.all_entities[ent_idx].exists: controller.remove_entity(ent_idx) agent.ate = True - # TODO : Implement this mechanism too - agent.time_since_last_meal = 0 - + agent.time_since_last_meal = 0 def eating_routine_proximeters(controller): """Make agents eat entities if they are in their diet, eating range and sensed by their proximeters @@ -730,7 +731,6 @@ def eating_routine_proximeters(controller): controller.subtypes_labels[left_type] in agent.diet and (1. - left_prox) * agent.proxs_dist_max <= agent.eating_range ) - can_eat_right = ( controller.subtypes_labels[right_type] in agent.diet and (1. - right_prox) * agent.proxs_dist_max <= agent.eating_range @@ -745,8 +745,8 @@ def eating_routine_proximeters(controller): eating_choice = 0 if can_eat_left else 1 controller.remove_entity(agent.prox_sensed_ent_idx[eating_choice]) - # TODO : update the mechanism to update the has eaten since time agent.ate = True + agent.time_since_last_meal = 0 else: agent.ate = False From 33ddcf2c0ee156d0a0765961e7b7b79a3096c7b7 Mon Sep 17 00:00:00 2001 From: corentinlger Date: Tue, 26 Nov 2024 17:30:58 +0100 Subject: [PATCH 7/8] Fix controller pause and agents sensed_entities problems --- vivarium/controllers/notebook_controller.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/vivarium/controllers/notebook_controller.py b/vivarium/controllers/notebook_controller.py index 6513a3a..bae0ee5 100644 --- a/vivarium/controllers/notebook_controller.py +++ b/vivarium/controllers/notebook_controller.py @@ -156,15 +156,14 @@ def sensors(self, sensed_entities=None): return [left, right] def sensed_entities(self): - """Return the left and right sensed entities of the agent + """Return the left and right sensed entities of the agent if they are sensed, else None :return: sensed entities """ left_idx, right_idx = self.prox_sensed_ent_idx - return [ - self.simulation_entities[left_idx], - self.simulation_entities[right_idx] - ] + left_ent = self.simulation_entities[left_idx] if self.config.left_prox != 0 else None + right_ent = self.simulation_entities[right_idx] if self.config.right_prox != 0 else None + return [left_ent, right_ent] def sense_attributes(self, sensed_attribute, default_value=None): """Return the sensed attribute of the left and right sensed entities @@ -561,14 +560,19 @@ def _run(self, num_steps=math.inf, catch_errors=True): run_time += 1 # finally stop the simulation - self.stop() + self.pause() def stop(self): - """Stop the simulation + """Stop the simulation and detach all routines """ self._is_running = False self.routine_handler.detach_all_routines() + def pause(self): + """Pause the simulation + """ + self._is_running = False + def wait(self, seconds): """Wait for a given number of seconds From 491c98faa9d82bdc51ccb983e15654a369849f56 Mon Sep 17 00:00:00 2001 From: corentinlger Date: Tue, 26 Nov 2024 17:31:18 +0100 Subject: [PATCH 8/8] Update notebook sessions --- notebooks/sessions/session_4.ipynb | 375 +++++++++++++++-------- notebooks/sessions/session_5_bonus.ipynb | 266 +++++++++++++--- 2 files changed, 470 insertions(+), 171 deletions(-) diff --git a/notebooks/sessions/session_4.ipynb b/notebooks/sessions/session_4.ipynb index 9aae723..ae7ad12 100644 --- a/notebooks/sessions/session_4.ipynb +++ b/notebooks/sessions/session_4.ipynb @@ -126,6 +126,7 @@ "name": "stdout", "output_type": "stream", "text": [ + "/home/cleger/Desktop/code/vivarium/vivarium/utils\n", "STARTING SERVER\n" ] }, @@ -140,8 +141,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "[2024-11-06 14:34:50,711][__main__][INFO] - Scene running: session_4\n", - "[2024-11-06 14:34:53,220][vivarium.simulator.simulator][INFO] - Simulator initialized\n", + "[2024-11-26 17:12:26,026][__main__][INFO] - Scene running: session_4\n", + "[2024-11-26 17:12:28,522][vivarium.simulator.simulator][INFO] - Simulator initialized\n", "\n", "STARTING INTERFACE\n" ] @@ -150,13 +151,13 @@ "name": "stderr", "output_type": "stream", "text": [ - "2024-11-06 14:34:55,261 Starting Bokeh server version 3.3.4 (running on Tornado 6.4)\n", - "2024-11-06 14:34:55,262 User authentication hooks NOT provided (default user enabled)\n", - "2024-11-06 14:34:55,264 Bokeh app running at: http://localhost:5006/run_interface\n", - "2024-11-06 14:34:55,264 Starting Bokeh server with process id: 8283\n", - "2024-11-06 14:34:57,811 An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n", - "2024-11-06 14:34:59,707 WebSocket connection opened\n", - "2024-11-06 14:34:59,738 ServerConnection created\n" + "2024-11-26 17:12:30,419 Starting Bokeh server version 3.3.4 (running on Tornado 6.4)\n", + "2024-11-26 17:12:30,420 User authentication hooks NOT provided (default user enabled)\n", + "2024-11-26 17:12:30,421 Bokeh app running at: http://localhost:5006/run_interface\n", + "2024-11-26 17:12:30,421 Starting Bokeh server with process id: 50178\n", + "2024-11-26 17:12:32,321 An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n", + "2024-11-26 17:12:34,015 WebSocket connection opened\n", + "2024-11-26 17:12:34,044 ServerConnection created\n" ] } ], @@ -166,46 +167,53 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [], - "source": [ - "controller = NotebookController()" - ] - }, - { - "cell_type": "code", - "execution_count": 7, + "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "Error while executing routine: 'super' object has no attribute '__getattr__', removing routine foraging_drive\n", - "Error while executing routine: 'super' object has no attribute '__getattr__', removing routine foraging_drive\n" + "/home/cleger/Desktop/code/vivarium/venv/lib/python3.10/site-packages/jax/_src/ops/scatter.py:96: FutureWarning: scatter inputs have incompatible types: cannot safely cast value from dtype=float32 to dtype=int32 with jax_numpy_dtype_promotion='standard'. In future JAX releases this will result in an error.\n", + " warnings.warn(\n" ] } ], + "source": [ + "controller = NotebookController()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], "source": [ "controller.run()" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 7, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "FPS: 162.00 (number of steps per second)\n" + "measuring the FPS (number of steps per second) in the controller during 2 seconds\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "FPS: 25.00\n" ] } ], "source": [ - "controller.get_fps()" + "controller.print_fps()" ] }, { @@ -245,7 +253,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -256,7 +264,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -271,26 +279,24 @@ ], "source": [ "for agent in controller.agents:\n", - " agent.check_behaviors()" + " agent.print_behaviors()" ] }, { "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "controller.start_resources_apparition(period=10)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ + "# Update agents diet\n", "for agent in controller.agents:\n", - " agent.diet = [\"resources\"]" + " agent.diet = [\"resources\"]\n", + "\n", + "# Start resources apparition\n", + "controller.start_resources_apparition(interval=60)\n", + "\n", + "# Start the eating mechanism\n", + "controller.start_eating_mechanism(interval=10)" ] }, { @@ -330,7 +336,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -344,7 +350,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -372,7 +378,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -402,6 +408,58 @@ "It is also possible to specify the weight of each running behavior, i.e. how much it will count in the averaging. This is done by returning three values in the function defining a behavior (instead of two as we were doing until now: one for the left wheel activation and one for the right one). For example, if we want to run the `obstacle_avoidance` behavior with a weight of 1 and the `fear` behavior with a weight of 0.5, we write:" ] }, + { + "cell_type": "code", + "execution_count": 29, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{}\n" + ] + } + ], + "source": [ + "print(controller.routine_handler._routines)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "metadata": {}, + "outputs": [], + "source": [ + "controller.pause()" + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [], + "source": [ + "controller.run()" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "metadata": {}, + "outputs": [], + "source": [ + "# Update agents diet\n", + "for agent in controller.agents:\n", + " agent.diet = [\"resources\"]\n", + "\n", + "# Start resources apparition\n", + "controller.start_resources_apparition(interval=60)\n", + "\n", + "# Start the eating mechanism\n", + "controller.start_eating_mechanism(interval=10)" + ] + }, { "cell_type": "code", "execution_count": null, @@ -411,7 +469,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -443,7 +501,7 @@ " robot.attach_behavior(obstacle_avoidance, weight=1)\n", " robot.attach_behavior(fear, weight=0.5)\n", " robot.start_all_behaviors()\n", - " robot.check_behaviors()" + " robot.print_behaviors()" ] }, { @@ -464,24 +522,32 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "This weighting is particularly useful to activate a behavior according to some internal states of the robot. Let's consider a robot that eats spheres (as in the previous session) and that eating those spheres allows to raise a simulated energy level of the robot. We want to continuously compute the energy level of the robot according to how much spheres it has recently eaten. To do so, we first need a way to know when a robot eats a sphere. For example to check it on robot 2, this is done by executing:" + "This weighting is particularly useful to activate a behavior according to some internal states of the robot. Let's consider a robot that eats spheres (as in the previous session) and that eating those spheres allows to raise a simulated energy level of the robot. We want to continuously compute the energy level of the robot according to how much spheres it has recently eaten. To do so, we first need a way to know when a robot has eaten for the last time. There are several ways of doing this that can serve in different use cases:\n", + "\n", + "- agent.has_eaten(): function that tells if the agent has eaten since the last call to this function, defaults to False\n", + "- agent.time_since_last_meal: attribute that tells the time since the last meal, defaults to infinity when the agent has never eaten\n", + "- agent.has_eaten_since(t): function that tells if the agent has eaten since time t" ] }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 33, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "True\n" + "True\n", + "101\n", + "False\n" ] } ], "source": [ - "print(robot_2.has_eaten())" + "print(robot_2.has_eaten())\n", + "print(robot_2.time_since_last_meal)\n", + "print(robot_2.has_eaten_since(50))" ] }, { @@ -493,22 +559,22 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 34, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "robot 0 has eaten : True\n", - "robot 1 has eaten : True\n", - "robot 2 has eaten : True\n" + "robot 0: -ate: False -time since last meal: inf\n", + "robot 1: -ate: True -time since last meal: 0\n", + "robot 2: -ate: False -time since last meal: 110\n" ] } ], "source": [ "for robot in controller.agents:\n", - " print(f\"robot {robot.idx} has eaten : {robot.has_eaten()}\")" + " print(f\"robot {robot.idx}: -ate: {robot.has_eaten()} -time since last meal: {robot.time_since_last_meal}\")" ] }, { @@ -520,7 +586,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 35, "metadata": { "collapsed": true }, @@ -535,18 +601,42 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "measuring the FPS (number of steps per second) in the controller during 2 seconds\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "FPS: 26.00\n" + ] + } + ], + "source": [ + "controller.print_fps()" + ] + }, + { + "cell_type": "code", + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "# First start sphere apparition faster in the environment:\n", - "controller.stop_entity_apparition()\n", - "controller.start_entity_apparition(period=5, entity_type=\"resources\")" + "controller.stop_resources_apparition()\n", + "controller.start_resources_apparition(interval=100)" ] }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -558,23 +648,6 @@ " robot.start_all_behaviors()" ] }, - { - "cell_type": "code", - "execution_count": 29, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "FPS: 132.00 (number of steps per second)\n" - ] - } - ], - "source": [ - "controller.get_fps()" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -596,7 +669,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -606,7 +679,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 40, "metadata": {}, "outputs": [ { @@ -627,7 +700,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 41, "metadata": { "collapsed": true }, @@ -654,7 +727,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 42, "metadata": { "collapsed": true }, @@ -705,14 +778,14 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 43, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "97.12999999999853\n" + "59.18000000000016\n" ] } ], @@ -722,14 +795,14 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 44, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "96.69999999999831\n" + "59.05000000000019\n" ] } ], @@ -776,7 +849,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 45, "metadata": { "collapsed": true }, @@ -795,7 +868,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 46, "metadata": { "collapsed": true }, @@ -825,60 +898,127 @@ "This cell above calls the `prox_activations` function of `robot3` with an additional argument, `return_robots`, which is set to `True`. When called like this, `prox_activations` will return the left and right proximeter activations as usual, as well as the references to the robots that are sensed by the proximeters, if any. Then we can access a specific attribute of those robots by executing:" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To return sensed entities by left and right proximeters, we can use the `sensed_entities` function of the agents. This returns the left and right entities if they are sensed, and `None` otherwise. " + ] + }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 67, + "metadata": {}, + "outputs": [], + "source": [ + "ent_l, ent_r = robot_0.sensed_entities()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can check what are the entities detected by a robot by running the following cell:" + ] + }, + { + "cell_type": "code", + "execution_count": 69, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Left:\n", - "Entity Overview:\n", - "--------------------\n", - "Type: OBJECT\n", - "Subtype: b_obstacles\n", - "Idx: 29\n", - "Position: x=67.39, y=178.52\n", + "Left entity:\n", + "No entity sensed\n", "\n", - "Right:\n", + "Right entity:\n", "Entity Overview:\n", "--------------------\n", "Type: OBJECT\n", - "Subtype: s_obstacles\n", - "Idx: 18\n", - "Position: x=110.49, y=121.34\n", + "Subtype: resources\n", + "Idx: 9\n", + "Exists: True\n", + "Position: x=172.37, y=274.10\n", "\n" ] } ], "source": [ + "# Give a specific color to robot_0 to recognize him\n", + "robot_0.color = \"black\"\n", + "# Sense entities on the left and right of robot_0\n", "ent_l, ent_r = robot_0.sensed_entities()\n", - "print(\"Left:\")\n", - "ent_l.infos()\n", "\n", - "print(\"Right:\")\n", - "ent_r.infos()" + "# Print the infos of the sensed entities\n", + "print(\"Left entity:\")\n", + "if ent_l:\n", + " ent_l.print_infos()\n", + "else:\n", + " print(\"No entity sensed\")\n", + "\n", + "print(\"\\nRight entity:\")\n", + "if ent_r:\n", + " ent_r.print_infos()\n", + "else:\n", + " print(\"No entity sensed\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also directly sense specific attributes of these sensed entities with the `sense_attributes` function. Make sure to specify the `sensed_attribute` you want to sense (e.g 'diameter', 'species', etc.). You can also specify the `default_value` value to return if the attribute is not found (otherwise it is set to None). The function will return the given attribute of the left and right sensed entities if it exists (it can be either if the entity doesn't exist, or because the entity doesn't possess the specific attribute), and the default value otherwise." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For example, each entity has a diameter, so if you see a `None` value, it means that the entity isn't sensed by the agent." ] }, { "cell_type": "code", - "execution_count": 30, + "execution_count": null, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Left: None Right: None\n" + "Left: None Right: 5.0\n" ] } ], "source": [ - "# attribute = \"x_position\"\n", - "attribute = \"species\"\n", - "l_attr, r_attr = robot_0.sense_attributes(sensed_attribute=attribute)\n", + "l_attr, r_attr = robot_0.sense_attributes(sensed_attribute=\"diameter\",)\n", + "print(f\"Left: {l_attr} Right: {r_attr}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "For this other case, we only defined species for the robots. We set the default value to 'unknown', so if an entity isn't detected, or doesn't have the attribute, it will return 'unknown'." + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Left: unknown Right: unknown\n" + ] + } + ], + "source": [ + "l_attr, r_attr = robot_0.sense_attributes(sensed_attribute=\"species\", default_value=\"unknown\")\n", "print(f\"Left: {l_attr} Right: {r_attr}\")" ] }, @@ -900,7 +1040,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 73, "metadata": { "collapsed": true }, @@ -923,7 +1063,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 74, "metadata": {}, "outputs": [], "source": [ @@ -935,7 +1075,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 76, "metadata": {}, "outputs": [ { @@ -947,7 +1087,7 @@ } ], "source": [ - "robot_2.check_behaviors()\n", + "robot_2.print_behaviors()\n", "robot_2.color = \"silver\"" ] }, @@ -960,7 +1100,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 77, "metadata": { "collapsed": true }, @@ -987,7 +1127,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 78, "metadata": { "collapsed": true }, @@ -999,7 +1139,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 79, "metadata": { "collapsed": true }, @@ -1017,14 +1157,7 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": 37, + "execution_count": 82, "metadata": {}, "outputs": [ { @@ -1067,7 +1200,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "venv", "language": "python", "name": "python3" }, diff --git a/notebooks/sessions/session_5_bonus.ipynb b/notebooks/sessions/session_5_bonus.ipynb index aab7061..60efe57 100644 --- a/notebooks/sessions/session_5_bonus.ipynb +++ b/notebooks/sessions/session_5_bonus.ipynb @@ -22,19 +22,10 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n" - ] - } - ], + "outputs": [], "source": [ "import numpy as np\n", "\n", - "from vivarium.controllers.notebook_controller import NotebookController\n", "from vivarium.utils.handle_server_interface import start_server_and_interface, stop_server_and_interface" ] }, @@ -62,24 +53,11 @@ "name": "stdout", "output_type": "stream", "text": [ - "[2024-11-25 14:48:18,993][__main__][INFO] - Scene running: session_5\n", - "[2024-11-25 14:48:21,668][vivarium.simulator.simulator][INFO] - Simulator initialized\n", + "[2024-11-26 11:44:55,762][__main__][INFO] - Scene running: session_5\n", + "[2024-11-26 11:44:58,396][vivarium.simulator.simulator][INFO] - Simulator initialized\n", "\n", "STARTING INTERFACE\n" ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2024-11-25 14:48:23,580 Starting Bokeh server version 3.3.4 (running on Tornado 6.4)\n", - "2024-11-25 14:48:23,580 User authentication hooks NOT provided (default user enabled)\n", - "2024-11-25 14:48:23,582 Bokeh app running at: http://localhost:5006/run_interface\n", - "2024-11-25 14:48:23,582 Starting Bokeh server with process id: 33727\n", - "2024-11-25 14:48:36,107 An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n", - "2024-11-25 14:48:38,248 WebSocket connection opened\n", - "2024-11-25 14:48:38,298 ServerConnection created\n" - ] } ], "source": [ @@ -102,12 +80,19 @@ "name": "stderr", "output_type": "stream", "text": [ + "2024-11-26 11:45:00,237 Starting Bokeh server version 3.3.4 (running on Tornado 6.4)\n", + "2024-11-26 11:45:00,237 User authentication hooks NOT provided (default user enabled)\n", + "2024-11-26 11:45:00,239 Bokeh app running at: http://localhost:5006/run_interface\n", + "2024-11-26 11:45:00,239 Starting Bokeh server with process id: 22097\n", + "An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n", "/home/cleger/Desktop/code/vivarium/venv/lib/python3.10/site-packages/jax/_src/ops/scatter.py:96: FutureWarning: scatter inputs have incompatible types: cannot safely cast value from dtype=float32 to dtype=int32 with jax_numpy_dtype_promotion='standard'. In future JAX releases this will result in an error.\n", " warnings.warn(\n" ] } ], "source": [ + "from vivarium.controllers.notebook_controller import NotebookController\n", + "\n", "controller = NotebookController()" ] }, @@ -252,13 +237,22 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 13, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False\n" + ] + } + ], "source": [ "agent_idx = 4 # idx of a dead agent\n", "agent = controller.agents[agent_idx]\n", - "agent.exists = True # make the agent spawn" + "agent.exists = True # make the agent spawn\n", + "print(agent.exists)" ] }, { @@ -270,16 +264,16 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 10, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" } @@ -323,7 +317,9 @@ "name": "stderr", "output_type": "stream", "text": [ - "Entity 5 already removed\n" + "2024-11-26 11:45:18,584 An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n", + "2024-11-26 11:45:20,336 WebSocket connection opened\n", + "2024-11-26 11:45:20,386 ServerConnection created\n" ] } ], @@ -348,7 +344,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 32, "metadata": {}, "outputs": [ { @@ -379,7 +375,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 33, "metadata": {}, "outputs": [ { @@ -416,25 +412,186 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Attach behaviors to existing agents (you can technically also attach them to dead agents but it won't have any effect):" + "Attach behaviors to existing agents (you can technically also attach them to dead agents but it won't have any effect, since their behaviors won't be executed):" ] }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 15, "metadata": {}, "outputs": [], + "source": [ + "for agent in controller.existing_agents:\n", + " agent.attach_behavior(obstacle_avoidance)\n", + " agent.attach_behavior(love_cuddly)\n", + " agent.start_all_behaviors()" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "agent 0: exists = True\n", + "agent 1: exists = True\n", + "agent 2: exists = True\n", + "agent 3: exists = True\n", + "agent 4: exists = True\n", + "agent 5: exists = True\n", + "agent 6: exists = False\n", + "agent 7: exists = False\n", + "agent 8: exists = False\n", + "agent 9: exists = False\n" + ] + } + ], "source": [ "for agent in controller.agents:\n", - " if agent.exists == True:\n", - " agent.attach_behavior(obstacle_avoidance)\n", - " agent.attach_behavior(love_cuddly)\n", - " agent.start_all_behaviors()" + " print(f\"agent {agent.idx}: exists = {agent.exists}\")" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 29, + "metadata": {}, + "outputs": [], + "source": [ + "def old():\n", + " \"\"\"make agents of the simulation eating the entities in their diet\n", + "\n", + " :param controller: NotebookController\n", + " \"\"\"\n", + " for agent in controller.agents:\n", + " # skip to next agent if the agent does not exist\n", + " if not agent.exists:\n", + " continue\n", + " for entity_type in agent.diet:\n", + " assert entity_type in controller.valid_subtypes, f\"Please specify a valid entity type among {controller.valid_subtypes}, for agent {agent.idx} diet : {agent.diet} \"\n", + " # transform the entity type label into an idx\n", + " entity_type = controller._subtype_label_to_idx[entity_type]\n", + " # get the idx of entities that are eatable by the agent (by precaution remove the agent itself)\n", + " eatable_entities_idx = [ent.idx for ent in controller.all_entities if ent.subtype == entity_type and ent.idx != agent.idx]\n", + " distances = agent.config.proximity_map_dist[eatable_entities_idx]\n", + " in_range = distances < agent.eating_range\n", + " # arr_idx is the index of the in_range array\n", + " for arr_idx, ent_idx in enumerate(eatable_entities_idx):\n", + " if in_range[arr_idx] and controller.all_entities[ent_idx].exists:\n", + " controller.remove_entity(ent_idx)\n", + " # TODO : check here \n", + " agent.time_since_last_meal = 0 \n", + "\n", + "\n", + "def new():\n", + " \"\"\"Routine to make agents eat entities in their diet. \n", + " They can only eat entities sensed by their proximeters, and that are in their eating range.\n", + "\n", + " :param controller: _description_\n", + " \"\"\"\n", + " for agent in controller.existing_agents:\n", + " left_prox, right_prox = agent.sensors()\n", + " left_type, right_type = agent.prox_sensed_ent_type\n", + "\n", + " can_eat_left = (\n", + " controller.subtypes_labels[left_type] in agent.diet and\n", + " (1. - left_prox) * agent.proxs_dist_max <= agent.eating_range\n", + " )\n", + "\n", + " can_eat_right = (\n", + " controller.subtypes_labels[right_type] in agent.diet and\n", + " (1. - right_prox) * agent.proxs_dist_max <= agent.eating_range\n", + " )\n", + "\n", + " # if the agent can eat\n", + " if can_eat_left or can_eat_right:\n", + " # determine which side to eat\n", + " if can_eat_left and can_eat_right:\n", + " eating_choice = np.random.choice([0, 1])\n", + " else:\n", + " eating_choice = 0 if can_eat_left else 1\n", + " \n", + " controller.remove_entity(agent.prox_sensed_ent_idx[eating_choice])\n", + " # TODO : update the mechanism to update the has eaten since time\n", + " agent.ate = True\n", + " else:\n", + " agent.ate = False\n", + "\n", + "\n", + "def clem():\n", + " \"\"\"make agents of the simulation eating the entities in their diet\n", + "\n", + " :param controller: NotebookController\n", + " \"\"\"\n", + "\n", + " for agent in controller.agents:\n", + " if agent.exists:\n", + " left, right = agent.config.left_prox, agent.config.right_prox\n", + " sensed_type_left, sensed_type_right = agent.prox_sensed_ent_type\n", + " eat_left = controller.subtypes_labels[sensed_type_left] in agent.diet and (1. - left) * agent.proxs_dist_max <= agent.eating_range\n", + " eat_right = controller.subtypes_labels[sensed_type_right] in agent.diet and (1. - right) * agent.proxs_dist_max <= agent.eating_range\n", + " ate = True\n", + " if eat_left:\n", + " if eat_right:\n", + " which = np.random.rand() < 0.5\n", + " controller.remove_entity(agent.prox_sensed_ent_idx[int(which)])\n", + " else:\n", + " controller.remove_entity(agent.prox_sensed_ent_idx[0])\n", + " else:\n", + " if eat_right:\n", + " controller.remove_entity(agent.prox_sensed_ent_idx[1])\n", + " else:\n", + " ate = False\n", + " agent.ate = ate\n", + "\n", + " return" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Old took 0.806134 seconds\n", + "New took 1.754245 seconds\n", + "Clem took 1.760777 seconds\n" + ] + } + ], + "source": [ + "import timeit\n", + "\n", + "from functools import partial\n", + "\n", + "num_iterations = 1000\n", + "\n", + "new = timeit.timeit(new, number=num_iterations)\n", + "old = timeit.timeit(old, number=num_iterations)\n", + "clem = timeit.timeit(clem, number=num_iterations)\n", + "\n", + "print(f\"Old took {old:.6f} seconds\")\n", + "print(f\"New took {new:.6f} seconds\")\n", + "print(f\"Clem took {clem:.6f} seconds\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "controller.stop()" + ] + }, + { + "cell_type": "code", + "execution_count": 35, "metadata": {}, "outputs": [ { @@ -461,7 +618,16 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 37, + "metadata": {}, + "outputs": [], + "source": [ + "controller.run(catch_errors=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 38, "metadata": {}, "outputs": [], "source": [ @@ -514,7 +680,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -529,7 +695,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -547,7 +713,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 41, "metadata": {}, "outputs": [], "source": [ @@ -563,7 +729,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 42, "metadata": {}, "outputs": [], "source": [ @@ -579,7 +745,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 43, "metadata": {}, "outputs": [], "source": [ @@ -596,7 +762,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ @@ -608,7 +774,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 45, "metadata": {}, "outputs": [], "source": [ @@ -1377,7 +1543,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 31, "metadata": {}, "outputs": [ {