Generate Simulations
Introduction#
This example demonstrates how to use the CompNeuroSim class to define simulations. It is shown how to define the simulation functions, requirements and how to use the simulation information object.
This example imports the "my_model" from other example create_model.py and saves recorded data used in other example plot_recordings.py.
Code#
import numpy as np
from CompNeuroPy import (
CompNeuroMonitors,
CompNeuroSim,
ReqPopHasAttr,
save_variables,
CompNeuroModel,
)
from ANNarchy import (
simulate,
get_population,
Population,
Neuron,
Projection,
Synapse,
Uniform,
)
from CompNeuroPy.examples.create_model import my_model
### CompNeuroSim is a class to define simulations
### It requires a simulation function, which we will define here:
def set_rates(pop_name: str, rates: float = 0.0, duration: float = 0.0):
"""
Sets the rates variable of a population given by pop_name and simulates duration ms.
Args:
pop_name (str):
name of the population
rates (float, optional):
rates variable of the population
duration (float, optional):
duration of the simulation in ms
"""
### set rates and simulate
get_population(pop_name).rates = rates
simulate(duration)
### Also create a second more complex simulation function
def increase_rates(
pop_name: str | list[str],
rate_step: float = 0.0,
time_step: float = 0.0,
nr_steps: int = 0,
):
"""
Increase rates variable of population(s).
Args:
pop_name (str or list of str):
name of population(s)
rate_step (float, optional):
increase of rate with each step, initial step = current rates of pop
time_step (float, optional):
duration of each step in ms
nr_steps (int, optional):
number of steps
"""
### convert single pop into list
pop_name_list = pop_name
if not (isinstance(pop_name_list, list)):
pop_name_list = [pop_name_list]
### define initial value for rates for each pop (assume all neurons have same rates)
start_rate_arr = np.array(
[get_population(pop_name).rates[0] for pop_name in pop_name_list]
)
### simulate all steps
for step in range(nr_steps):
### calculate rates for each pop
rates_arr = step * rate_step + start_rate_arr
### set rates variable of all populations
for pop_idx, pop_name in enumerate(pop_name_list):
set_rates(
pop_name, rates=rates_arr[pop_idx], duration=0
) # use already defined simulation set_rates
### then simulate step
set_rates(pop_name_list[0], rates=rates_arr[0], duration=time_step)
### simulation_functions can return some information which may be helpful later
### the simulation arguments do not need to be returned, since they are accessible
### through the CompNeuroSim object anyway (see below)
return {"duration": time_step * nr_steps, "d_rates": rate_step * nr_steps}
### see below why we need this function
def extend_model(my_model: CompNeuroModel):
"""
Create a simple projections and a projection with decaying weights.
Args:
my_model (CompNeuroModel):
model to which the projection should be added
"""
### create a simple population for later use
Population(1, neuron=Neuron(equations="r=0"), name="simple_pop")
### create a projection with decaying weights to demonstrate recording of projection
proj = Projection(
pre=my_model.populations[0],
post=my_model.populations[1],
target="ampa",
synapse=Synapse(parameters="tau=500", equations="dw/dt=-w/tau"),
name="ampa_proj",
)
proj.connect_all_to_all(weights=Uniform(1.0, 2.0))
def main():
### create and compile the model from other example "create_model.py"
my_model.create(do_compile=False)
### extend the model to demonstrate the functionality of CompNeuroSim requirements
### (see below) and the recording of projections (recorded data will be used in
### other example "plot_recordings.py")
extend_model(my_model)
my_model.compile()
### Define Monitors, recording p and spike from both model populations with periods
### of 10 ms and 15 ms and the weights of the ampa projection with period of 10 ms
monitor_dictionary = {
f"{my_model.populations[0]};10": ["p", "spike"],
f"{my_model.populations[1]};15": ["p", "spike"],
"ampa_proj;10": ["w"],
}
mon = CompNeuroMonitors(monitor_dictionary)
### Now use CompNeuroSim to define a simulation. Use the previously defined
### simulation functions and define their arguments as kwargs dictionary. Give the
### simulation a name and description and you can also define requirements for the
### simulation. Here, for example, we require that the populations contain the
### attribute 'rates'. One can define multiple requirements in a list of
### dictionaries. The arguments of the requirements can be inherited from the
### simulation kwargs by using the syntax 'simulation_kwargs.<kwarg_name>'.
### The monitor object is also given to the simulation, so that the simulation
### runs can be automatically associated with the monitor recording chunks.
increase_rates_pop = CompNeuroSim(
simulation_function=increase_rates,
simulation_kwargs={
"pop_name": my_model.populations[0],
"rate_step": 10,
"time_step": 100,
"nr_steps": 15,
},
name="increase_rates_pop",
description="increase rates variable of pop",
requirements=[
{"req": ReqPopHasAttr, "pop": "simulation_kwargs.pop_name", "attr": "rates"}
],
monitor_object=mon,
)
### Now let's use this simulation
### Simulate 500 ms without recordings and then run the simulation
simulate(500)
mon.start()
increase_rates_pop.run()
### resetting monitors and model, creating new recording chunk
mon.reset()
### again simulate 700 ms without recording
### then run the simulation with different simulation kwargs (for all populations)
mon.pause()
simulate(700)
mon.start()
increase_rates_pop.run({"pop_name": my_model.populations})
simulate(500)
### now again change the pop_name kwarg but use the simple_pop population without
### the required attribute 'rates'
### this will raise an error
try:
increase_rates_pop.run({"pop_name": "simple_pop"})
except Exception as e:
print("\n###############################################")
print(
"Running simulation with population not containing attribute 'rates' causes the following error:"
)
print(e)
print("###############################################\n")
### get recordings and recording times from the CompNeuroMonitors object
recordings = mon.get_recordings()
recording_times = mon.get_recording_times()
### get the simulation information object from the CompNeuroSim object
increase_rates_pop_info = increase_rates_pop.simulation_info()
### save the recordings, recording times and simulation information
save_variables(
variable_list=[recordings, recording_times, increase_rates_pop_info],
name_list=["recordings", "recording_times", "increase_rates_pop_info"],
path="run_and_monitor_simulations",
)
### print the information contained in the simulation information object
print("\nA simulation object contains:")
print("name\n", increase_rates_pop_info.name)
print("\ndescription\n", increase_rates_pop_info.description)
print("\nstart (for each run)\n", increase_rates_pop_info.start)
print("\nend (for each run)\n", increase_rates_pop_info.end)
print("\ninfo (for each run)\n", increase_rates_pop_info.info)
print("\nkwargs (for each run)\n", increase_rates_pop_info.kwargs)
print("\nmonitor chunk (for each run)\n", increase_rates_pop_info.monitor_chunk)
return 1
if __name__ == "__main__":
main()
Console Output#
$ python run_and_monitor_simulations.py
ANNarchy 4.7 (4.7.3b) on linux (posix).
created model, other parameters: 1
Compiling ... OK
###############################################
Running simulation with population not containing attribute 'rates' causes the following error:
Population simple_pop does not contain attribute rates!
###############################################
A simulation object contains:
name
increase_rates_pop
description
increase rates variable of pop
start (for each run)
[500.0, 700.0]
end (for each run)
[2000.0, 2200.0]
info (for each run)
[{'duration': 1500, 'd_rates': 150}, {'duration': 1500, 'd_rates': 150}]
kwargs (for each run)
[{'pop_name': 'first_poisson', 'rate_step': 10, 'time_step': 100, 'nr_steps': 15}, {'pop_name': ['first_poisson', 'second_poisson'], 'rate_step': 10, 'time_step': 100, 'nr_steps': 15}]
monitor chunk (for each run)
[0, 1]