diff --git a/simulations/validation/conviction_cadCAD.ipynb b/simulations/validation/conviction_cadCAD.ipynb
index c7b219f..494e28b 100644
--- a/simulations/validation/conviction_cadCAD.ipynb
+++ b/simulations/validation/conviction_cadCAD.ipynb
@@ -34,7 +34,7 @@
"\n",
"cadCAD is a Python library that assists in the processes of designing, testing and validating complex systems through simulation. At its core, cadCAD is a differential games engine that supports parameter sweeping and Monte Carlo analyses and can be easily integrated with other scientific computing Python modules and data science workflows.\n",
"\n",
- "To learn more about cadCAD, follow our [tutorial series](https://github.com/BlockScience/cadCAD-Tutorials/tree/master/01%20Tutorials)\n",
+ "To learn more about cadCAD, follow our [tutorial series](../../tutorials)\n",
"\n",
"**Installing cadCAD:**\n",
"\n",
diff --git a/tutorials/README.md b/tutorials/README.md
new file mode 100644
index 0000000..2d7ecef
--- /dev/null
+++ b/tutorials/README.md
@@ -0,0 +1,10 @@
+**Robot and Marbles Tutorial Series**
+
+In this series, we introduce basic concepts of cadCAD and system modelling in general using a simple toy model.
+[Part 1](robot-marbles-part-1/robot-marbles-part-1.ipynb) - States and State Update Functions
+[Part 2](robot-marbles-part-2/robot-marbles-part-2.ipynb) - Actions and State Dependent Policies
+[Part 3](robot-marbles-part-3/robot-marbles-part-3.ipynb) - From Synchronous to Asynchronous Time
+[Part 4](robot-marbles-part-4/robot-marbles-part-4.ipynb) - Uncertainty and Stochastic Processes
+[Part 5](robot-marbles-part-5/robot-marbles-part-5.ipynb) - Using class objects as state variables
+
+Check out the [videos](videos) folder for detailed walkthroughs of each one of the tutorials.
\ No newline at end of file
diff --git a/tutorials/robot-marbles-part-1/partial-state-update-blocks.png b/tutorials/robot-marbles-part-1/partial-state-update-blocks.png
new file mode 100644
index 0000000..746a748
Binary files /dev/null and b/tutorials/robot-marbles-part-1/partial-state-update-blocks.png differ
diff --git a/tutorials/robot-marbles-part-1/robot-marbles-part-1.ipynb b/tutorials/robot-marbles-part-1/robot-marbles-part-1.ipynb
new file mode 100644
index 0000000..2cbf87f
--- /dev/null
+++ b/tutorials/robot-marbles-part-1/robot-marbles-part-1.ipynb
@@ -0,0 +1,639 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# cadCAD Tutorials: The Robot and the Marbles, part 1\n",
+ "## What is cadCAD?\n",
+ "cadCAD is a Python library that assists in the processes of designing, testing and validating complex systems through simulation. At its core, cadCAD is a differential games engine that supports parameter sweeping and Monte Carlo analyses and can be easily integrated with other scientific computing Python modules and data science workflows.\n",
+ "\n",
+ "At [BlockScience](http://bit.ly/github_articles_M_1), we've been using cadCAD as a tool for [Token Engineering](https://medium.com/block-science/on-the-practice-of-token-engineering-part-i-c2cc2434e727) - the design of self-organizing systems enabled through cryptographic peer-to-peer networks. But cadCAD can simulate any system that can be described as state variables that evolve over time according to a set of equations. This series of articles will go over the basic concepts of cadCAD and the 'language' in which a system must be described in order for it to be interpretable by the library. This article will cover the following concepts:\n",
+ "* State Variables\n",
+ "* Timestep\n",
+ "* State Update Functions\n",
+ "* Partial State Update Blocks\n",
+ "* Simulation Configuration Parameters\n",
+ "\n",
+ "To help illustrate, we'll refer to a simple example system\n",
+ "\n",
+ "__The robot and the marbles__ \n",
+ "* Picture a box (`box_A`) with ten marbles in it; an empty box (`box_B`) next to the first one; and a robot arm capable of taking a marble from any one of the boxes and dropping it into the other one. \n",
+ "* The robot is programmed to take one marble at a time from the box containing the largest number of marbles and drop it in the other box. It repeats that process until the boxes contain an equal number of marbles. \n",
+ "\n",
+ "## State Variables\n",
+ ">A state variable is one of the set of variables that are used to describe the mathematical \"state\" of a dynamical system. Intuitively, the state of a system describes enough about the system to determine its future behaviour in the absence of any external forces affecting the system. [_(source: Wikipedia)_](https://en.wikipedia.org/wiki/State_variable)\n",
+ "\n",
+ "cadCAD can handle state variables of any Python data type, including custom classes. It is up to the user of cadCAD to determine the state variables needed to sufficiently accurately describe the system they are interested in.\n",
+ "\n",
+ "We could describe the simple system in our example with only two state variables: the number of marbles in `box_A` and in `box_B`. These are not the only two variables that describe the system, of course. Things like the position of the robot arm in space or its temperature also qualify as \"variables that describe the state of the system\". But if we assume those variables have no impact on the behavior of the system (as implied by the description) we can safely disregard them.\n",
+ "\n",
+ "cadCAD expects state variables to be passed to it as a Python `dict` where the `dict_keys` are the __names of the \n",
+ "variables__ and the `dict_values` are their __initial values__."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# List of all the state variables in the system and their initial values\n",
+ "initial_conditions = {\n",
+ " 'box_A': 10, # as per the description of the example, box_A starts out with 10 marbles in it\n",
+ " 'box_B': 0 # as per the description of the example, box_B starts out empty\n",
+ "}\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Timestep\n",
+ "Computer simulations run in discrete time:\n",
+ ">Discrete time views values of variables as occurring at distinct, separate \"points in time\", or equivalently as being unchanged throughout each non-zero region of time (\"time period\")—that is, time is viewed as a discrete variable. Thus a non-time variable jumps from one value to another as time moves from one time period to the next. This view of time corresponds to a digital clock that gives a fixed reading of 10:37 for a while, and then jumps to a new fixed reading of 10:38, etc. [_(source: Wikipedia)_](https://en.wikipedia.org/wiki/Discrete_time_and_continuous_time)\n",
+ "\n",
+ "The concept of Timestep in cadCAD refers to a discrete unit of time. cadCAD increments a \"time counter\", and at each step it updates the state variables according to the equations that describe the system. If time itself is a state variable that the user is interested in keeping track of, they may specify a state update function that models the passing of time. We'll cover that scenario in an upcoming article. For the moment, it suffices to define that the robot acts once per timestep.\n",
+ "# State Update Functions\n",
+ "State Update Functions are Python functions that represent the equations according to which the state variables change over time. Each state update function must return a tuple containing the name of the state variable being updated and its new value. The general structure of a state update function is:\n",
+ "```python\n",
+ "def function(params, step, sL, s, _input):\n",
+ " ...\n",
+ " y = ...\n",
+ " x = ...\n",
+ " return (y, x)\n",
+ "```\n",
+ "State update functions can read the current state of the system from argument `s`. We'll ignore the other arguments for now. `s` is a Python `dict` where the `dict_keys` are the __names of the variables__ and the `dict_values` are their __current values__. With this, we can define the state update functions for variables `box_A` and `box_B`."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "def update_A(params, step, sL, s, _input):\n",
+ " y = 'box_A'\n",
+ " add_to_A = 0\n",
+ " if (s['box_A'] > s['box_B']):\n",
+ " add_to_A = -1\n",
+ " elif (s['box_A'] < s['box_B']):\n",
+ " add_to_A = 1\n",
+ " x = s['box_A'] + add_to_A\n",
+ " return (y, x)\n",
+ "\n",
+ "def update_B(params, step, sL, s, _input):\n",
+ " y = 'box_B'\n",
+ " add_to_B = 0\n",
+ " if (s['box_B'] > s['box_A']):\n",
+ " add_to_B = -1\n",
+ " elif (s['box_B'] < s['box_A']):\n",
+ " add_to_B = 1\n",
+ " x = s['box_B'] + add_to_B\n",
+ " return (y, x)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Partial State Update Blocks\n",
+ "Within a timestep, state update functions can be run in any combination of serial or parallel executions. Take the following diagram for example:\n",
+ "
Figure 1: Visual representation of Partial State Update Blocks
\n",
+ "\n",
+ "State update functions (SUF) 1 and 2 are run in parallel. This means that if SUF2 reads the value of variable A, it will not get the value updated by SUF1. On the other hand, SUF3 and SUF4 are executed after SUF1 and SUF2 have completed, thus having access to the updated values of variables A and C.\n",
+ "\n",
+ "We refer to the groups of state update functions that are executed in parallel within a timestep as Partial State Update Blocks. cadCAD expects partial state update blocks to be specified as a list of `dict`s with the following structure:\n",
+ "```python\n",
+ "partial_state_update_blocks = [\n",
+ " { \n",
+ " 'policies': {\n",
+ " 'policy1': policy_function_1,\n",
+ " 'policy2': policy_function_2,\n",
+ " ...\n",
+ " },\n",
+ " 'variables': {\n",
+ " 'variable1': state_update_function_1,\n",
+ " 'variable2': state_update_function_2,\n",
+ " ...\n",
+ " }\n",
+ " },\n",
+ " ...\n",
+ "]\n",
+ "```\n",
+ "\n",
+ "We'll ignore the `policies` key for now. The `dict` that represents the structure of Figure 1 would be:\n",
+ "```python\n",
+ "partial_state_update_blocks = [\n",
+ " { \n",
+ " 'policies': {\n",
+ " },\n",
+ " 'variables': {\n",
+ " 'variableA': state_update_function_1,\n",
+ " 'variableC': state_update_function_2,\n",
+ " }\n",
+ " },\n",
+ " { \n",
+ " 'policies': {\n",
+ " },\n",
+ " 'variables': {\n",
+ " 'variableA': state_update_function_3,\n",
+ " 'variableB': state_update_function_4,\n",
+ " }\n",
+ " }\n",
+ "]\n",
+ "```\n",
+ "\n",
+ "In the case of our robot and marbles example system, we can model the system so that all state update functions are executed in parallel. In other words, we consider the marbles move from one box to the other simultaneously (ie, `box_A + box_B` is constant)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# In the Partial State Update Blocks, the user specifies if state update functions will be run in series or in parallel\n",
+ "partial_state_update_blocks = [\n",
+ " { \n",
+ " 'policies': { # We'll ignore policies for now\n",
+ " },\n",
+ " 'variables': { # The following state variables will be updated simultaneously\n",
+ " 'box_A': update_A,\n",
+ " 'box_B': update_B\n",
+ " }\n",
+ " }\n",
+ "]\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Simulation Configuration Parameters\n",
+ "Lastly, we define the number of timesteps and the number of Monte Carlo runs of the simulation. These parameters must be passed in a dictionary, in `dict_keys` `T` and `N`, respectively. In our example, we'll run the simulation for 10 timesteps. And because we are dealing with a deterministic system, it makes no sense to have multiple Monte Carlo runs, so we set `N=1`. We'll ignore the `M` key for now and set it to an empty `dict`"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# Settings of general simulation parameters, unrelated to the system itself\n",
+ "# `T` is a range with the number of discrete units of time the simulation will run for;\n",
+ "# `N` is the number of times the simulation will be run (Monte Carlo runs)\n",
+ "# In this example, we'll run the simulation once (N=1) and its duration will be of 10 timesteps\n",
+ "# We'll cover the `M` key in a future article. For now, let's leave it empty\n",
+ "simulation_parameters = {\n",
+ " 'T': range(10),\n",
+ " 'N': 1,\n",
+ " 'M': {}\n",
+ "}\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # "
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Putting it all together\n",
+ "We have defined the state variables of our system and their initial conditions, as well as the state update functions, which have been grouped in a single state update block. We have also specified the parameters of the simulation (number of timesteps and runs). We are now ready to put all those pieces together in a `Configuration` object."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from cadCAD.configuration import Configuration\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# The configurations above are then packaged into a `Configuration` object\n",
+ "config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values\n",
+ " partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n",
+ " sim_config=simulation_parameters #dict containing simulation parameters\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Running the engine\n",
+ "We are now ready to run the engine with the configuration defined above. Instantiate an ExecutionMode, an ExecutionContext and an Executor objects, passing the Configuration object to the latter. Then run the `execute()` method of the Executor object, which returns the results of the experiment in the first element of a tuple."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%capture\n",
+ "from cadCAD.engine import ExecutionMode, ExecutionContext, Executor\n",
+ "exec_mode = ExecutionMode()\n",
+ "exec_context = ExecutionContext(exec_mode.single_proc)\n",
+ "executor = Executor(exec_context, [config]) # Pass the configuration object inside an array\n",
+ "raw_result, tensor = executor.execute() # The `execute()` method returns a tuple; its first elements contains the raw results"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Analyzing the results\n",
+ "We can now convert the raw results into a DataFrame for analysis"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "df.plot('timestep', ['box_A', 'box_B'], grid=True, \n",
+ " colormap = 'RdYlGn',\n",
+ " xticks=list(df['timestep'].drop_duplicates()), \n",
+ " yticks=list(range(1+(df['box_A']+df['box_B']).max())));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As was to be expected, the system oscilates between 5 and 6 marbles in each box.\n",
+ "\n",
+ "In the next article of this series we'll cover another base concept in cadCAD: **policies**.\n",
+ "\n",
+ "---\n",
+ "\n",
+ "_About BlockScience_ \n",
+ "[BlockScience](http://bit.ly/github_articles_M_1) is a research and engineering firm specialized in complex adaptive systems and applying practical methodologies from engineering design, development and testing to projects in emerging technologies such as blockchain. Follow us on [Medium](http://bit.ly/bsci-medium) or [Twitter](http://bit.ly/bsci-twitter) to stay in touch."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.5"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/tutorials/robot-marbles-part-2/policies.png b/tutorials/robot-marbles-part-2/policies.png
new file mode 100644
index 0000000..c50e55a
Binary files /dev/null and b/tutorials/robot-marbles-part-2/policies.png differ
diff --git a/tutorials/robot-marbles-part-2/policy.png b/tutorials/robot-marbles-part-2/policy.png
new file mode 100644
index 0000000..cede32f
Binary files /dev/null and b/tutorials/robot-marbles-part-2/policy.png differ
diff --git a/tutorials/robot-marbles-part-2/robot-marbles-part-2.ipynb b/tutorials/robot-marbles-part-2/robot-marbles-part-2.ipynb
new file mode 100644
index 0000000..4d68ba9
--- /dev/null
+++ b/tutorials/robot-marbles-part-2/robot-marbles-part-2.ipynb
@@ -0,0 +1,355 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# cadCAD Tutorials: The Robot and the Marbles, part 2\n",
+ "In [Part 1](../robot-marbles-part-1/robot-marbles-part-1.ipynb) we introduced the 'language' in which a system must be described in order for it to be interpretable by cadCAD and some of the basic concepts of the library:\n",
+ "* State Variables\n",
+ "* Timestep\n",
+ "* State Update Functions\n",
+ "* Partial State Update Blocks\n",
+ "* Simulation Configuration Parameters\n",
+ "\n",
+ "This article will introduce the concept of __Policies__. But first let's copy the base configuration from Part 1. As a reminder, here's the description of the simple system we are using for illustration purposes.\n",
+ "\n",
+ "__The robot and the marbles__ \n",
+ "* Picture a box (`box_A`) with ten marbles in it; an empty box (`box_B`) next to the first one; and a robot arm capable of taking a marble from any one of the boxes and dropping it into the other one. \n",
+ "* The robot is programmed to take one marble at a time from the box containing the largest number of marbles and drop it in the other box. It repeats that process until the boxes contain an equal number of marbles. "
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%capture\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# List of all the state variables in the system and their initial values\n",
+ "initial_conditions = {\n",
+ " 'box_A': 10, # as per the description of the example, box_A starts out with 10 marbles in it\n",
+ " 'box_B': 0 # as per the description of the example, box_B starts out empty\n",
+ "}\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "def update_A(params, step, sL, s, _input):\n",
+ " y = 'box_A'\n",
+ " add_to_A = 0\n",
+ " if (s['box_A'] > s['box_B']):\n",
+ " add_to_A = -1\n",
+ " elif (s['box_A'] < s['box_B']):\n",
+ " add_to_A = 1\n",
+ " x = s['box_A'] + add_to_A\n",
+ " return (y, x)\n",
+ "\n",
+ "def update_B(params, step, sL, s, _input):\n",
+ " y = 'box_B'\n",
+ " add_to_B = 0\n",
+ " if (s['box_B'] > s['box_A']):\n",
+ " add_to_B = -1\n",
+ " elif (s['box_B'] < s['box_A']):\n",
+ " add_to_B = 1\n",
+ " x = s['box_B'] + add_to_B\n",
+ " return (y, x)\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# In the Partial State Update Blocks, the user specifies if state update functions will be run in series or in parallel\n",
+ "partial_state_update_blocks = [\n",
+ " { \n",
+ " 'policies': { # We'll ignore policies for now\n",
+ " },\n",
+ " 'variables': { # The following state variables will be updated simultaneously\n",
+ " 'box_A': update_A,\n",
+ " 'box_B': update_B\n",
+ " }\n",
+ " }\n",
+ "]\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# Settings of general simulation parameters, unrelated to the system itself\n",
+ "# `T` is a range with the number of discrete units of time the simulation will run for;\n",
+ "# `N` is the number of times the simulation will be run (Monte Carlo runs)\n",
+ "# In this example, we'll run the simulation once (N=1) and its duration will be of 10 timesteps\n",
+ "# We'll cover the `M` key in a future article. For now, let's leave it empty\n",
+ "simulation_parameters = {\n",
+ " 'T': range(10),\n",
+ " 'N': 1,\n",
+ " 'M': {}\n",
+ "}\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "from cadCAD.configuration import Configuration\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# The configurations above are then packaged into a `Configuration` object\n",
+ "config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values\n",
+ " partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n",
+ " sim_config=simulation_parameters #dict containing simulation parameters\n",
+ " )\n",
+ "\n",
+ "from cadCAD.engine import ExecutionMode, ExecutionContext, Executor\n",
+ "exec_mode = ExecutionMode()\n",
+ "exec_context = ExecutionContext(exec_mode.single_proc)\n",
+ "executor = Executor(exec_context, [config]) # Pass the configuration object inside an array\n",
+ "raw_result, tensor = executor.execute() # The `execute()` method returns a tuple; its first elements contains the raw results\n",
+ "\n",
+ "%matplotlib inline\n",
+ "import pandas as pd\n",
+ "df = pd.DataFrame(raw_result)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "df.plot('timestep', ['box_A', 'box_B'], grid=True, \n",
+ " colormap = 'RdYlGn',\n",
+ " xticks=list(df['timestep'].drop_duplicates()), \n",
+ " yticks=list(range(1+(df['box_A']+df['box_B']).max())));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Policies\n",
+ "In part 1, we ignored the `_input` argument of state update functions. That argument is a signal passed to the state update function by another set of functions: Policy Functions.\n",
+ "\n",
+ "Policy Functions are most commonly used as representations of the behavior of agents that interact with the components of the system we're simulating in cadCAD. But more generally, they describe the logic of some component or mechanism of the system. It is possible to encode the functionality of a policy function in the state update functions themselves (as we did in part 1, where we had the robot's algorithm reside in the `update_A` and `update_B` functions), but as systems grow more complex this approach makes the code harder to read and maintain, and in some cases more inefficient because of unnecessary repetition of computational steps.\n",
+ "\n",
+ "The general structure of a policy function is:\n",
+ "```python\n",
+ "def policy_function(params, step, sL, s):\n",
+ " ...\n",
+ " return {'value1': value1, 'value2': value2, ...}\n",
+ "```\n",
+ "Just like State Update Functions, policies can read the current state of the system from argument `s`, a Python `dict` where the `dict_keys` are the __names of the variables__ and the `dict_values` are their __current values__. The Policy Function must return a dictionary, which will be passed as an argument (`_input`) to the state update functions.\n",
+ "\n",
+ "\n",
+ "Let's update our simulation so that the robot arm's logic is encoded in a Policy instead of in the State Update Functions."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%capture\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# We specify the robot arm's logic in a Policy Function\n",
+ "def robot_arm(params, step, sL, s):\n",
+ " add_to_A = 0\n",
+ " if (s['box_A'] > s['box_B']):\n",
+ " add_to_A = -1\n",
+ " elif (s['box_A'] < s['box_B']):\n",
+ " add_to_A = 1\n",
+ " return({'add_to_A': add_to_A, 'add_to_B': -add_to_A})\n",
+ " \n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# We make the state update functions less \"intelligent\",\n",
+ "# ie. they simply add the number of marbles specified in _input \n",
+ "# (which, per the policy function definition, may be negative)\n",
+ "def increment_A(params, step, sL, s, _input):\n",
+ " y = 'box_A'\n",
+ " x = s['box_A'] + _input['add_to_A']\n",
+ " return (y, x)\n",
+ "\n",
+ "def increment_B(params, step, sL, s, _input):\n",
+ " y = 'box_B'\n",
+ " x = s['box_B'] + _input['add_to_B']\n",
+ " return (y, x)\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# In the Partial State Update Blocks, \n",
+ "# the user specifies if state update functions will be run in series or in parallel\n",
+ "# and the policy functions that will be evaluated in that block\n",
+ "partial_state_update_blocks = [\n",
+ " { \n",
+ " 'policies': { # The following policy functions will be evaluated and their returns will be passed to the state update functions\n",
+ " 'robot_arm': robot_arm\n",
+ " },\n",
+ " 'states': { # The following state variables will be updated simultaneously\n",
+ " 'box_A': increment_A,\n",
+ " 'box_B': increment_B\n",
+ " }\n",
+ " }\n",
+ "]\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# The configurations above are then packaged into a `Configuration` object\n",
+ "config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values\n",
+ " partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n",
+ " sim_config=simulation_parameters #dict containing simulation parameters\n",
+ " )\n",
+ "\n",
+ "exec_mode = ExecutionMode()\n",
+ "exec_context = ExecutionContext(exec_mode.single_proc)\n",
+ "executor = Executor(exec_context, [config]) # Pass the configuration object inside an array\n",
+ "raw_result, tensor = executor.execute() # The `execute()` method returns a tuple; its first elements contains the raw results"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "df.plot('timestep', ['box_A', 'box_B'], grid=True, \n",
+ " xticks=list(df['timestep'].drop_duplicates()), \n",
+ " colormap = 'RdYlGn',\n",
+ " yticks=list(range(1+(df['box_A']+df['box_B']).max())));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "As expected, the results are the same as when the robot arm logic was encoded within the state update functions. \n",
+ "\n",
+ "Several policies may be evaluated within a Partial State Update Block. When that's the case, cadCAD's engine aggregates the outputs of the policies and passes them as a single signal to the state update functions. \n",
+ "\n",
+ "\n",
+ "Aggregation of policies is defined in cadCAD as __key-wise sum (+) of the elements of the outputted `dict`s__.\n",
+ "```python\n",
+ ">policy_1_output = {'int': 1, 'str': 'abc', 'list': [1, 2], '1-only': 'Specific to policy 1'}\n",
+ ">policy_2_output = {'int': 2, 'str': 'def', 'list': [3, 4], '2-only': 'Specific to policy 2'}\n",
+ ">print(aggregate([policy_1_output, policy_2_output]))\n",
+ "```\n",
+ "```\n",
+ "{'int': 3, 'str': 'abcdef', 'list': [1, 2, 3, 4], '1-only': 'Specific to policy 1', '2-only': 'Specific to policy 2'}\n",
+ "```\n",
+ "\n",
+ "To illustrate, let's add to another system another robot arm identical to the first one, that acts in tandem with it. All it takes is to add a policy to the `dict` that describes the partial state update block."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%capture\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# In the Partial State Update Blocks, \n",
+ "# the user specifies if state update functions will be run in series or in parallel\n",
+ "# and the policy functions that will be evaluated in that block\n",
+ "partial_state_update_blocks = [\n",
+ " { \n",
+ " 'policies': { # The following policy functions will be evaluated and their returns will be passed to the state update functions\n",
+ " 'robot_arm_1': robot_arm,\n",
+ " 'robot_arm_2': robot_arm\n",
+ " },\n",
+ " 'variables': { # The following state variables will be updated simultaneously\n",
+ " 'box_A': increment_A,\n",
+ " 'box_B': increment_B\n",
+ " }\n",
+ " }\n",
+ "]\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# The configurations above are then packaged into a `Configuration` object\n",
+ "config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values\n",
+ " partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n",
+ " sim_config=simulation_parameters #dict containing simulation parameters\n",
+ " )\n",
+ "\n",
+ "exec_mode = ExecutionMode()\n",
+ "exec_context = ExecutionContext(exec_mode.single_proc)\n",
+ "executor = Executor(exec_context, [config]) # Pass the configuration object inside an array\n",
+ "raw_result, tensor = executor.execute() # The `execute()` method returns a tuple; its first elements contains the raw results"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "%matplotlib inline\n",
+ "df = pd.DataFrame(raw_result)\n",
+ "df.plot('timestep', ['box_A', 'box_B'], grid=True, \n",
+ " xticks=list(df['timestep'].drop_duplicates()), \n",
+ " colormap = 'RdYlGn',\n",
+ " yticks=list(range(1+(df['box_A']+df['box_B']).max())));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Because we have made it so that both robots read and update the state of the system at the same time, the equilibrium we had before (with 5 marbles in each box) is never reached. Instead, the system oscillates around that point.\n",
+ "\n",
+ "---\n",
+ "\n",
+ "_About BlockScience_ \n",
+ "[BlockScience](http://bit.ly/github_articles_M_02) is a research and engineering firm specialized in complex adaptive systems and applying practical methodologies from engineering design, development and testing to projects in emerging technologies such as blockchain. Follow us on [Medium](http://bit.ly/bsci-medium) or [Twitter](http://bit.ly/bsci-twitter) to stay in touch."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.5"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/tutorials/robot-marbles-part-3/robot-marbles-part-3.ipynb b/tutorials/robot-marbles-part-3/robot-marbles-part-3.ipynb
new file mode 100644
index 0000000..c3d5ba5
--- /dev/null
+++ b/tutorials/robot-marbles-part-3/robot-marbles-part-3.ipynb
@@ -0,0 +1,298 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# cadCAD Tutorials: The Robot and the Marbles, part 3\n",
+ "In parts [1](../robot-marbles-part-1/robot-marbles-part-1.ipynb) and [2](../robot-marbles-part-2/robot-marbles-part-2.ipynb) we introduced the 'language' in which a system must be described in order for it to be interpretable by cadCAD and some of the basic concepts of the library:\n",
+ "* State Variables\n",
+ "* Timestep\n",
+ "* State Update Functions\n",
+ "* Partial State Update Blocks\n",
+ "* Simulation Configuration Parameters\n",
+ "* Policies\n",
+ "\n",
+ "In this notebook we'll look at how subsystems within a system can operate in different frequencies. But first let's copy the base configuration with which we ended Part 2. Here's the description of that system:\n",
+ "\n",
+ "__The robot and the marbles__ \n",
+ "* Picture a box (`box_A`) with ten marbles in it; an empty box (`box_B`) next to the first one; and __two__ robot arms capable of taking a marble from any one of the boxes and dropping it into the other one. \n",
+ "* The robots are programmed to take one marble at a time from the box containing the largest number of marbles and drop it in the other box. They repeat that process until the boxes contain an equal number of marbles.\n",
+ "* The robots act simultaneously; in other words, they assess the state of the system at the exact same time, and decide what their action will be based on that information."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%capture\n",
+ "\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# List of all the state variables in the system and their initial values\n",
+ "initial_conditions = {\n",
+ " 'box_A': 10, # as per the description of the example, box_A starts out with 10 marbles in it\n",
+ " 'box_B': 0 # as per the description of the example, box_B starts out empty\n",
+ "}\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# Settings of general simulation parameters, unrelated to the system itself\n",
+ "# `T` is a range with the number of discrete units of time the simulation will run for;\n",
+ "# `N` is the number of times the simulation will be run (Monte Carlo runs)\n",
+ "# In this example, we'll run the simulation once (N=1) and its duration will be of 10 timesteps\n",
+ "# We'll cover the `M` key in a future article. For now, let's leave it empty\n",
+ "simulation_parameters = {\n",
+ " 'T': range(10),\n",
+ " 'N': 1,\n",
+ " 'M': {}\n",
+ "}\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# We specify the robot arm's logic in a Policy Function\n",
+ "def robot_arm(params, step, sL, s):\n",
+ " add_to_A = 0\n",
+ " if (s['box_A'] > s['box_B']):\n",
+ " add_to_A = -1\n",
+ " elif (s['box_A'] < s['box_B']):\n",
+ " add_to_A = 1\n",
+ " return({'add_to_A': add_to_A, 'add_to_B': -add_to_A})\n",
+ " \n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# We make the state update functions less \"intelligent\",\n",
+ "# ie. they simply add the number of marbles specified in _input \n",
+ "# (which, per the policy function definition, may be negative)\n",
+ "def increment_A(params, step, sL, s, _input):\n",
+ " y = 'box_A'\n",
+ " x = s['box_A'] + _input['add_to_A']\n",
+ " return (y, x)\n",
+ "\n",
+ "def increment_B(params, step, sL, s, _input):\n",
+ " y = 'box_B'\n",
+ " x = s['box_B'] + _input['add_to_B']\n",
+ " return (y, x)\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# In the Partial State Update Blocks, \n",
+ "# the user specifies if state update functions will be run in series or in parallel\n",
+ "# and the policy functions that will be evaluated in that block\n",
+ "partial_state_update_blocks = [\n",
+ " { \n",
+ " 'policies': { # The following policy functions will be evaluated and their returns will be passed to the state update functions\n",
+ " 'robot_arm_1': robot_arm,\n",
+ " 'robot_arm_2': robot_arm\n",
+ " },\n",
+ " 'variables': { # The following state variables will be updated simultaneously\n",
+ " 'box_A': increment_A,\n",
+ " 'box_B': increment_B\n",
+ " }\n",
+ " }\n",
+ "]\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "\n",
+ "from cadCAD.configuration import Configuration\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# The configurations above are then packaged into a `Configuration` object\n",
+ "config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values\n",
+ " partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n",
+ " sim_config=simulation_parameters #dict containing simulation parameters\n",
+ " )\n",
+ "\n",
+ "from cadCAD.engine import ExecutionMode, ExecutionContext, Executor\n",
+ "exec_mode = ExecutionMode()\n",
+ "exec_context = ExecutionContext(exec_mode.single_proc)\n",
+ "executor = Executor(exec_context, [config]) # Pass the configuration object inside an array\n",
+ "raw_result, tensor = executor.execute() # The `execute()` method returns a tuple; its first elements contains the raw results\n",
+ "\n",
+ "%matplotlib inline\n",
+ "import pandas as pd\n",
+ "df = pd.DataFrame(raw_result)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "df.plot('timestep', ['box_A', 'box_B'], grid=True, \n",
+ " xticks=list(df['timestep'].drop_duplicates()), \n",
+ " colormap = 'RdYlGn',\n",
+ " yticks=list(range(1+(df['box_A']+df['box_B']).max())));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Asynchronous Subsystems\n",
+ "We have defined that the robots operate simultaneously on the boxes of marbles. But it is often the case that agents within a system operate asynchronously, each having their own operation frequencies or conditions.\n",
+ "\n",
+ "Suppose that instead of acting simultaneously, the robots in our examples operated in the following manner:\n",
+ "* Robot 1: acts once every 2 timesteps\n",
+ "* Robot 2: acts once every 3 timesteps\n",
+ "\n",
+ "One way to simulate the system with this change is to introduce a check of the current timestep before the robots act, with the definition of separate policy functions for each robot arm."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%capture\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# We specify each of the robots logic in a Policy Function\n",
+ "robots_periods = [2,3] # Robot 1 acts once every 2 timesteps; Robot 2 acts once every 3 timesteps\n",
+ "\n",
+ "def get_current_timestep(cur_substep, s):\n",
+ " if cur_substep == 1:\n",
+ " return s['timestep']+1\n",
+ " return s['timestep']\n",
+ "\n",
+ "def robot_arm_1(params, step, sL, s):\n",
+ " _robotId = 1\n",
+ " if get_current_timestep(step, s)%robots_periods[_robotId-1]==0: # on timesteps that are multiple of 2, Robot 1 acts\n",
+ " return robot_arm(params, step, sL, s)\n",
+ " else:\n",
+ " return({'add_to_A': 0, 'add_to_B': 0}) # for all other timesteps, Robot 1 doesn't interfere with the system\n",
+ "\n",
+ "def robot_arm_2(params, step, sL, s):\n",
+ " _robotId = 2\n",
+ " if get_current_timestep(step, s)%robots_periods[_robotId-1]==0: # on timesteps that are multiple of 3, Robot 2 acts\n",
+ " return robot_arm(params, step, sL, s)\n",
+ " else:\n",
+ " return({'add_to_A': 0, 'add_to_B': 0}) # for all other timesteps, Robot 2 doesn't interfere with the system\n",
+ "\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# In the Partial State Update Blocks, \n",
+ "# the user specifies if state update functions will be run in series or in parallel\n",
+ "# and the policy functions that will be evaluated in that block\n",
+ "partial_state_update_blocks = [\n",
+ " { \n",
+ " 'policies': { # The following policy functions will be evaluated and their returns will be passed to the state update functions\n",
+ " 'robot_arm_1': robot_arm_1,\n",
+ " 'robot_arm_2': robot_arm_2\n",
+ " },\n",
+ " 'variables': { # The following state variables will be updated simultaneously\n",
+ " 'box_A': increment_A,\n",
+ " 'box_B': increment_B\n",
+ " }\n",
+ " }\n",
+ "]\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# The configurations above are then packaged into a `Configuration` object\n",
+ "config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values\n",
+ " partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n",
+ " sim_config=simulation_parameters #dict containing simulation parameters\n",
+ " )\n",
+ "\n",
+ "exec_mode = ExecutionMode()\n",
+ "exec_context = ExecutionContext(exec_mode.single_proc)\n",
+ "executor = Executor(exec_context, [config]) # Pass the configuration object inside an array\n",
+ "raw_result, tensor = executor.execute() # The `execute()` method returns a tuple; its first elements contains the raw results\n",
+ "\n",
+ "df = pd.DataFrame(raw_result)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 4,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "df.plot('timestep', ['box_A', 'box_B'], \n",
+ " grid=True, \n",
+ " xticks=list(df['timestep'].drop_duplicates()), \n",
+ " yticks=list(range(1+max(df['box_A'].max(),df['box_B'].max()))),\n",
+ " colormap = 'RdYlGn'\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Let's take a step-by-step look at what the simulation tells us:\n",
+ "* Timestep 1: the number of marbles in the boxes does not change, as none of the robots act\n",
+ "* Timestep 2: Robot 1 acts, Robot 2 doesn't; resulting in one marble being moved from box A to box B\n",
+ "* Timestep 3: Robot 2 acts, Robot 1 doesn't; resulting in one marble being moved from box A to box B\n",
+ "* Timestep 4: Robot 1 acts, Robot 2 doesn't; resulting in one marble being moved from box A to box B\n",
+ "* Timestep 5: the number of marbles in the boxes does not change, as none of the robots act\n",
+ "* Timestep 6: Robots 1 __and__ 2 act, as 6 is a multiple of 2 __and__ 3; resulting in two marbles being moved from box A to box B and an equilibrium being reached."
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.5"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/tutorials/robot-marbles-part-4/robot-marbles-part-4.ipynb b/tutorials/robot-marbles-part-4/robot-marbles-part-4.ipynb
new file mode 100644
index 0000000..1fac437
--- /dev/null
+++ b/tutorials/robot-marbles-part-4/robot-marbles-part-4.ipynb
@@ -0,0 +1,715 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# cadCAD Tutorials: The Robot and the Marbles, part 4\n",
+ "In parts [1](../robot-marbles-part-1/robot-marbles-part-1.ipynb) and [2](../robot-marbles-part-2/robot-marbles-part-2.ipynb) we introduced the 'language' in which a system must be described in order for it to be interpretable by cadCAD and some of the basic concepts of the library:\n",
+ "* State Variables\n",
+ "* Timestep\n",
+ "* State Update Functions\n",
+ "* Partial State Update Blocks\n",
+ "* Simulation Configuration Parameters\n",
+ "* Policies\n",
+ "\n",
+ "In [part 3](../robot-marbles-part-3/robot-marbles-part-3.ipynb) we covered how to describe the presence of asynchronous subsystems within the system being modeled in cadCAD.\n",
+ "\n",
+ "So far, all the examples referred to deterministic systems: no matter how many times you ran one of those simulations, the results would be the same. However, systems are more commonly non-deterministic, and modelling them as deterministic might be an oversimplification sometimes. \n",
+ "\n",
+ "In this notebook, we'll cover cadCAD's support for modelling non-deterministic systems and Monte Carlo simulations. But first let's copy the base configuration with which we ended Part 3. Here's the description of that system:\n",
+ "\n",
+ "__The robot and the marbles__ \n",
+ "* Picture a box (`box_A`) with ten marbles in it; an empty box (`box_B`) next to the first one; and __two__ robot arms capable of taking a marble from any one of the boxes and dropping it into the other one. \n",
+ "* The robots are programmed to take one marble at a time from the box containing the largest number of marbles and drop it in the other box. They repeat that process until the boxes contain an equal number of marbles.\n",
+ "* The robots act __asynchronously__; robot 1 acts once every two timesteps, and robot 2 acts once every three timesteps."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%capture\n",
+ "\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# List of all the state variables in the system and their initial values\n",
+ "initial_conditions = {\n",
+ " 'box_A': 10, # as per the description of the example, box_A starts out with 10 marbles in it\n",
+ " 'box_B': 0 # as per the description of the example, box_B starts out empty\n",
+ "}\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# Settings of general simulation parameters, unrelated to the system itself\n",
+ "# `T` is a range with the number of discrete units of time the simulation will run for;\n",
+ "# `N` is the number of times the simulation will be run (Monte Carlo runs)\n",
+ "# In this example, we'll run the simulation once (N=1) and its duration will be of 10 timesteps\n",
+ "# We'll cover the `M` key in a future article. For now, let's leave it empty\n",
+ "simulation_parameters = {\n",
+ " 'T': range(10),\n",
+ " 'N': 1,\n",
+ " 'M': {}\n",
+ "}\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# We specify the robot arm's logic in a Policy Function\n",
+ "def robot_arm(params, step, sL, s):\n",
+ " add_to_A = 0\n",
+ " if (s['box_A'] > s['box_B']):\n",
+ " add_to_A = -1\n",
+ " elif (s['box_A'] < s['box_B']):\n",
+ " add_to_A = 1\n",
+ " return({'add_to_A': add_to_A, 'add_to_B': -add_to_A})\n",
+ " \n",
+ "robots_periods = [2,3] # Robot 1 acts once every 2 timesteps; Robot 2 acts once every 3 timesteps\n",
+ "\n",
+ "def robot_arm_1(params, step, sL, s):\n",
+ " _robotId = 1\n",
+ " if s['timestep']%robots_periods[_robotId-1]==0: # on timesteps that are multiple of 2, Robot 1 acts\n",
+ " return robot_arm(params, step, sL, s)\n",
+ " else:\n",
+ " return({'add_to_A': 0, 'add_to_B': 0}) # for all other timesteps, Robot 1 doesn't interfere with the system\n",
+ "\n",
+ "def robot_arm_2(params, step, sL, s):\n",
+ " _robotId = 2\n",
+ " if s['timestep']%robots_periods[_robotId-1]==0: # on timesteps that are multiple of 3, Robot 2 acts\n",
+ " return robot_arm(params, step, sL, s)\n",
+ " else:\n",
+ " return({'add_to_A': 0, 'add_to_B': 0}) # for all other timesteps, Robot 2 doesn't interfere with the system\n",
+ "\n",
+ " \n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# We make the state update functions less \"intelligent\",\n",
+ "# ie. they simply add the number of marbles specified in _input \n",
+ "# (which, per the policy function definition, may be negative)\n",
+ "def increment_A(params, step, sL, s, _input):\n",
+ " y = 'box_A'\n",
+ " x = s['box_A'] + _input['add_to_A']\n",
+ " return (y, x)\n",
+ "\n",
+ "def increment_B(params, step, sL, s, _input):\n",
+ " y = 'box_B'\n",
+ " x = s['box_B'] + _input['add_to_B']\n",
+ " return (y, x)\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# In the Partial State Update Blocks, \n",
+ "# the user specifies if state update functions will be run in series or in parallel\n",
+ "# and the policy functions that will be evaluated in that block\n",
+ "partial_state_update_blocks = [\n",
+ " { \n",
+ " 'policies': { # The following policy functions will be evaluated and their returns will be passed to the state update functions\n",
+ " 'robot_arm_1': robot_arm_1,\n",
+ " 'robot_arm_2': robot_arm_2\n",
+ " },\n",
+ " 'variables': { # The following state variables will be updated simultaneously\n",
+ " 'box_A': increment_A,\n",
+ " 'box_B': increment_B\n",
+ " }\n",
+ " }\n",
+ "]\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "\n",
+ "\n",
+ "from cadCAD.configuration import Configuration\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# The configurations above are then packaged into a `Configuration` object\n",
+ "config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values\n",
+ " partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n",
+ " sim_config=simulation_parameters #dict containing simulation parameters\n",
+ " )\n",
+ "\n",
+ "from cadCAD.engine import ExecutionMode, ExecutionContext, Executor\n",
+ "exec_mode = ExecutionMode()\n",
+ "exec_context = ExecutionContext(exec_mode.single_proc)\n",
+ "executor = Executor(exec_context, [config]) # Pass the configuration object inside an array\n",
+ "raw_result, tensor = executor.execute() # The `execute()` method returns a tuple; its first elements contains the raw results\n",
+ "\n",
+ "%matplotlib inline\n",
+ "import pandas as pd\n",
+ "df = pd.DataFrame(raw_result)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "df.plot('timestep', ['box_A', 'box_B'], grid=True, \n",
+ " xticks=list(df['timestep'].drop_duplicates()), \n",
+ " colormap = 'RdYlGn',\n",
+ " yticks=list(range(1+(df['box_A']+df['box_B']).max())));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# Non-determinism\n",
+ "Non-deterministic systems exhibit different behaviors on different runs for the same input. The order of heads and tails in a series of 3 coin tosses, for example, is non deterministic. \n",
+ "\n",
+ "Our robots and marbles system is currently modelled as a deterministic system. Meaning that every time we run the simulation: none of the robots act on timestep 1; robot 1 acts on timestep 2; robot 2 acts on timestep 3; an so on. \n",
+ "\n",
+ "If however we were to define that at every timestep each robot would act with a probability P, then we would have a non-deterministic (probabilistic) system. Let's make the following changes to our system.\n",
+ "* Robot 1: instead of acting once every two timesteps, there's a 50% chance it will act in any given timestep\n",
+ "* Robot 2: instead of acting once every three timesteps, there's a 33.33% chance it will act in any given timestep"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%capture\n",
+ "from numpy.random import rand\n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# We specify each of the robots logic in a Policy Function\n",
+ "robots_probabilities = [0.5,1/3] # Robot 1 acts with a 50% probability; Robot 2, 33.33%\n",
+ "\n",
+ "def robot_arm_1(params, step, sL, s):\n",
+ " _robotId = 1\n",
+ " if rand()"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "df.plot('timestep', ['box_A', 'box_B'], grid=True, \n",
+ " xticks=list(df['timestep'].drop_duplicates()), \n",
+ " colormap = 'RdYlGn',\n",
+ " yticks=list(range(1+(df['box_A']+df['box_B']).max())));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "And if we run it again, it returns yet another result"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%capture\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# The configurations above are then packaged into a `Configuration` object\n",
+ "config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values\n",
+ " partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n",
+ " sim_config=simulation_parameters #dict containing simulation parameters\n",
+ " )\n",
+ "\n",
+ "exec_mode = ExecutionMode()\n",
+ "exec_context = ExecutionContext(exec_mode.single_proc)\n",
+ "executor = Executor(exec_context, [config]) # Pass the configuration object inside an array\n",
+ "raw_result, tensor = executor.execute() # The `execute()` method returns a tuple; its first elements contains the raw results\n",
+ "\n",
+ "df = pd.DataFrame(raw_result)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "df.plot('timestep', ['box_A', 'box_B'], grid=True, \n",
+ " xticks=list(df['timestep'].drop_duplicates()), \n",
+ " colormap = 'RdYlGn',\n",
+ " yticks=list(range(1+(df['box_A']+df['box_B']).max())));"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In order to take advantage of cadCAD's Monte Carlo simulation features, we should modify the configuration file so as to define the number of times we want the same simulation to be run. This is done in the `N` key of the `simulation_parameters` dict."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "%%capture\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# Settings of general simulation parameters, unrelated to the system itself\n",
+ "# `T` is a range with the number of discrete units of time the simulation will run for;\n",
+ "# `N` is the number of times the simulation will be run (Monte Carlo runs)\n",
+ "# In this example, we'll run the simulation once (N=1) and its duration will be of 10 timesteps\n",
+ "# We'll cover the `M` key in a future article. For now, let's leave it empty\n",
+ "simulation_parameters = {\n",
+ " 'T': range(10),\n",
+ " 'N': 50, # We'll run the same simulation 50 times; the random events in each simulation are independent\n",
+ " 'M': {}\n",
+ "}\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "\n",
+ "# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # \n",
+ "# The configurations above are then packaged into a `Configuration` object\n",
+ "config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values\n",
+ " partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n",
+ " sim_config=simulation_parameters #dict containing simulation parameters\n",
+ " )\n",
+ "\n",
+ "exec_mode = ExecutionMode()\n",
+ "exec_context = ExecutionContext(exec_mode.single_proc)\n",
+ "executor = Executor(exec_context, [config]) # Pass the configuration object inside an array\n",
+ "raw_result, tensor = executor.execute() # The `execute()` method returns a tuple; its first elements contains the raw results\n",
+ "\n",
+ "df = pd.DataFrame(raw_result)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "max_final_A = df[df['timestep']==df['timestep'].max()]['box_A'].max()\n",
+ "# max_final_A\n",
+ "slow_runs = df[(df['timestep']==df['timestep'].max()) & \n",
+ " (df['box_A']==max_final_A)]['run']\n",
+ "slow_runs = list(slow_runs)\n",
+ "slow_runs\n",
+ "\n",
+ "ax = None\n",
+ "for i in slow_runs:\n",
+ " ax = df[df['run']==i].plot('timestep', ['box_A', 'box_B'],\n",
+ " grid=True,\n",
+ " xticks=list(df['timestep'].drop_duplicates()), \n",
+ " yticks=list(range(1+max(df['box_A'].max(),df['box_B'].max()))),\n",
+ " legend = (ax == None),\n",
+ " colormap = 'RdYlGn',\n",
+ " ax = ax\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We invite the reader to fork this code and come up with answers for their other questions that might be interesting to look at. For example:\n",
+ "* How often does box B momentarily contain more marbles than box A?\n",
+ "* What's the frequency distribution of the time to reach equilibrium?\n",
+ "* What's the probability distribution of the waiting times of each one of the robots?"
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.5"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/tutorials/robot-marbles-part-5/robot-marbles-part-5.ipynb b/tutorials/robot-marbles-part-5/robot-marbles-part-5.ipynb
new file mode 100644
index 0000000..84ceb4d
--- /dev/null
+++ b/tutorials/robot-marbles-part-5/robot-marbles-part-5.ipynb
@@ -0,0 +1,356 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# cadCAD Tutorials: The Robot and the Marbles Part 5 - Networks\n",
+ "\n",
+ "To expand upon our previous examples, we will introduce the concept of using a graph network object that is updated during each state update. The ability to essential embed a graph 'database' into a state is a game changer for scalability, allowing increased complexity with multiple agents or components is represented, easily updated. Below, building upon our previous examples, we will represent the Robots and Marbles example with n boxes, and a variable number of marbles. \n",
+ "\n",
+ "## Behavior and Mechanisms:\n",
+ "* A network of robotic arms is capable of taking a marble from their one of their boxes and dropping it into the other one. \n",
+ "* Each robotic arm in the network only controls two boxes and they act by moving a marble from one box to the other.\n",
+ "* Each robotic arm is programmed to take one marble at a time from the box containing the most significant number of marbles and drop it in the other box. It repeats that process until the boxes contain an equal number of marbles.\n",
+ "* For our analysis of this system, suppose we are only interested in monitoring the number of marbles in only their two boxes."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 14,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "from cadCAD.engine import ExecutionMode, ExecutionContext, Executor\n",
+ "from cadCAD.configuration import Configuration\n",
+ "import networkx as nx\n",
+ "import matplotlib.pyplot as plt\n",
+ "import numpy as np\n",
+ "import pandas as pd\n",
+ "#from copy import deepcopy\n",
+ "\n",
+ "%matplotlib inline\n",
+ "\n",
+ "# define global variables\n",
+ "T = 25 #iterations in our simulation\n",
+ "boxes=5 #number of boxes in our network\n",
+ "m= 2 #for barabasi graph type number of edges is (n-2)*m"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We create a [BarabĂ¡si–Albert](https://en.wikipedia.org/wiki/Barab%C3%A1si%E2%80%93Albert_model) graph and then fill the 5 boxes with between 1 and 10 balls. You can create as many different nodes or types of nodes as needed"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# create graph object with the number of boxes as nodes\n",
+ "network = nx.barabasi_albert_graph(boxes, m)\n",
+ "\n",
+ "# add balls to box nodes\n",
+ "for node in network.nodes:\n",
+ " network.nodes[node]['balls'] = np.random.randint(1,10)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Now we will plot the network of boxes and with their labels showing how many balls are in each box."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 3,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stderr",
+ "output_type": "stream",
+ "text": [
+ "/home/aclarkdata/anaconda3/lib/python3.6/site-packages/networkx/drawing/nx_pylab.py:611: MatplotlibDeprecationWarning: isinstance(..., numbers.Number)\n",
+ " if cb.is_numlike(alpha):\n"
+ ]
+ },
+ {
+ "data": {
+ "image/png": "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K6+8omHDhql169Z64YUXSEFRgP/GgHNIujihnj176r333tOUKVP0f//3f9q5c6d1SXCI9Uoc8DK2l3HSLrroImVmZqp+/fpq3LixVq5caV0SHELSBZxhvail6UaY0047TWPGjFFaWpq6du2qRx55RPn5+dZloQxZr8QBryPposTatWunQCCgzMxMpaam6uuvv7YuCWXEeiUOeJn1+KLpRrDzzjtPGRkZ6tixoxITEzVnzhzrklBGSLqAc0i6KLVy5cppyJAheuuttzR48GD169dPubm51mXhFFivxAEvsz59Q9P1iKZNmyoQCGj37t1KSkrSxx9/bF0STgFJF3CG9aKWpushsbGxeu211zRw4EClpqZqwoQJTN4RyHolDngdSRdlxufz6bbbbtPq1as1fvx4de7cWbt27bIuCyVgvRIHvMx6fNF0PepPf/qT1q1bp1q1asnv92vt2rXWJaEESLqAc0i6cESlSpX03HPP6fnnn9eNN96okSNHqqCgwLosnID1ShzwMuvTNzTdKNCxY0dlZ2dryZIlat26tb799lvrknACJF3AGdaLWppulKhdu7aWLl2qVq1aKSEhQW+99ZZ1SSiG9Uoc8DqSLlxRvnx5PfTQQ5o9e7buu+8+DRgwQAcOHLAuC0exXokDXmY9vmi6USglJUXvv/++vvnmGzVr1kybN2+2LglHIekCziHpwnXVqlXTm2++qbvvvlstWrTQ5MmTmejDhPVKHPAy69M3NN0o5vP51LdvXy1fvlxPP/20unfvrj179liXBZF0AadYL2pputBll12mrKwsxcbGKj4+Xhs2bLAuKapZr8QBryPpwlxMTIzGjx+v0aNHq2PHjho9erQKCwuty4pK1itxwMusxxdNF79z4403KisrSwsWLFDbtm31ww8/WJcUlUi6gHNIuggr559/vlasWKHk5GT5/X6988471iVFFbaXAedYjy+aLopUoUIFjRgxQjNmzNCdd96pwYMHKy8vz7qsqGC9/QV4mfX4ouniuFJTUxUIBLRlyxY1b95cW7dutS4pKpB0AeeQdBHWzjnnHM2fP1+9e/dWs2bNNH36dOuSPM16JQ54mfX4ounipPh8PvXv319LlizRyJEj1bt3b+3du9e6LM8i6QLOIekiYjRq1EjZ2dmqUKGCEhISlJ2dbV2S51hf6AF4mfX4oumixKpUqaKX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\n",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "# plot of boxes and balls\n",
+ "nx.draw_kamada_kawai(network,labels=nx.get_node_attributes(network,'balls'))"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 4,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# we initialize the cadCAD state as a network object\n",
+ "initial_conditions = {'network':network}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#Behavior: node by edge dimensional operator\n",
+ "#input the states of the boxes output the deltas along the edges\n",
+ "\n",
+ "# We specify the robotic networks logic in a Policy/Behavior Function\n",
+ "# unlike previous examples our policy controls a vector valued action, defined over the edges of our network\n",
+ "def robotic_network(params, step, sL, s):\n",
+ " network = s['network']\n",
+ " delta_balls = {}\n",
+ " for e in network.edges:\n",
+ " src = e[0]\n",
+ " dst = e[1]\n",
+ " #transfer one ball across the edge in the direction of more balls to less\n",
+ " delta_balls[e] = np.sign(network.nodes[src]['balls']-network.nodes[dst]['balls'])\n",
+ " return({'delta': delta_balls})"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 6,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#mechanism: edge by node dimensional operator\n",
+ "#input the deltas along the edges and update the boxes\n",
+ "\n",
+ "# We make the state update functions less \"intelligent\",\n",
+ "# ie. they simply add the number of marbles specified in _input \n",
+ "# (which, per the policy function definition, may be negative)\n",
+ "\n",
+ "def update_network(params, step, sL, s, _input):\n",
+ " network = s['network'] #deepcopy(s['network']) \n",
+ " delta_balls = _input['delta']\n",
+ " for e in network.edges:\n",
+ " move_ball = delta_balls[e]\n",
+ " src = e[0]\n",
+ " dst = e[1]\n",
+ " if (network.nodes[src]['balls'] >= move_ball) and (network.nodes[dst]['balls'] >= -move_ball):\n",
+ " network.nodes[src]['balls'] = network.nodes[src]['balls']-move_ball\n",
+ " network.nodes[dst]['balls'] = network.nodes[dst]['balls']+move_ball\n",
+ " \n",
+ " return ('network', network)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# wire up the mechanisms and states\n",
+ "partial_state_update_blocks = [\n",
+ " { \n",
+ " 'policies': { # The following policy functions will be evaluated and their returns will be passed to the state update functions\n",
+ " 'action': robotic_network\n",
+ " },\n",
+ " 'variables': { # The following state variables will be updated simultaneously\n",
+ " 'network': update_network\n",
+ " \n",
+ " }\n",
+ " }\n",
+ "]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 8,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# Settings of general simulation parameters, unrelated to the system itself\n",
+ "# `T` is a range with the number of discrete units of time the simulation will run for;\n",
+ "# `N` is the number of times the simulation will be run (Monte Carlo runs)\n",
+ "simulation_parameters = {\n",
+ " 'T': range(T),\n",
+ " 'N': 1,\n",
+ " 'M': {}\n",
+ "}"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 9,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# The configurations above are then packaged into a `Configuration` object\n",
+ "config = Configuration(initial_state=initial_conditions, #dict containing variable names and initial values\n",
+ " partial_state_update_blocks=partial_state_update_blocks, #dict containing state update functions\n",
+ " sim_config=simulation_parameters #dict containing simulation parameters\n",
+ " )"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "single_proc: []\n",
+ "[]\n"
+ ]
+ }
+ ],
+ "source": [
+ "# Run the simulations\n",
+ "exec_mode = ExecutionMode()\n",
+ "exec_context = ExecutionContext(exec_mode.single_proc)\n",
+ "executor = Executor(exec_context, [config]) # Pass the configuration object inside an array\n",
+ "raw_result, tensor = executor.execute() # The `execute()` method returns a tuple; its first elements contains the raw results\n",
+ "df = pd.DataFrame(raw_result)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "We create some helper functions to extract the networkx graph object from the Pandas dataframe and plot it."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 11,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "#NetworkX helper functions\n",
+ "def get_nodes(g):\n",
+ " return [node for node in g.nodes if g.nodes[node]]\n",
+ "\n",
+ "\n",
+ "def pad(vec, length,fill=True):\n",
+ "\n",
+ " if fill:\n",
+ " padded = np.zeros(length,)\n",
+ " else:\n",
+ " padded = np.empty(length,)\n",
+ " padded[:] = np.nan\n",
+ " \n",
+ " for i in range(len(vec)):\n",
+ " padded[i]= vec[i]\n",
+ " \n",
+ " return padded\n",
+ "\n",
+ "def make2D(key, data, fill=False):\n",
+ " maxL = data[key].apply(len).max()\n",
+ " newkey = 'padded_'+key\n",
+ " data[newkey] = data[key].apply(lambda x: pad(x,maxL,fill))\n",
+ " reshaped = np.array([a for a in data[newkey].values])\n",
+ " \n",
+ " return reshaped"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Using our helper function get_nodes() we pull out the boxes ball quantity and save it to a new dataframe column."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 12,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "df['Balls'] = df.network.apply(lambda g: np.array([g.nodes[j]['balls'] for j in get_nodes(g)]))"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "Next we will plot the number of balls in each box over the simulation time period. We can see an oscillation occurs never reaching an equilibrium due to the uneven nature of the boxes and balls."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 13,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ ""
+ ]
+ },
+ "execution_count": 13,
+ "metadata": {},
+ "output_type": "execute_result"
+ },
+ {
+ "data": {
+ "image/png": "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\n",
+ "text/plain": [
+ "
"
+ ]
+ },
+ "metadata": {
+ "needs_background": "light"
+ },
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "plt.plot(df.timestep,make2D('Balls', df))\n",
+ "plt.title('Number of balls in boxes over simulation period')\n",
+ "plt.ylabel('Qty')\n",
+ "plt.xlabel('Iteration')\n",
+ "plt.legend(['Box #'+str(node) for node in range(boxes)], ncol = 2)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "In summary, we created a graph network of boxes and robotic arms to transfer balls between the boxes, striving for an unachievable equilibrium state. The ability to embed a graph, virtually a graph database, into a cadCAD state allows for tremendous scalability and flexibility as a modeling tool. "
+ ]
+ }
+ ],
+ "metadata": {
+ "kernelspec": {
+ "display_name": "Python 3",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "codemirror_mode": {
+ "name": "ipython",
+ "version": 3
+ },
+ "file_extension": ".py",
+ "mimetype": "text/x-python",
+ "name": "python",
+ "nbconvert_exporter": "python",
+ "pygments_lexer": "ipython3",
+ "version": "3.6.7"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/tutorials/videos/robot-marbles-part-1/README.md b/tutorials/videos/robot-marbles-part-1/README.md
new file mode 100644
index 0000000..8639784
--- /dev/null
+++ b/tutorials/videos/robot-marbles-part-1/README.md
@@ -0,0 +1 @@
+(https://youtu.be/uJEiYHRWA9g)
diff --git a/tutorials/videos/robot-marbles-part-1/config.py b/tutorials/videos/robot-marbles-part-1/config.py
new file mode 100644
index 0000000..da40790
--- /dev/null
+++ b/tutorials/videos/robot-marbles-part-1/config.py
@@ -0,0 +1,85 @@
+# import libraries
+from decimal import Decimal
+import numpy as np
+from datetime import timedelta
+from cadCAD.configuration import append_configs
+from cadCAD.configuration.utils import bound_norm_random, ep_time_step, config_sim
+
+seeds = {
+ # 'z': np.random.RandomState(1),
+ # 'a': np.random.RandomState(2)
+}
+
+sim_config = config_sim({
+ 'T': range(10), #number of discrete iterations in each experiement
+ 'N': 1, #number of times the simulation will be run (Monte Carlo runs)
+ #'M': g #parameter sweep dictionary
+})
+
+
+# define the time deltas for the discrete increments in the model
+# ts_format = '%Y-%m-%d %H:%M:%S'
+# t_delta = timedelta(days=0, minutes=1, seconds=0)
+# def time_model(_g, step, sL, s, _input):
+# y = 'time'
+# x = ep_time_step(s, dt_str=s['time'], fromat_str=ts_format, _timedelta=t_delta)
+# return (y, x)
+
+# Behaviors
+
+# Mechanisms
+def update_A(_g, step, sL, s, _input):
+ y = 'box_A'
+ add_to_A = 0
+ if (s['box_A'] > s['box_B']):
+ add_to_A = -1
+ elif (s['box_A'] < s['box_B']):
+ add_to_A = 1
+ x = s['box_A'] + add_to_A
+ return (y, x)
+
+def update_B(_g, step, sL, s, _input):
+ y = 'box_B'
+ add_to_B = 0
+ if (s['box_B'] > s['box_A']):
+ add_to_B = -1
+ elif (s['box_B'] < s['box_A']):
+ add_to_B = 1
+ x = s['box_B'] + add_to_B
+ return (y, x)
+
+# Initial States
+genesis_states = {
+ 'box_A': 10, # as per the description of the example, box_A starts out with 10 marbles in it
+ 'box_B': 0 # as per the description of the example, box_B starts out empty
+}
+
+exogenous_states = {
+ #'time': time_model
+}
+
+env_processes = {
+}
+
+#build mechanism dictionary to "wire up the circuit"
+mechanisms = [
+ {
+ 'policies': {
+ },
+ 'variables': { # The following state variables will be updated simultaneously
+ 'box_A': update_A,
+ 'box_B': update_B
+ }
+ }
+]
+
+
+
+append_configs(
+ sim_configs=sim_config,
+ initial_state=genesis_states,
+ seeds=seeds,
+ raw_exogenous_states=exogenous_states,
+ env_processes=env_processes,
+ partial_state_update_blocks=mechanisms
+)
diff --git a/tutorials/videos/robot-marbles-part-1/config2.py b/tutorials/videos/robot-marbles-part-1/config2.py
new file mode 100644
index 0000000..c87f05d
--- /dev/null
+++ b/tutorials/videos/robot-marbles-part-1/config2.py
@@ -0,0 +1,85 @@
+# import libraries
+from decimal import Decimal
+import numpy as np
+from datetime import timedelta
+from cadCAD.configuration import append_configs
+from cadCAD.configuration.utils import bound_norm_random, ep_time_step, config_sim
+
+seeds = {
+ # 'z': np.random.RandomState(1),
+ # 'a': np.random.RandomState(2)
+}
+
+sim_config = config_sim({
+ 'T': range(10), #number of discrete iterations in each experiement
+ 'N': 1, #number of times the simulation will be run (Monte Carlo runs)
+ #'M': g #parameter sweep dictionary
+})
+
+
+# define the time deltas for the discrete increments in the model
+# ts_format = '%Y-%m-%d %H:%M:%S'
+# t_delta = timedelta(days=0, minutes=1, seconds=0)
+# def time_model(_g, step, sL, s, _input):
+# y = 'time'
+# x = ep_time_step(s, dt_str=s['time'], fromat_str=ts_format, _timedelta=t_delta)
+# return (y, x)
+
+# Behaviors
+
+# Mechanisms
+def update_A(_g, step, sL, s, _input):
+ y = 'box_A'
+ add_to_A = 0
+ if (s['box_A'] > s['box_B']):
+ add_to_A = -1
+ elif (s['box_A'] < s['box_B']):
+ add_to_A = 1
+ x = s['box_A'] + add_to_A
+ return (y, x)
+
+def update_B(_g, step, sL, s, _input):
+ y = 'box_B'
+ add_to_B = 0
+ if (s['box_B'] > s['box_A']):
+ add_to_B = -1
+ elif (s['box_B'] < s['box_A']):
+ add_to_B = 1
+ x = s['box_B'] + add_to_B
+ return (y, x)
+
+# Initial States
+genesis_states = {
+ 'box_A': 11, # as per the description of the example, box_A starts out with 10 marbles in it
+ 'box_B': 0 # as per the description of the example, box_B starts out empty
+}
+
+exogenous_states = {
+ #'time': time_model
+}
+
+env_processes = {
+}
+
+#build mechanism dictionary to "wire up the circuit"
+mechanisms = [
+ {
+ 'policies': {
+ },
+ 'variables': { # The following state variables will be updated simultaneously
+ 'box_A': update_A,
+ 'box_B': update_B
+ }
+ }
+]
+
+
+
+append_configs(
+ sim_configs=sim_config,
+ initial_state=genesis_states,
+ seeds=seeds,
+ raw_exogenous_states=exogenous_states,
+ env_processes=env_processes,
+ partial_state_update_blocks=mechanisms
+)
diff --git a/tutorials/videos/robot-marbles-part-1/images/Mech.jpeg b/tutorials/videos/robot-marbles-part-1/images/Mech.jpeg
new file mode 100644
index 0000000..26606d5
Binary files /dev/null and b/tutorials/videos/robot-marbles-part-1/images/Mech.jpeg differ
diff --git a/tutorials/videos/robot-marbles-part-1/images/Overview.jpeg b/tutorials/videos/robot-marbles-part-1/images/Overview.jpeg
new file mode 100644
index 0000000..b978dd5
Binary files /dev/null and b/tutorials/videos/robot-marbles-part-1/images/Overview.jpeg differ
diff --git a/tutorials/videos/robot-marbles-part-1/images/equations.png b/tutorials/videos/robot-marbles-part-1/images/equations.png
new file mode 100644
index 0000000..5421cb0
Binary files /dev/null and b/tutorials/videos/robot-marbles-part-1/images/equations.png differ
diff --git a/tutorials/videos/robot-marbles-part-1/robot-marbles-part-1.ipynb b/tutorials/videos/robot-marbles-part-1/robot-marbles-part-1.ipynb
new file mode 100644
index 0000000..81552c6
--- /dev/null
+++ b/tutorials/videos/robot-marbles-part-1/robot-marbles-part-1.ipynb
@@ -0,0 +1,409 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "metadata": {},
+ "source": [
+ "# cadCAD Template: Robot and the Marbles\n",
+ "\n",
+ "\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 1,
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# import libraries\n",
+ "import pandas as pd\n",
+ "import numpy as np\n",
+ "import matplotlib \n",
+ "from cadCAD.engine import ExecutionMode, ExecutionContext, Executor\n",
+ "import config\n",
+ "from cadCAD import configs\n",
+ "import matplotlib.pyplot as plt\n",
+ "\n",
+ "%matplotlib inline\n",
+ "\n",
+ "exec_mode = ExecutionMode()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 2,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "single_proc: []\n",
+ "[]\n"
+ ]
+ },
+ {
+ "data": {
+ "text/html": [
+ "