def test_2_4_vectorization():
    """Testing vectorization functionality of ComputeGraph class.

    See Also
    --------
    :method:`_vectorize`: Detailed documentation of vectorize method of `ComputeGraph` class.
    """

    backends = ['tensorflow', 'numpy']
    for b in backends:
        # test whether vectorized networks produce same output as non-vectorized backend
        ################################################################################

        # define simulation params
        dt = 1e-2
        sim_time = 10.
        sim_steps = int(sim_time / dt)
        inp = np.zeros((sim_steps, 2)) + 0.5

        # set up networks
        net_config0 = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net12").apply()
        net_config1 = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net12").apply()
        net0 = ComputeGraph(net_config=net_config0,
                            name='net0',
                            vectorization='none',
                            dt=dt,
                            build_in_place=False,
                            backend=b)
        net1 = ComputeGraph(net_config=net_config1,
                            name='net1',
                            vectorization='nodes',
                            dt=dt,
                            build_in_place=False,
                            backend=b)

        # simulate network behaviors
        results0 = net0.run(sim_time,
                            outputs={
                                'a': 'pop0/op7/a',
                                'b': 'pop1/op7/a'
                            },
                            inputs={'all/op7/inp': inp})
        results1 = net1.run(sim_time,
                            outputs={
                                'a': 'pop0/op7/a',
                                'b': 'pop1/op7/a'
                            },
                            inputs={'all/op7/inp': inp},
                            out_dir='/tmp/log')

        error1 = nmrse(results0.values, results1.values)

        assert np.sum(results1.values) > 0.
        assert np.mean(error1) == pytest.approx(0., rel=1e-6, abs=1e-6)
def test_2_5_solver():
    """Testing different numerical solvers of pyrates.

    See Also
    --------
    :method:`_solve`: Detailed documentation of how to numerical integration is performed by the `NumpyBackend`.
    :method:`run`: Detailed documentation of the method that needs to be called to solve differential equations in the
    `NumpyBackend`.
    """

    backend = 'numpy'

    # define input
    dt = 1e-3
    sim_time = 100.
    sim_steps = int(np.round(sim_time / dt, decimals=0))
    inp = np.zeros((sim_steps, 1)) + 0.5

    # standard euler solver (trusted)
    net_config = CircuitTemplate.from_yaml(
        "model_templates.test_resources.test_backend.net13").apply(
            label='net0')
    net = net_config.compile(vectorization=True,
                             step_size=dt,
                             backend=backend,
                             solver='euler')
    results = net.run(sim_time,
                      outputs={
                          'a1': 'p1/op9/a',
                          'a2': 'p2/op9/a'
                      },
                      inputs={'p1/op9/I_ext': inp})
    net.clear()

    # scipy solver (tested)
    net_config2 = CircuitTemplate.from_yaml(
        "model_templates.test_resources.test_backend.net13").apply(
            label='net1')
    net2 = net_config2.compile(vectorization=True,
                               step_size=dt,
                               backend=backend,
                               solver='scipy')
    results2 = net2.run(sim_time,
                        outputs={
                            'a1': 'p1/op9/a',
                            'a2': 'p2/op9/a'
                        },
                        inputs={'p1/op9/I_ext': inp},
                        method='RK23')
    net2.clear()

    assert np.mean(results.loc[:, 'a2'].values -
                   results2.loc[:, 'a2'].values) == pytest.approx(0.,
                                                                  rel=1e-4,
                                                                  abs=1e-4)
示例#3
0
def test_op_caching_nodes():
    """Test the case that two operator templates are identical up to variable definitions, where one inherits from the
    other"""
    from pyrates.frontend import CircuitTemplate

    circuit = CircuitTemplate.from_yaml("model_templates.test_resources.test_operator_caching_templates.TestCircuit1").apply()

    # access nodes
    node1 = circuit["node1"]
    node2 = circuit["node2"]

    # verify that operator labels and contents are identical
    op1, op1_dict = next(iter(node1.op_graph.nodes(data=True)))
    op2, op2_dict = next(iter(node2.op_graph.nodes(data=True)))

    assert op1 == op2
    assert op1_dict == op2_dict
    assert op1_dict["operator"] is op2_dict["operator"]

    # verify that values in nodes are indeed different (but refer to same operator)
    assert node1.values["TestOpLin0"]["c"] == 0
    assert node2.values["TestOpLin0"]["c"] == 1

    # now do the vectorization step
    circuit = circuit.optimize_graph_in_place()
示例#4
0
    def eval_fitness(self, genes: list, gene_map: list, loss_func: callable,
                     loss_kwargs: Optional[dict] = None, run_func: Optional[callable] = None,
                     template: Optional[str] = None, compile_kwargs: Optional[dict] = None,
                     run_kwargs: Optional[dict] = None) -> float:
        """Performs simulation in PyRates of the model defined by `template` and calculates the fitness based on the
        resulting timeseries of that simulation.

        Parameters
        ----------
        genes
            List of model parameters
        gene_map
            List of string-based variable pointers that indicate which gene refers to which variable in the model.
        loss_func
        loss_kwargs
        run_func
            User-specified run function that can be specified instead of a template, to run user-specified routines.
        template
        compile_kwargs
        run_kwargs

        Returns
        -------

        """

        if run_func is None:

            attempts = 1
            while attempts < 100:

                try:

                    # load model template
                    model_id = self.get_unique_id(int(attempts*1e6))
                    if type(template) is str:
                        template = CircuitTemplate.from_yaml(template)
                    model = deepcopy(template).apply(label=f'model_{model_id}')

                    # apply new parameters to model template
                    params, param_map = dict(), dict()
                    for i, (p, key) in enumerate(zip(genes, gene_map)):
                        params[i] = p
                        param_map[i] = key
                    adapt_circuit(model, params=params, param_map=param_map)

                    # compile model into backend
                    model_compiled = model.compile(**compile_kwargs)

                    # define run func
                    run_func = model_compiled.run
                    break

                except (FileExistsError, FileNotFoundError):
                    attempts += 1
                    continue

        results = run_func(**run_kwargs)
        return loss_func(results, **loss_kwargs)
