%load_ext autoreload
%autoreload 2
Controllers and Update Rules¶
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).
In this guide, we will illustrate the following:
- Implementing a simple update rule
- Returning updates for multiple sites
- Returning updates for the general simulation state
A simple example¶
In our toy simulation, the state stored at each state will be a single integer. We will represent this state like this:
{ "value": 1 }
Where "value"
is an arbitrarily chosen key for our state value, and 1
is the current integer value.
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.
from pylattica.core import BasicController, SimulationState
class ToyController(BasicController):
def get_state_update(self, site_id: int, prev_state: SimulationState):
# retrieve the previous state for the site that needs to be updated
previous_site_state = prev_state.get_site_state(site_id)
old_value = previous_state.get("value")
# compute the new value
new_value = old_value + 1
# return a dictionary of updates that should be applied to the state at that site
return {
"value": new_value
}
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.
There are two salient features of this system:
pylattica
makes no assumptions about how you will use the previousSimulationState
to compute the updates. For example, you could ignore it or you could use it in conjunction with aNeighborhood
.- 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.
Updating Multiple Site States at Once¶
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).
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.
To do this, you can return a more complex form of the update dictionary, shown below.
from pylattica.core import BasicController, SimulationState
class MultiSiteUpdate(BasicController):
def get_state_update(self, site_id: int, prev_state: SimulationState):
# rename the first site for readability
site_1_id = site_id
# determine the other site that will be updated in conjunction with the one identified by site_id
site_2_id = site_id + 1
# retrieve the two states
site_1_prev_state = prev_state.get_site_state(site_1_id)
site_1_old_val = site_1_prev_state.get("value")
site_2_prev_state = prev_state.get_site_state(site_2_id)
site_2_old_val = site_2_prev_state.get("value")
# compute the new value
new_site_1_val = site_1_old_val + 1
new_site_2_val = site_2_old_val - 1
# return a dictionary of updates that should be applied to the state at that site
return {
site_1_id: {
"value": new_site_1_val
},
site_2_id: {
"value": new_site_2_val
}
}
Let's highlight the form of the return value for this controller:
{
site_1_id: {
"value": new_site_1_val
},
site_2_id: {
"value": new_site_2_val
}
}
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
).
Updating Multiple Sites and the General State¶
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.
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.
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"
.
from pylattica.core import BasicController, SimulationState
from pylattica.core.simulation_state import SITES, GENERAL
class GeneralStateController(BasicController):
def get_state_update(self, site_id: int, prev_state: SimulationState):
# retrieve the previous state for the site that needs to be updated
previous_site_state = prev_state.get_site_state(site_id)
old_value = previous_state.get("value")
previous_general_value = prev_state.get_general_state().get("general_value")
# compute the new value
new_value = old_value + 1
new_general_value = previous_general_value + 1
# return a dictionary of updates that should be applied to the state at that site
return {
SITES: {
site_id: {
"value": new_value
}
},
GENERAL: {
"general_value": new_general_value
}
}
We had to import the SITES
and GENERAL
constants from pylattica
in order to specify the form of this dictionary.
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:
{
SITES: {
site_id_1: {
"value": new_value
},
site_id_2: {
"value": new_value_2
}
},
GENERAL: {
"general_value": new_general_value
}
}