Skip to content

Commit

Permalink
Add controllers and runner guides
Browse files Browse the repository at this point in the history
  • Loading branch information
mcgalcode committed Nov 14, 2023
1 parent be9b799 commit 985ebae
Show file tree
Hide file tree
Showing 5 changed files with 871 additions and 16 deletions.
225 changes: 223 additions & 2 deletions docs/guides/controllers_and_update_rules.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,232 @@
"cells": [
{
"cell_type": "code",
"execution_count": null,
"execution_count": 1,
"id": "493170b9-97c0-439c-a272-20261b6b0adf",
"metadata": {},
"outputs": [],
"source": []
"source": [
"%load_ext autoreload\n",
"%autoreload 2"
]
},
{
"cell_type": "markdown",
"id": "f5e2e73b-370b-4989-99dd-ee37c05b11fb",
"metadata": {},
"source": [
"# Controllers and Update Rules\n",
"\n",
"In `pylattica`, the rule for evolving a simulation is defined in a subclass of `BasicController` which is implemented by the user. This is an extremely lightweight class which is given to the `Runner` in order to evolve a simulation (see the Runners guide for more details on running a simulation).\n",
"\n",
"In this guide, we will illustrate the following:\n",
"\n",
"- Implementing a simple update rule\n",
"- Returning updates for multiple sites\n",
"- Returning updates for the general simulation state\n",
"\n",
"## A simple example\n",
"\n",
"In our toy simulation, the state stored at each state will be a single integer. We will represent this state like this:\n",
"\n",
"```\n",
"{ \"value\": 1 }\n",
"```\n",
"\n",
"Where `\"value\"` is an arbitrarily chosen key for our state value, and `1` is the current integer value.\n",
"\n",
"Additionally, in this toy simulation, the value of the state at each site is incremented by 1 during each simulation step. Here's how we would use a `BasicController` subclass to implement this."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "75b16f2c-8e39-4554-9c15-9e9d310c89ce",
"metadata": {},
"outputs": [],
"source": [
"from pylattica.core import BasicController, SimulationState\n",
"\n",
"class ToyController(BasicController):\n",
" \n",
" def get_state_update(self, site_id: int, prev_state: SimulationState):\n",
" # retrieve the previous state for the site that needs to be updated\n",
" previous_site_state = prev_state.get_site_state(site_id)\n",
" old_value = previous_state.get(\"value\")\n",
" \n",
" # compute the new value\n",
" new_value = old_value + 1\n",
" \n",
" # return a dictionary of updates that should be applied to the state at that site\n",
" return {\n",
" \"value\": new_value\n",
" }"
]
},
{
"cell_type": "markdown",
"id": "0b158e0e-c69f-4051-b563-59884952c636",
"metadata": {},
"source": [
"The key observation to make here is that the update rule is implemented in a method called `get_state_update` that takes two arguments - the ID of the state whose updates should be computed and returned, and the previous state of the simulation.\n",
"\n",
"There are two salient features of this system:\n",
"\n",
"1. `pylattica` makes no assumptions about how you will use the previous `SimulationState` to compute the updates. For example, you could ignore it or you could use it in conjunction with a `Neighborhood`.\n",
"2. The return value of this function is a dictionary that represents the updates that should be applied to the state. If each site had a state dictionary with multiple keys (i.e. something in addition to `\"value\"`,), those keys would remain unchanged by this update rule."
]
},
{
"cell_type": "markdown",
"id": "7589c114-32a3-4ff6-9a95-17a7cc949f43",
"metadata": {},
"source": [
"## Updating Multiple Site States at Once\n",
"\n",
"Sometimes a simulation update rule will need to update the values of multiple site states during each application of the update rule (note that this will require use of the `AsynchronousRunner` - as described in the Runners guide).\n",
"\n",
"For example, you might need to implement an update rule that updates multiple sites at once to achieve conservation of some quantity in your simulation state.\n",
"\n",
"To do this, you can return a more complex form of the update dictionary, shown below."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "c8d91d12-29b0-4297-b11d-61bb8d95e35a",
"metadata": {},
"outputs": [],
"source": [
"from pylattica.core import BasicController, SimulationState\n",
"\n",
"class MultiSiteUpdate(BasicController):\n",
" \n",
" def get_state_update(self, site_id: int, prev_state: SimulationState):\n",
" # rename the first site for readability\n",
" site_1_id = site_id\n",
" \n",
" # determine the other site that will be updated in conjunction with the one identified by site_id\n",
" site_2_id = site_id + 1\n",
" \n",
" # retrieve the two states\n",
" site_1_prev_state = prev_state.get_site_state(site_1_id)\n",
" site_1_old_val = site_1_prev_state.get(\"value\")\n",
" \n",
" site_2_prev_state = prev_state.get_site_state(site_2_id)\n",
" site_2_old_val = site_2_prev_state.get(\"value\")\n",
" \n",
" # compute the new value\n",
" new_site_1_val = site_1_old_val + 1\n",
" new_site_2_val = site_2_old_val - 1\n",
" \n",
" # return a dictionary of updates that should be applied to the state at that site\n",
" return {\n",
" site_1_id: {\n",
" \"value\": new_site_1_val\n",
" },\n",
" site_2_id: {\n",
" \"value\": new_site_2_val\n",
" }\n",
" }"
]
},
{
"cell_type": "markdown",
"id": "8f5e1b9a-44ed-45e7-b871-e478ae327ce8",
"metadata": {},
"source": [
"Let's highlight the form of the return value for this controller:\n",
"\n",
"```\n",
"{\n",
" site_1_id: {\n",
" \"value\": new_site_1_val\n",
" },\n",
" site_2_id: {\n",
" \"value\": new_site_2_val\n",
" }\n",
"}\n",
"```\n",
"\n",
"In this case, the updates dictionary being returned has a top level of keys specifying the IDs of sites to which the updates should be applied. This format lets us express that this single invocation of the update rule should yield changes to both site 1 and site 2 (given by `new_site_1_val` and `new_site_2_val`)."
]
},
{
"cell_type": "markdown",
"id": "fb3c9ca7-2143-4c42-a7be-5dfa7c8948b2",
"metadata": {},
"source": [
"## Updating Multiple Sites and the General State\n",
"\n",
"In addition to per-site state values, `pylattica` also supports accounting for an additional state dictionary that applies to the entire simulation. This is called the *general state*.\n",
"\n",
"> **NOTE**: If you use the *general state*, you should also use the `AsynchronousRunner` to avoid applying conflicting updates to the general state at the same time.\n",
"\n",
"For example, below we implement a toy controller that increments both the site specific state, and a similar value in the general state called `\"general_value\"`."
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "a4152e4d-cc30-4ba7-b9fd-1b244b8f6623",
"metadata": {},
"outputs": [],
"source": [
"from pylattica.core import BasicController, SimulationState\n",
"from pylattica.core.simulation_state import SITES, GENERAL\n",
"\n",
"class GeneralStateController(BasicController):\n",
" \n",
" def get_state_update(self, site_id: int, prev_state: SimulationState):\n",
" # retrieve the previous state for the site that needs to be updated\n",
" previous_site_state = prev_state.get_site_state(site_id)\n",
" old_value = previous_state.get(\"value\")\n",
" \n",
" previous_general_value = prev_state.get_general_state().get(\"general_value\")\n",
" \n",
" # compute the new value\n",
" new_value = old_value + 1\n",
" \n",
" new_general_value = previous_general_value + 1\n",
" \n",
" # return a dictionary of updates that should be applied to the state at that site\n",
" return {\n",
" SITES: {\n",
" site_id: {\n",
" \"value\": new_value\n",
" }\n",
" },\n",
" GENERAL: {\n",
" \"general_value\": new_general_value\n",
" }\n",
" }"
]
},
{
"cell_type": "markdown",
"id": "8bee24d0-3e6f-4bee-9e58-62602457b30e",
"metadata": {},
"source": [
"We had to import the `SITES` and `GENERAL` constants from `pylattica` in order to specify the form of this dictionary.\n",
"\n",
"Note that the dictionary under the `SITES` key is a mapping of site IDs to state updates. If we wanted to update multiple sites in addition to the general state, we could use a return value of something like this:\n",
"\n",
"```\n",
"{\n",
" SITES: {\n",
" site_id_1: {\n",
" \"value\": new_value\n",
" },\n",
" site_id_2: {\n",
" \"value\": new_value_2\n",
" }\n",
" },\n",
" GENERAL: {\n",
" \"general_value\": new_general_value\n",
" }\n",
"}\n",
"```"
]
}
],
"metadata": {
Expand Down
2 changes: 2 additions & 0 deletions docs/guides/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ It's likely that this demonstration does not illustrate everything you need for
- [Periodic Structures](./periodic_structures.ipynb)
- [Building Neighborhoods](./neighborhoods.ipynb)
- [Representing Simulation State](./neighborhoods.ipynb)
- [Controllers and Update Rules](./controllers_and_update_rules.ipynb)
- [Runners](./runners.ipynb)
- [Working With Square Grids](./square_grids.ipynb)

Finally, if all else fails, the [Reference](../reference/core/lattice.md) provides individual method-level detail for the use of this library.
Loading

0 comments on commit 985ebae

Please sign in to comment.