def test_2_1_operator():
    """Testing operator functionality of compute graph class:

    See Also
    --------
    :method:`add_operator`: Detailed documentation of method for adding operations to instance of `ComputeGraph`.
    """

    backends = ["tensorflow", "numpy"]
    accuracy = 1e-4

    for b in backends:

        # test correct numerical evaluation of operator with two coupled simple, linear equations
        #########################################################################################

        # create net config from YAML file
        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net0").apply()

        # instantiate compute graph from net config
        dt = 1e-1
        net = ComputeGraph(net_config=net_config,
                           name='net0',
                           vectorization=True,
                           backend=b)

        # simulate operator behavior
        sim_time = 10.0
        results = net.run(simulation_time=sim_time,
                          step_size=dt,
                          outputs={'a': 'pop0/op0/a'})
        net.clear()

        # generate target values
        sim_steps = int(sim_time / dt)
        update0_1 = lambda x: x * 0.5
        update0_0 = lambda x: x + 2.0
        targets = np.zeros((sim_steps + 1, 2), dtype=np.float32)
        for i in range(sim_steps):
            targets[i + 1, 0] = targets[i, 0] + dt * update0_0(targets[i, 1])
            targets[i + 1, 1] = targets[i, 1] + dt * update0_1(targets[i, 0])

        # compare results with target values
        diff = results['a'].values[:, 0] - targets[1:, 1]
        assert np.mean(np.abs(diff)) == pytest.approx(0.,
                                                      rel=accuracy,
                                                      abs=accuracy)

        # test correct numerical evaluation of operator with a single differential equation and external input
        ######################################################################################################

        # set up operator in pyrates
        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net1").apply()
        net = ComputeGraph(net_config=net_config,
                           name='net1',
                           vectorization=True,
                           backend=b)

        # define input
        inp = np.zeros((sim_steps, )) + 0.5

        # simulate operator behavior
        results = net.run(sim_time,
                          step_size=dt,
                          inputs={'pop0/op1/u': inp},
                          outputs={'a': 'pop0/op1/a'})
        net.clear()

        # calculate operator behavior from hand
        update1 = lambda x, y: x + dt * (y - x)
        targets = np.zeros((sim_steps + 1, 1), dtype=np.float32)
        for i in range(sim_steps):
            targets[i + 1] = update1(targets[i], inp[i])

        diff = results['a'].values[:, 0] - targets[1:, 0]
        assert np.mean(np.abs(diff)) == pytest.approx(0.,
                                                      rel=accuracy,
                                                      abs=accuracy)

        # test correct numerical evaluation of operator with two coupled equations (1 ODE, 1 non-DE eq.)
        ################################################################################################

        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net2").apply()
        net = ComputeGraph(net_config=net_config,
                           name='net2',
                           vectorization=True,
                           backend=b)
        results = net.run(sim_time, outputs={'a': 'pop0/op2/a'}, step_size=dt)
        net.clear()

        # calculate operator behavior from hand
        update2 = lambda x: 1. / (1. + np.exp(-x))
        targets = np.zeros((sim_steps + 1, 2), dtype=np.float32)
        for i in range(sim_steps):
            targets[i + 1, 1] = update2(targets[i, 0])
            targets[i + 1, 0] = update1(targets[i, 0], targets[i + 1, 1])

        diff = results['a'].values[:, 0] - targets[1:, 0]
        assert np.mean(np.abs(diff)) == pytest.approx(0.,
                                                      rel=accuracy,
                                                      abs=accuracy)

        # test correct numerical evaluation of operator with a two coupled DEs
        ######################################################################

        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net3").apply()
        net = ComputeGraph(net_config=net_config,
                           name='net3',
                           vectorization=True,
                           backend=b)
        results = net.run(sim_time,
                          outputs={'b': 'pop0/op3/b'},
                          inputs={'pop0/op3/u': inp},
                          out_dir="/tmp/log",
                          step_size=dt)
        net.clear()

        # calculate operator behavior from hand
        update3_0 = lambda a, b, u: a + dt * (-10. * a + b**2 + u)
        update3_1 = lambda b, a: b + dt * 0.1 * a
        targets = np.zeros((sim_steps + 1, 2), dtype=np.float32)
        for i in range(sim_steps):
            targets[i + 1, 0] = update3_0(targets[i, 0], targets[i, 1], inp[i])
            targets[i + 1, 1] = update3_1(targets[i, 1], targets[i, 0])

        diff = results['b'].values[:, 0] - targets[1:, 1]
        assert np.mean(np.abs(diff)) == pytest.approx(0.,
                                                      rel=accuracy,
                                                      abs=accuracy)
