:py:mod:`cubnm.sim.jr`
#####################

.. py:module:: cubnm.sim.jr


.. autoapisummary::

   cubnm.sim.jr.JRSimGroup




.. py:class:: JRSimGroup(*args, **kwargs)


   Bases: :py:obj:`cubnm.sim.base.SimGroup`

   Group of Jansen-Rit simulations that are executed in parallel.

   Parameters
   ---------
   *args, **kwargs:
       see :class:`cubnm.sim.SimGroup` for details

   Attributes
   ----------
   param_lists: :obj:`dict` of :obj:`np.ndarray`
       dictionary of parameter lists, including
           - ``'G'``: global coupling strength. Shape: (N_SIMS,)
           - ``'J'``: average number of synapses. Shape: (N_SIMS, nodes)
           - ``'a_1'``: average probability of synapses in feedback excitatory loop. Shape: (N_SIMS, nodes)
           - ``'a_2'``: average probability of synapses in slow feedback excitatory loop. Shape: (N_SIMS, nodes)
           - ``'a_3'``: average probability of synapses in feedback inhibitory loop. Shape: (N_SIMS, nodes)
           - ``'a_4'``: average probability of synapses in slow feedback inhibitory loop. Shape: (N_SIMS, nodes)
           - ``'sigma'``: local noise sigma. Shape: (N_SIMS, nodes)
           - ``'v'``: conduction velocity. Shape: (N_SIMS,)

   Equations
   ---------
   .. math::

       \begin{gather}
       \dot{y_{0_{i}}} = y_{3_{i}} \\
       \dot{y_{1_{i}}} = y_{4_{i}} \\
       \dot{y_{2_{i}}} = y_{5_{i}} \\
       \dot{y_{3_{i}}} = A a [\frac{2 \nu_{max}}{1+e^{r(v_0 - (y_{2_{i}} - y_{1_{i}}))}}]-2 a y_{3_{i}}-a^2 y_{0_{i}} + \sigma_i\epsilon_i \\
       \dot{y_{4_{i}}} = A a [\mu+a_{2_{i}} J_i \frac{2\nu_{max}}{1+e^{r(v_0 - (a_{1_{i}} J y_{1_{i}}))}}+ \sum_j{C_{ij}x_j} \\
       \dot{y_{5_{i}}} = B b (a_{4_{i}} J_i [\frac{2\nu_{max}}{1 + e^{r(v_0 - (a_{3_{i}} J y_{0_{i}})}}]) - 2by_{5_{i}} - b^2 y_{2_{i}} \\
       z_i = y_{1_{i}} - y_{2_{i}} \\
       x_i = c_{min} + \frac{c_{max}-c_{min}}{1 + e^{r(c_{mid} -z_i)}} \\
       \end{gather}

   References
   ----------        
   * Jansen and Rit 1995 Biological Cybernetics (10.1007/BF00199471)
   * Ritter et al. 2013 Brain Connectivity (10.1089/brain.2012.0120)

   .. py:attribute:: model_name
      :value: 'JR'

      

   .. py:attribute:: global_param_names
      :value: ['G']

      

   .. py:attribute:: regional_param_names
      :value: ['J', 'a_1', 'a_2', 'a_3', 'a_4', 'sigma']

      

   .. py:attribute:: state_names
      :value: ['y0', 'y1', 'y2', 'y3', 'y4', 'y5', 'y1_y2', 's_y1_y2']

      

   .. py:attribute:: sel_state_var
      :value: 'y1_y2'

      

   .. py:attribute:: n_noise
      :value: 1

      

   .. py:attribute:: default_params

      

   .. py:method:: post_init()

      Post-initilaization hook that normally does nothing, but
      can be overridden in derived classes to add custom
      post-initialization steps.


   .. py:method:: _check_dt()

      Check if integrations steps are valid


   .. py:method:: _set_default_params(missing=True)

      Set default parameters for the simulations.

      Parameters
      ----------
      missing: :obj:`bool`, optional
          If True (default), only sets parameters that are currently None
          or have an incorrect length in self.param_lists. 
          If False, always overwrites all listed defaults.


   .. py:method:: get_config(include_N=False, for_reinit=False)

      Get the configuration of the simulation group

      Parameters
      ----------
      include_N: :obj:`bool`
          include N in the output config
          is ignored when for_reinit is True
      for_reinit: :obj:`bool`
          include the parameters that need reinitialization of the
          simulation core session if changed

      Returns
      -------
      config: :obj:`dict`
          dictionary of simulation group configuration


   .. py:method:: run(force_reinit=False)

      Run the simulations in parallel (as possible) on GPU/CPU
      through the :func:`cubnm._core.run_simulations` function which runs
      compiled C++/CUDA code.

      Parameters
      ----------
      force_reinit: :obj:`bool`
          force reinitialization of the session.
          At the beginning of each session (when `cubnm` is imported)
          some variables are initialized on CPU/GPU and reused in
          every run. Set this to True if you want to reinitialize
          these variables. This is rarely needed.


   .. py:method:: _process_out(out)

      Assigns model outputs (as arrays) to object attributes
      with correct shapes, names and types.

      Parameters
      ----------
      out: :obj:`tuple`
          output of ``cubnm._core.run_simulations`` function

      Notes
      -----
      The simulation outputs are assigned to the following object attributes:

      - init_time: :obj:`float`
          initialization time of the simulations
      - run_time: :obj:`float`
          run time of the simulations
      - sim_bold: :obj:`np.ndarray`
          simulated BOLD time series. Shape: (N_SIMS, duration/TR, nodes)
      - sim_states: :obj:`dict` of :obj:`np.ndarray`
          simulated state variables with keys as state names
          and values as arrays with the shape (N_SIMS, nodes)
          when ``states_ts`` is False, and (N_SIMS, duration/TR, nodes)
          when ``states_ts`` is True

      If ``do_fc`` is ``True``, additionally includes:

      - sim_fc_trils: :obj:`np.ndarray`
          simulated FC lower triangle. Shape: (N_SIMS, n_pairs)

      If ``do_fcd`` is ``True``, additionally includes:

      - sim_fcd_trils: :obj:`np.ndarray`
          simulated FCD lower triangle. Shape: (N_SIMS, n_window_pairs)


   .. py:method:: get_state_averages()

      Get the averages of state variables across time and nodes
      for each simulation.

      Returns
      -------
      state_averages: :obj:`pd.DataFrame`
          DataFrame of state averages with columns as state names
          and rows as simulations


   .. py:method:: get_noise()

      Get the (recreated) noise time series. This requires recreation
      of the noise array if noise segmenting is on. Noise will be
      recreated based on shuffling indices of nodes and time steps,
      similar to how it is done in the core code.

      Returns
      -------
      noise: :obj:`np.ndarray`
          Noise segment array. Shape: (n_noise, nodes, bw_it, inner_it)


   .. py:method:: get_sim_fc(idx)

      Get the simulated FC of a given simulation ``idx``
      as a square matrix.

      Parameters
      ----------
      idx: :obj:`int`
          index of the simulation to get the FC for

      Returns
      -------
      fc: :obj:`np.ndarray`
          simulated FC matrix of shape (nodes, nodes)
          for the simulation with index ``idx``


   .. py:method:: get_sim_fcd(idx)

      Get the simulated FCD of a given simulation ``idx``
      as a square matrix.

      Parameters
      ----------
      idx: :obj:`int`
          index of the simulation to get the FC for

      Returns
      -------
      fcd: :obj:`np.ndarray`
          simulated FC matrix of shape (n_windows, n_windows)
          for the simulation with index ``idx``


   .. py:method:: slice(key, inplace=False)

      Slice the simulation group to a single simulation

      Parameters
      ----------
      key: :obj:`int`
          index of the simulation to slice
      inplace: :obj:`bool`
          the object will be sliced in place
          and therefore the data of other simulations
          will be removed. Otherwise a new object
          copied from the current object will be returned.

      Returns
      -------
      obj: :obj:`cubnm.sim.SimGroup`
          sliced simulation group


   .. py:method:: clear()

      Clear the simulation outputs


   .. py:method:: _problem_init(problem)

      Extends BNMProblem initialization if needed.
      By default it doesn't do anything.

      Parameters
      ----------
      problem: :obj:`cubnm.optimize.BNMProblem`
          optimization problem object


   .. py:method:: _problem_evaluate(problem, X, out, *args, **kwargs)

      Extends BNMProblem evaluation if needed.
      By default it doesn't do anything.

      Parameters
      ----------
      X: :obj:`np.ndarray`
          the normalized parameters of current population in range [0, 1]. 
          Shape: (N, ndim)
      out: :obj:`dict`
          the output dictionary to store the results with keys 'F' and 'G'.
          Currently only 'F' (cost) is used.
      *args, **kwargs


   .. py:method:: score(emp_fc_tril=None, emp_fcd_tril=None, emp_bold=None, force_cpu=False, usable_mem=None)

      Calcualates individual goodness-of-fit terms and aggregates them.

      .. note::
          If `emp_bold` is provided, `emp_fc_tril` and `emp_fcd_tril` will be ignored.

      .. note::
          For each measure, if the value is NaN, it will be set to the "worst" possible value.
          NaNs may occur in simulated FCD or FC. For example, in the rWWEx model, when excitation
          is too high and noise is low, `S` and in turn `BOLD` in some areas may become saturated
          and show no variability. This can result in correlations of their BOLD signals with
          other nodes (within certain dynamic windows) being NaN.

      Parameters
      --------
      emp_fc_tril: :obj:`np.ndarray` or None
          1D array of empirical FC lower triangle. Shape: (edges,)
      emp_fcd_tril: :obj:`np.ndarray` or None
          1D array of empirical FCD lower triangle. Shape: (window_pairs,)
      emp_bold: :obj:`np.ndarray` or None
          cleaned and parcellated empirical BOLD time series. Shape: (nodes, volumes)
          Motion outliers should either be excluded (not recommended as it disrupts
          the temporal structure) or replaced with zeros.
          If provided emp_fc_tril and emp_fcd_tril will be ignored.
      force_cpu: :obj:`bool`
          force CPU for the calculations. Otherwise if GPU is available
          some of the scores (including "+fc_corr", "-fcd_ks") 
          will be calculated on GPU.
      usable_mem: :obj:`int`
          amount of available GPU memory to be used in bytes.
          If None, 80% of the free memory will be used.

      Returns
      -------
      scores: :obj:`pd.DataFrame`
          The goodness of fit measures (columns) of each simulation (rows)


   .. py:method:: _get_save_data()

      Get the simulation outputs and parameters to be saved to disk

      Returns
      -------
      out_data: :obj:`dict`
          dictionary of simulation outputs and parameters


   .. py:method:: save()

      Save simulation outputs to disk as an npz file.


   .. py:method:: _get_test_configs(cpu_gpu_identity=False)
      :classmethod:

      Get configs for testing the simulations

      Parameters
      ----------
      cpu_gpu_identity: :obj:`bool`
          indicates whether configs are for CPU/GPU identity tests
          in which case force_cpu is not included in the configs
          since tests will be done on both CPU and GPU

      Returns
      -------
      configs: :obj:`dict` of :obj:`list`


   .. py:method:: _get_test_instance(opts)
      :classmethod:

      Initializes an instance that is used in tests

      Parameters
      ----------
      opts: :obj:`dict`
          dictionary of test options

      Returns
      -------
      sim_group: :obj:`cubnm.sim.SimGroup`
          simulation group object of the test simulation
          which is not run yet