def test_2_3_edge():
    """Testing edge functionality of compute graph class.

    See Also
    --------
    :method:`add_edge`: Detailed documentation of add_edge method of `ComputeGraph`class.

    """

    backends = ['numpy', 'tensorflow']
    accuracy = 1e-4

    for b in backends:

        # test correct numerical evaluation of graph with 1 source projecting unidirectional to 2 target nodes
        ######################################################################################################

        # set up edge in pyrates
        dt = 1e-1
        sim_time = 10.
        sim_steps = int(sim_time / dt)
        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net8").apply()
        net = ComputeGraph(net_config=net_config,
                           name='net0',
                           vectorization=True,
                           backend=b)

        # calculate edge behavior from hand
        update0 = lambda x, y: x + dt * y * 0.5
        update1 = lambda x, y: x + dt * (y + 2.0)
        update2 = lambda x, y: x + dt * (y - x)
        targets = np.zeros((sim_steps + 1, 4), dtype=np.float32)
        for i in range(sim_steps):
            targets[i + 1, 0] = update0(targets[i, 0], targets[i, 1])
            targets[i + 1, 1] = update1(targets[i, 1], targets[i, 0])
            targets[i + 1, 2] = update2(targets[i, 2], targets[i, 0] * 2.0)
            targets[i + 1, 3] = update2(targets[i, 3], targets[i, 0] * 0.5)

        # simulate edge behavior
        results = net.run(sim_time,
                          outputs={
                              'a': 'pop1/op1/a',
                              'b': 'pop2/op1/a'
                          },
                          step_size=dt)
        net.clear()

        diff = np.mean(np.abs(results['a'].values[:, 0] - targets[1:, 2])) + \
               np.mean(np.abs(results['b'].values[:, 0] - targets[1:, 3]))
        assert diff == pytest.approx(0., rel=accuracy, abs=accuracy)

        # test correct numerical evaluation of graph with 2 bidirectionaly coupled nodes
        ################################################################################

        # define input
        inp = np.zeros((sim_steps, 1)) + 0.5

        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net9").apply()
        net = ComputeGraph(net_config=net_config,
                           name='net1',
                           vectorization=True,
                           backend=b)
        results = net.run(sim_time,
                          outputs={
                              'a': 'pop0/op1/a',
                              'b': 'pop1/op7/a'
                          },
                          inputs={'pop1/op7/inp': inp},
                          step_size=dt)
        net.clear()

        # calculate edge behavior from hand
        update3 = lambda x, y, z: x + dt * (y + z - x)
        targets = np.zeros((sim_steps + 1, 2), dtype=np.float32)
        for i in range(sim_steps):
            targets[i + 1, 0] = update2(targets[i, 0], targets[i, 1] * 0.5)
            targets[i + 1, 1] = update3(targets[i, 1], targets[i, 0] * 2.0,
                                        inp[i])

        diff = np.mean(np.abs(results['a'].values[:, 0] - targets[1:, 0])) + \
               np.mean(np.abs(results['b'].values[:, 0] - targets[1:, 1]))
        assert diff == pytest.approx(0., rel=accuracy, abs=accuracy)

        # test correct numerical evaluation of graph with 2 bidirectionally delay-coupled nodes
        #######################################################################################

        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net10").apply()
        net = ComputeGraph(net_config=net_config,
                           name='net2',
                           vectorization=True,
                           backend=b,
                           step_size=dt)
        results = net.run(sim_time,
                          outputs={
                              'a': 'pop0/op8/a',
                              'b': 'pop1/op8/a'
                          },
                          step_size=dt)
        net.clear()

        # calculate edge behavior from hand
        delay0 = int(0.5 / dt)
        delay1 = int(1. / dt)
        targets = np.zeros((sim_steps + 1, 2), dtype=np.float32)
        update4 = lambda x, y: x + dt * (2.0 + y)
        for i in range(sim_steps):
            inp0 = 0. if i < delay0 else targets[i - delay0, 1]
            inp1 = 0. if i < delay1 else targets[i - delay1, 0]
            targets[i + 1, 0] = update4(targets[i, 0], inp0 * 0.5)
            targets[i + 1, 1] = update4(targets[i, 1], inp1 * 2.0)

        diff = np.mean(np.abs(results['a'].values[:, 0] - targets[1:, 0])) + \
               np.mean(np.abs(results['b'].values[:, 0] - targets[1:, 1]))
        assert diff == pytest.approx(0., rel=accuracy, abs=accuracy)

        # test correct numerical evaluation of graph with 2 unidirectionally, multi-delay-coupled nodes
        ###############################################################################################

        # define input
        inp = np.zeros((sim_steps, 1)) + 0.5

        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net9").apply()
        net = ComputeGraph(net_config=net_config,
                           name='net3',
                           vectorization=True,
                           step_size=dt,
                           backend=b)
        results = net.run(sim_time,
                          step_size=dt,
                          outputs={
                              'a': 'pop0/op1/a',
                              'b': 'pop1/op7/a'
                          },
                          inputs={'pop1/op7/inp': inp})
        net.clear()

        # calculate edge behavior from hand
        update3 = lambda x, y, z: x + dt * (y + z - x)
        targets = np.zeros((sim_steps + 1, 2), dtype=np.float32)
        for i in range(sim_steps):
            targets[i + 1, 0] = update2(targets[i, 0], targets[i, 1] * 0.5)
            targets[i + 1, 1] = update3(targets[i, 1], targets[i, 0] * 2.0,
                                        inp[i])

        diff = np.mean(np.abs(results['a'].values[:, 0] - targets[1:, 0])) + \
               np.mean(np.abs(results['b'].values[:, 0] - targets[1:, 1]))
        assert diff == pytest.approx(0., rel=accuracy, abs=accuracy)

        # test correct numerical evaluation of graph with delay distributions
        #####################################################################

        # define input
        dt = 1e-2
        sim_time = 100.
        sim_steps = int(np.round(sim_time / dt, decimals=0))
        inp = np.zeros((sim_steps, 1)) + 0.5

        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net13").apply(
                label='net4')
        net = net_config.compile(vectorization=True,
                                 step_size=dt,
                                 backend=b,
                                 solver='euler')
        results = net.run(sim_time,
                          outputs={
                              'a1': 'p1/op9/a',
                              'a2': 'p2/op9/a'
                          },
                          inputs={'p1/op9/I_ext': inp})
        # TODO: add evaluation of network behavior by hand and compare results against that
        net.clear()
def test_2_2_node():
    """Testing node functionality of compute graph class.

    See Also
    --------
    :method:`add_node`: Detailed documentation of method for adding nodes to instance of `ComputeGraph`.
    """

    backends = ['numpy', 'tensorflow']

    accuracy = 1e-4

    for b in backends:

        # test correct numerical evaluation of node with 2 operators, where op1 projects to op2
        #######################################################################################

        # set up node in pyrates
        dt = 1e-1
        sim_time = 10.
        sim_steps = int(sim_time / dt)
        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net4").apply()
        net = ComputeGraph(net_config=net_config,
                           name='net0',
                           vectorization=True,
                           backend=b)

        # simulate node behavior
        results = net.run(sim_time, outputs={'a': 'pop0/op1/a'}, step_size=dt)
        net.clear()

        # calculate node behavior from hand
        update0 = lambda x: x + dt * 2.
        update1 = lambda x, y: x + dt * (y - x)
        targets = np.zeros((sim_steps + 1, 2), dtype=np.float32)
        for i in range(sim_steps):
            targets[i + 1, 0] = update0(targets[i, 0])
            targets[i + 1, 1] = update1(targets[i, 1], targets[i + 1, 0])

        diff = results['a'].values[:, 0] - targets[:-1, 1]
        assert np.mean(np.abs(diff)) == pytest.approx(0.,
                                                      rel=accuracy,
                                                      abs=accuracy)

        # test correct numerical evaluation of node with 2 independent operators
        ########################################################################

        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net5").apply()
        net = ComputeGraph(net_config=net_config,
                           name='net1',
                           vectorization=True,
                           backend=b)

        # simulate node behavior
        results = net.run(sim_time, outputs={'a': 'pop0/op5/a'}, step_size=dt)
        net.clear()

        # calculate node behavior from hand
        targets = np.zeros((sim_steps + 1, 2), dtype=np.float32)
        for i in range(sim_steps):
            targets[i + 1, 0] = update0(targets[i, 0])
            targets[i + 1, 1] = update1(targets[i, 1], 0.)

        diff = results['a'].values[:, 0] - targets[:-1, 1]
        assert np.mean(np.abs(diff)) == pytest.approx(0.,
                                                      rel=accuracy,
                                                      abs=accuracy)

        # test correct numerical evaluation of node with 2 independent operators projecting to the same target operator
        ###############################################################################################################

        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net6").apply()
        net = ComputeGraph(net_config=net_config,
                           name='net2',
                           vectorization=True,
                           backend=b)
        results = net.run(sim_time, outputs={'a': 'pop0/op1/a'}, step_size=dt)
        net.clear()

        # calculate node behavior from hand
        targets = np.zeros((sim_steps + 1, 3), dtype=np.float32)
        update2 = lambda x: x + dt * (4. + np.tanh(0.5))
        for i in range(sim_steps):
            targets[i + 1, 0] = update0(targets[i, 0])
            targets[i + 1, 1] = update2(targets[i, 1])
            targets[i + 1, 2] = update1(targets[i, 2],
                                        targets[i + 1, 0] + targets[i + 1, 1])

        diff = results['a'].values[:, 0] - targets[:-1, 2]
        assert np.mean(np.abs(diff)) == pytest.approx(0.,
                                                      rel=accuracy,
                                                      abs=accuracy)

        # test correct numerical evaluation of node with 1 source operator projecting to 2 independent targets
        ######################################################################################################

        net_config = CircuitTemplate.from_yaml(
            "model_templates.test_resources.test_backend.net7").apply()
        net = ComputeGraph(net_config=net_config,
                           name='net3',
                           vectorization=True,
                           backend=b)
        results = net.run(sim_time,
                          outputs={
                              'a': 'pop0/op1/a',
                              'b': 'pop0/op3/b'
                          },
                          step_size=dt)

        # calculate node behavior from hand
        targets = np.zeros((sim_steps + 1, 4), dtype=np.float32)
        update3 = lambda a, b, u: a + dt * (-10. * a + b**2 + u)
        update4 = lambda x, y: x + dt * 0.1 * y
        for i in range(sim_steps):
            targets[i + 1, 0] = update0(targets[i, 0])
            targets[i + 1, 1] = update1(targets[i, 1], targets[i + 1, 0])
            targets[i + 1, 2] = update3(targets[i, 2], targets[i, 3],
                                        targets[i + 1, 0])
            targets[i + 1, 3] = update4(targets[i, 3], targets[i, 2])

        diff = np.mean(np.abs(results['a'].values[:, 0] - targets[:-1, 1])) + \
               np.mean(np.abs(results['b'].values[:, 0] - targets[:-1, 3]))
        assert diff == pytest.approx(0., rel=accuracy, abs=accuracy)
示例#8
0
from pyrates.utility.visualization import Interactive2DParamPlot

# parameter definition
dt = 1e-3
dts = 1e-2
cutoff = 100.0
T = 150.0 + cutoff
start = int((0 + cutoff)/dt)
dur = int(5/(0.6*dt))
steps = int(T/dt)
inp = np.zeros((steps, 1))
inp[start:start+dur] = 0.6

# model setup
path = "../config/spinal_cord/sc"
template = CircuitTemplate.from_yaml(path).apply()
plot_network_graph(template)

model = template.compile(backend='numpy', solver='scipy', step_size=dt)

# simulation
results = model.run(simulation_time=T, step_size=dt, sampling_step_size=dts,
                    inputs={'m1/m1_dummy/m_in': inp},
                    outputs={
                        'psp': 'muscle/muscle_op/I_acc',
                        'alpha_exc': 'alpha/alpha_op/I_ampa',
                        'alpha_inh': 'alpha/alpha_op/I_glycin',
                        'renshaw': 'renshaw/renshaw_op/I_acc'
                    }
                    )
示例#9
0
from pyrates.frontend import CircuitTemplate

# %%
# Step 2: Loading a model template from the `model_templates` library
# -------------------------------------------------------------------
#
# In the second step, we load the model template for an excitatory QIF population that comes with PyRates via the
# :code:`from_yaml()` method of the :code:`CircuitTemplate`. This method returns a :code:`CircuitTemplate` instance
# which provides the method :code:`apply()` for turning it into a graph-based representation, i.e. a
# :code:`pyrates.ir.CircuitIR` instance. Have a look at the yaml definition of the model that can be found at the path
# used for the :code:`from_yaml()` method. You will see that all variables and parameters are already defined there.
# These are the basic steps you perform, if you want to load a model that is
# defined inside a yaml file. To check out the different model templates provided by PyRates, have a look at
# the :code:`PyRates.model_templates` module.

qif_circuit = CircuitTemplate.from_yaml(
    "model_templates.montbrio.simple_montbrio.QIF_exc").apply()

# %%
# Step 3: Loading the model into the backend
# ------------------------------------------
#
# In this example, we directly load the :code:`CircuitIR` instance into the backend via the  :code:`compile()` method
# without any further changes to the graph. This way, a :code:`pyrates.backend.NumpyBackend` instance is created.
# After this step, structural modifications of the network are not possible anymore.

qif_compiled = qif_circuit.compile(backend='numpy', step_size=1e-3)

# %%
# Step 4: Numerical simulation of a the model behavior in time
# ------------------------------------------------------------
#
import matplotlib.pyplot as plt
import os
import numpy as np

# parameters
dt = 5e-4
T = 50.0
start = int(10.0 / dt)
stop = int(12.0 / dt)
dts = 1e-2

inp = np.zeros((int(T / dt), 1))
inp[start:stop] = 1.0

# target: delayed biexponential feedback
biexp = CircuitTemplate.from_yaml("config/stn_gpe/biexp_gamma")
r1 = biexp.run(simulation_time=T,
               sampling_step_size=dts,
               inputs={'n1/biexp_rate/I_ext': inp},
               outputs={'r': 'n1/biexp_rate/r'},
               backend='numpy',
               step_size=dt,
               solver='euler')
# fig, ax = plt.subplots()
# ax.plot(r1['r'])
# plt.show()

# approximation: gamma-distributed feedback
param_grid = {
    'd': np.asarray([5.0, 6.0, 7.0]),
    's': np.asarray([1.0, 1.5, 2.0])
示例#11
0
# additional imports
import numpy as np
import matplotlib.pyplot as plt

dt = 1e-1  # integration step size in s
dts = 5e-1  # variable storage sub-sampling step size in s
sub = int(dts / dt)  # sub-sampling rate
T = 1000.0  # total simulation time in ms

inp = np.zeros((int(T / dt), 1),
               dtype='float32')  # external input to the population
start = 200.0
stop = 400.0
inp[int(start / dt):int(stop / dt), :] = 10.0

circuit = CircuitTemplate.from_yaml(
    "model_templates.wilson_cowan.simple_wilsoncowan.RC").apply()
compute_graph = ComputeGraph(circuit,
                             vectorization=True,
                             backend='numpy',
                             name='wc_net',
                             step_size=dt,
                             solver='scipy')

result, t = compute_graph.run(
    T,
    inputs={"P1/Op_rate/I_ext": inp},
    outputs={
        "r1": "P1/Op_rate/r",
        "r2": "P2/Op_rate/r"
    },
    sampling_step_size=dts,
示例#12
0
from pyrates.frontend import CircuitTemplate

# %%
# Step 2: Loading a model template from the `model_templates` library
# -------------------------------------------------------------------
#
# In the second step, we load a model template for the Jansen-Rit model that comes with PyRates via the
# :code:`from_yaml()` method of the :code:`CircuitTemplate`. This method returns a :code:`CircuitTemplate` instance
# which provides the method :code:`apply()` for turning it into a graph-based representation, i.e. a
# :code:`pyrates.ir.CircuitIR` instance. Have a look at the yaml definition of the model that can be found at the path
# used for the :code:`from_yaml()` method. You will see that all variables and parameters are already defined there.
# These are the basic steps you perform, if you want to load a model that is
# defined inside a yaml file. To check out the different model templates provided by PyRates, have a look at
# the :code:`PyRates.model_templates` module.

jrc = CircuitTemplate.from_yaml(
    "model_templates.jansen_rit.simple_jansenrit.JRC_simple").apply()

# %%
# Step 3: Loading the model into the backend
# ------------------------------------------
#
# In this example, we directly load the :code:`CircuitIR` instance into the backend via the  :code:`compile()` method
# without any further changes to the graph. This way, a :code:`pyrates.backend.NumpyBackend` instance is created.
# After this step, structural modifications of the network are not possible anymore. Here, we choose scipy as a solver
# for our differential equation system. The default is the forward Euler method that is implemented in PyRates itself.
# Generally, the scipy solver is both more accurate and faster and thus the recommended solver in PyRates.

jrc_compiled = jrc.compile(backend='numpy', step_size=1e-4, solver='scipy')

# %%
# Step 4: Numerical simulation of a the model behavior in time
# simulations
#############

outputs = {
            'stn': 'stn/stn_op/R',
            'gpe-p': 'gpe_p/gpe_p_op/R',
            'gpe-a': 'gpe_a/gpe_a_op/R',
            'msn-d1': 'msn_d1/msn_d1_op/R',
            'msn-d2': 'msn_d2/msn_d2_op/R',
            'fsi': 'fsi/fsi_op/R',
        }

for c_dict in deepcopy(conditions):

    model = CircuitTemplate.from_yaml("config/stn_gpe_str/stn_gpe_str")
    model = model.update_var(node_vars=node_updates, edge_vars=edge_updates)
    results = model.run(simulation_time=T, step_size=dt, sampling_step_size=dts, outputs=outputs.copy(), solver='scipy',
                        verbose=True, method='RK23', atol=1e-5, rtol=1e-4, clear=True)
    clear_frontend_caches()

    fig, ax = plt.subplots(figsize=(6, 2.0), dpi=dpi)
    results = results * 1e3
    for key in outputs:
        ax.plot(results.loc[:, key])
    plt.legend(list(outputs.keys()))
    ax.set_ylabel('Firing rate')
    ax.set_xlabel('time (ms)')
    # ax.set_xlim([4000.0, 5000.0])
    # ax.set_ylim([0.0, 50.0])
    ax.tick_params(axis='both', which='major', labelsize=9)
示例#14
0
from pyrates.utility.visualization import plot_timeseries, create_cmap

# additional imports
import numpy as np
import matplotlib.pyplot as plt

dt = 1e-3                                      # integration step size in s
dts = 1e-3                                     # variable storage sub-sampling step size in s
sub = int(dts/dt)                              # sub-sampling rate
T = 800.0                                        # total simulation time in s
#inp = np.zeros((int(T/dt), 1), dtype='float32')                 # external input to the population
#inp[int(20./dt):int((T-20.)/dt)] = 5.

circuit = CircuitTemplate.from_yaml("model_templates.montbrio.simple_montbrio.QIF_sfa_exp"
                                    ).apply(node_values={'p/Op_sfa_exp/eta': -3.96,
                                                         'p/Op_sfa_exp/J': 15.0*np.sqrt(2.0),
                                                         'p/Op_sfa_exp/alpha': 0.7}
                                            )
compute_graph = circuit.compile(vectorization=True, backend='numpy', name='montbrio', solver='scipy')

result, t = compute_graph.run(T,
                              step_size=dt,
                              #inputs={"E/Op_e_adapt/inp": inp},
                              outputs={"r": "p/Op_sfa_exp/r",
                                       "v": "p/Op_sfa_exp/v",
                                       "A": "p/Op_sfa_exp/I_a"},
                              sampling_step_size=dts,
                              profile='t',
                              verbose=True,
                              method='LSODA'
                              )
示例#15
0
dt = 1e-3
dts = 1e-2
cutoff = 100.0
T = 200.0 + cutoff
start = int((0 + cutoff) / dt)
dur = int(5 / (0.6 * dt))
steps = int(T / dt)
inp = np.zeros((steps, 1))
inp[start:start + dur] = 0.6

# target: delayed biexponential response of the alpha or renshaw neuron
path = "../config/spinal_cord/sc"
neuron = 'alpha'
target_var = 'I_ampa'
model = CircuitTemplate.from_yaml(path).apply().compile(backend='numpy',
                                                        step_size=dt,
                                                        solver='euler')
r1 = model.run(simulation_time=T,
               sampling_step_size=dts,
               inputs={'m1/m1_dummy/m_in': inp},
               outputs={neuron: f'{neuron}/{neuron}_op/{target_var}'})
model.clear()
r1.plot()
plt.show()

# approximation: gamma-distributed feedback
source = 'm1'
param_grid = {
    'd': np.asarray([1.5, 2.0, 2.5]),
    's': np.asarray([0.4, 0.6, 0.8, 1.0])
}
示例#16
0
def test_2_6_inputs_outputs():
    """Tests the input-output interface of the run method in circuits of different hierarchical depth.

    See Also
    -------
    :method:`CircuitIR.run` detailed documentation of how to use the arguments `inputs` and `outputs`.

    """

    backend = 'numpy'
    dt = 1e-3
    sim_time = 100.
    sim_steps = int(np.round(sim_time / dt, decimals=0))

    # define inputs and outputs for each population separately
    ##########################################################

    # define input
    inp1 = np.zeros((sim_steps, 1)) + 0.5
    inp2 = np.zeros((sim_steps, 1)) + 0.2

    # perform simulation
    net_config = CircuitTemplate.from_yaml(
        "model_templates.test_resources.test_backend.net13").apply(
            label='net1')
    net = net_config.compile(vectorization=True,
                             step_size=dt,
                             backend=backend,
                             solver='scipy')
    r1 = net.run(sim_time,
                 outputs={
                     'a1': 'p1/op9/a',
                     'a2': 'p2/op9/a'
                 },
                 inputs={
                     'p1/op9/I_ext': inp1,
                     'p2/op9/I_ext': inp2
                 })
    net.clear()

    # define input and output for both populations simultaneously
    #############################################################

    backend = 'numpy'

    # define input
    inp = np.zeros((sim_steps, 2))
    inp[:, 0] = 0.5
    inp[:, 1] = 0.2

    # perform simulation
    net_config = CircuitTemplate.from_yaml(
        "model_templates.test_resources.test_backend.net13").apply(
            label='net2')
    net = net_config.compile(vectorization=True,
                             step_size=dt,
                             backend=backend,
                             solver='scipy')
    r2 = net.run(sim_time,
                 outputs={'a': 'all/op9/a'},
                 inputs={'all/op9/I_ext': inp})
    net.clear()

    assert np.mean(r1.values.flatten() - r2.values.flatten()) == pytest.approx(
        0., rel=1e-4, abs=1e-4)

    # repeat in a network with 2 hierarchical levels of node organization
    #####################################################################

    # define input
    inp3 = np.zeros((sim_steps, 1)) + 0.1
    inp4 = np.zeros((sim_steps, 1))

    # perform simulation
    nc1 = CircuitTemplate.from_yaml(
        "model_templates.test_resources.test_backend.net14").apply(
            label='net3')
    n1 = nc1.compile(vectorization=True,
                     step_size=dt,
                     backend=backend,
                     solver='scipy')
    r1 = n1.run(sim_time,
                outputs={
                    'a1': 'c1/p1/op9/a',
                    'a2': 'c1/p2/op9/a',
                    'a3': 'c2/p1/op9/a',
                    'a4': 'c2/p2/op9/a'
                },
                inputs={
                    'c1/p1/op9/I_ext': inp1,
                    'c1/p2/op9/I_ext': inp2,
                    'c2/p1/op9/I_ext': inp3,
                    'c2/p2/op9/I_ext': inp4
                })
    n1.clear()

    # define input
    inp = np.zeros((sim_steps, 4))
    inp[:, 0] = 0.5
    inp[:, 1] = 0.2
    inp[:, 2] = 0.1

    # perform simulation
    nc2 = CircuitTemplate.from_yaml(
        "model_templates.test_resources.test_backend.net14").apply(
            label='net4')
    n2 = nc2.compile(vectorization=True,
                     step_size=dt,
                     backend=backend,
                     solver='scipy')
    r2 = n2.run(sim_time,
                outputs={'a': 'all/all/op9/a'},
                inputs={'all/all/op9/I_ext': inp})
    n2.clear()

    assert np.mean(r1.values.flatten() - r2.values.flatten()) == pytest.approx(
        0., rel=1e-4, abs=1e-4)
示例#17
0
# documentation can be found
# `here <https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.differential_evolution.html>`_. All
# arguments that :code:`scipy.optimize.differential_evolution` takes can also be provided as keyword arguments to the
# :code:`run()` method. By providing a :code:`template`, :code:`compile_kwargs` and :code:`run_kwargs`, the
# :code:`run()` method knows that it should use the provided model template, load it into the backend via
# :code:`CircuitIR.compile(**compile_kwargs)` and then simulate its behavior via :code:`CircuitIR.run(**run_kwargs)`.
# The resulting timeseries is then forwarded to the :code:`loss_func` together with the keyword arguments in
# :code:`loss_kwargs`.
#
# The return value of the :code:`run()` method contains the winning parameter set and its loss function value.
# Let's check out, whether this model parameter indeed produces the behavior we optimized for:

from pyrates.frontend import CircuitTemplate
from matplotlib.pyplot import show

jr_temp = CircuitTemplate.from_yaml(model_template).apply()
jr_temp.set_node_var('JRC/JRC_op/c', winner.at[0, 'C'])
jr_comp = jr_temp.compile(solver='scipy', backend='numpy', step_size=1e-4)
results = jr_comp.run(simulation_time=3.0,
                      sampling_step_size=1e-2,
                      outputs={
                          'V_pce': 'JRC/JRC_op/PSP_pc_e',
                          'V_pci': 'JRC/JRC_op/PSP_pc_i'
                      })

results = results['V_pce'] - results['V_pci']
results.plot()
jr_comp.clear()
show()

# %%
示例#18
0
# %%
# Part 1: Creating a PyAuto Instance
# ==================================
#
# In this first part, we will be concerned with how to create a model representation that is compatible with auto-07p,
# which is the software that is used for parameter continuations and bifurcation analysis in PyRates [2]_.

# %%
# Step 1: Load the model into PyRates
# -----------------------------------
#
# As a first step, we have to load the model into PyRates. This is done the usual way. If you are not familiar with
# this, check out the example galleries for model definitions.

from pyrates.frontend import CircuitTemplate
qif = CircuitTemplate.from_yaml("model_templates.montbrio.simple_montbrio.QIF_exc").apply(label='qif')

# %%
# Step 2: Load the model into the backend
# ---------------------------------------
#
# Here, we will parse the model equations into the PyRates backend. If you are not familiar with this, check out the
# example galleries for model compilation and simulation. This step, however, differs slightly from the usual way.
# We need to use the :code:`FortranBackend`, since :code:`auto07-p` requires a fortran file with the model equations
# and initial values [2]_. Furthermore, we need to set the keyword argument :code:`auto_compat` of the :code:`compile()`
# method to :code:`True`, to indicate to the :code:`FortranBackend` that it should compile the model equations in a way
# that is compatible with auto-07p [2]_.

qif_compiled = qif.compile(backend='fortran', auto_compat=True)

# %%
示例#19
0
References
^^^^^^^^^^

.. [1] B.H. Jansen & V.G. Rit (1995) *Electroencephalogram and visual evoked potential generation in a mathematical
       model of coupled cortical columns.* Biological Cybernetics, 73(4): 357-366.
"""

# %%
# Part 1: Translating a model definition into an intermediate representation
# --------------------------------------------------------------------------
#
# First, we will need to load a model template into PyRates. We choose to load a YAML-based model definition, but this
# can be done just as well from a Python-based model definition.

from pyrates.frontend import CircuitTemplate
jr_template = CircuitTemplate.from_yaml(
    "model_templates.jansen_rit.simple_jansenrit.JRC")

print(jr_template.nodes)
print(jr_template.edges)

# %%
# As demonstrated by the :code:`print()` calls above, the :code:`CircuitTemplate` contains fields for nodes and edges,
# which contain list of :code:`NodeTemplate` and :code:`EdgeTemplate` instances with :code:`OperatorTemplate` instances
# defined on them that contain the underlying model equations. This template can be transformed into an intermediate
# representation via the :code:`apply()` method defined for each template class:

jr_ir = jr_template.apply()

print(jr_ir.nodes)
print(jr_ir.edges)
示例#20
0
def grid_search(circuit_template: Union[CircuitTemplate, str],
                param_grid: Union[dict, pd.DataFrame],
                param_map: dict,
                dt: float,
                simulation_time: float,
                inputs: dict,
                outputs: dict,
                sampling_step_size: Optional[float] = None,
                permute_grid: bool = False,
                init_kwargs: dict = None,
                **kwargs) -> tuple:
    """Function that runs multiple parametrizations of the same circuit in parallel and returns a combined output.

    Parameters
    ----------
    circuit_template
        Path to the circuit template.
    param_grid
        Key-value pairs for each circuit parameter that should be altered over different circuit parametrizations.
    param_map
        Key-value pairs that map the keys of param_grid to concrete circuit variables.
    dt
        Simulation step-size in s.
    simulation_time
        Simulation time in s.
    inputs
        Inputs as provided to the `run` method of `:class:ComputeGraph`.
    outputs
        Outputs as provided to the `run` method of `:class:ComputeGraph`.
    sampling_step_size
        Sampling step-size as provided to the `run` method of `:class:ComputeGraph`.
    permute_grid
        If true, all combinations of the provided param_grid values will be realized. If false, the param_grid values
        will be traversed pairwise.
    kwargs
        Additional keyword arguments passed to the `:class:ComputeGraph` initialization.



    Returns
    -------
    tuple
        Simulation results stored in a multi-index data frame, the mapping between the data frame column names and the
        parameter grid, the simulation time, and the memory consumption.

    """

    # argument pre-processing
    #########################

    if not init_kwargs:
        init_kwargs = {}
    vectorization = init_kwargs.pop('vectorization', 'nodes')
    if type(circuit_template) is str:
        circuit_template = CircuitTemplate.from_yaml(circuit_template)

    # linearize parameter grid if necessary
    if type(param_grid) is dict:
        param_grid = linearize_grid(param_grid, permute_grid)

    # create grid-structure of network
    ##################################

    # get parameter names and grid length
    param_keys = list(param_grid.keys())
    N = param_grid.shape[0]

    # assign parameter updates to each circuit, combine them to unconnected network and remember their parameters
    circuit = CircuitIR()
    circuit_names = []
    for idx in param_grid.index:
        new_params = {}
        for key in param_keys:
            new_params[key] = param_grid[key][idx]
        circuit_tmp = circuit_template.apply()
        circuit_key = f'{circuit_tmp.label}_{idx}'
        circuit_tmp = adapt_circuit(circuit_tmp, new_params, param_map)
        circuit.add_circuit(circuit_key, circuit_tmp)
        circuit_names.append(circuit_key)
    param_grid.index = circuit_names

    # create backend graph
    net = circuit.compile(dt=dt, vectorization=vectorization, **init_kwargs)

    # adjust input of simulation to combined network
    for inp_key, inp in inputs.copy().items():
        inputs[f"all/{inp_key}"] = np.tile(inp, (1, N))
        inputs.pop(inp_key)

    # adjust output of simulation to combined network
    for out_key, out in outputs.items():
        outputs[out_key] = f"all/{out}"

    # simulate the circuits behavior
    results = net.run(simulation_time=simulation_time,
                      inputs=inputs,
                      outputs=outputs,
                      sampling_step_size=sampling_step_size,
                      **kwargs)  # type: pd.DataFrame
    if 'profile' in kwargs:
        results, duration = results

    if 'profile' in kwargs:
        return results, param_grid, duration
    return results, param_grid
def benchmark(Ns, Ps, T, dt, init_kwargs, run_kwargs, disable_gpu=False):
    """Function that will run a benchmark simulation for each combination of N and P.
    Each benchmark simulation simulates the behavior of a neural population network, where the Jansen-Rit model is used for each of the N nodes and
    connections are drawn randomly, such that on overall coupling density of P is established.

    Parameters
    ----------
    Ns
        Vector with network sizes.
    Ps
        Vector with coupling densities.
    T
        Overall simulation time.
    dt
        Integration step-size.
    init_kwargs
        Additional key-word arguments for the model initialization.
    run_kwargs
        Additional key-word arguments for running the simulation.
    disable_gpu
        If true, GPU devices will be disabled, so the benchmark will be run on the CPU only.

    Returns
    -------
    tuple
        Simulation times, peak memory consumptions

    """

    if disable_gpu:
        os.environ['CUDA_VISIBLE_DEVICES'] = '-1'
    else:
        os.environ['CUDA_VISIBLE_DEVICES'] = '0'

    times = np.zeros((len(Ns), len(Ps)))

    for i, n in enumerate(Ns):
        for j, p in enumerate(Ps):

            print(f'Running benchmark for n = {n} and p = {p}.')
            print("Setting up the network in PyRates...")

            # define inter-JRC connectivity
            C = np.random.uniform(size=(n, n))
            C[C > p] = 0.
            c_sum = np.sum(C, axis=1)
            for k in range(C.shape[0]):
                if c_sum[k] != 0.:
                    C[k, :] /= c_sum[k]
            conns = DataFrame(
                np.round(C, 3),
                columns=[f'jrc_{idx}/PC/PRO/m_out' for idx in range(n)])
            conns.index = [f'jrc_{idx}/PC/RPO_e_pc/m_in' for idx in range(n)]

            # define input
            inp = 220 + np.asarray(np.random.randn(int(T / dt), n),
                                   dtype=np.float32) * 22.

            # set up template
            template = CircuitTemplate.from_yaml(
                "model_templates.jansen_rit.simple_jansenrit.JRC")

            # set up intermediate representation
            circuits = {}
            for idx in range(n):
                circuits[f'jrc_{idx}'] = deepcopy(template)
            circuit = CircuitIR.from_circuits(label='net',
                                              circuits=circuits,
                                              connectivity=conns)

            # set up compute graph
            net = circuit.compile(dt=dt, **init_kwargs)

            print("Starting the benchmark simulation...")

            # run simulations
            _, t = net.run(T,
                           inputs={'all/PC/RPO_e_pc/u': inp},
                           outputs={'V': 'all/PC/OBS/V'},
                           verbose=False,
                           **run_kwargs)
            times[i, j] = t

            print("Finished!")
            print(f'simulation time: {t} s.')

    return times