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

.. py:module:: cubnm.sim

.. autoapi-nested-parse::

   Simulation of the models



.. autoapisummary::

   cubnm.sim.SimGroup
   cubnm.sim.rWWSimGroup
   cubnm.sim.rWWExSimGroup
   cubnm.sim.KuramotoSimGroup
   cubnm.sim.MultiSimGroupMixin



.. autoapisummary::

   cubnm.sim.create_multi_sim_group



.. autoapisummary::

   cubnm.sim.has_cupy
   cubnm.sim.WORST_SCORES


.. py:data:: has_cupy
   :value: True

   

.. py:data:: WORST_SCORES

   

.. py:class:: SimGroup(duration, TR, sc, sc_dist=None, out_dir=None, dt='0.1', bw_dt='1.0', ext_out=True, states_ts=False, states_sampling=None, noise_out=False, do_fc=True, do_fcd=True, window_size=30, window_step=5, rand_seed=410, exc_interhemispheric=False, force_cpu=False, force_gpu=False, serial_nodes=False, gof_terms=['+fc_corr', '-fcd_ks'], bw_params='friston2003', bold_remove_s=30, fcd_drop_edges=True, noise_segment_length=30, sim_verbose=False, progress_interval=500)


   Group of simulations that are executed in parallel
   (as possible depending on the available hardware)
   on GPU/CPU.

   Parameters
   ---------
   duration: :obj:`float`
       simulation duration (in seconds)
   TR: :obj:`float`
       BOLD TR (in seconds)
   sc: :obj:`str` or :obj:`np.ndarray`
       path to structural connectome strengths (as an unlabled .txt/.npy)
       or a numpy array
       Shape: (nodes, nodes)
       If asymmetric, rows are sources and columns are targets.
   sc_dist: :obj:`str` or :obj:`np.ndarray`, optional
       path to structural connectome distances (as an unlabled .txt)
       or a numpy array
       Shape: (nodes, nodes)
       If provided v (velocity) will be a free parameter and there
       will be delay in inter-regional connections
       If asymmetric, rows are sources and columns are targets.
   out_dir: {:obj:`str` or 'same' or None}, optional
       - :obj:`str`: will create a directory in the provided path
       - 'same': will create a directory named based on sc
           (only should be used when sc is a path and not a numpy array)
       - None: will not have an output directory (and cannot save outputs)
   dt: :obj:`decimal.Decimal` or :obj:`str`, optional
       model integration time step (in msec)
   bw_dt: :obj:`decimal.Decimal` or :obj:`str`, optional
       Ballon-Windkessel integration time step (in msec)
   ext_out: :obj:`bool`, optional
       return model state variables to self.sim_states
   states_ts: :obj:`bool`, optional
       return time series of model states to self.sim_states
       Note that this will increase the memory usage and is not
       recommended for large number of simulations (e.g. in a grid search)
   states_sampling: :obj:`float`, optional
       sampling rate of model states in seconds.
       Default is None, which uses BOLD TR as the sampling rate.
   noise_out: :obj:`bool`, optional
       return noise time series
   do_fc: :obj:`bool`, optional
       calculate simulated functional connectivity (FC)
   do_fcd: :obj:`bool`, optional
       calculate simulated functional connectivity dynamics (FCD)
   window_size: :obj:`int`, optional
       dynamic FC window size (in seconds)
       will be converted to N TRs (nearest even number)
       The actual window size is number of TRs + 1 (including center)
   window_step: :obj:`int`, optional
       dynamic FC window step (in seconds)
       will be converted to N TRs
   rand_seed: :obj:`int`, optional
       seed used for the noise simulation
   exc_interhemispheric: :obj:`bool`, optional
       excluded interhemispheric connections from sim FC and FCD calculations
   force_cpu: :obj:`bool`, optional
       use CPU for the simulations (even if GPU is available). If set
       to False the program might use GPU or CPU depending on GPU
       availability
   force_gpu: :obj:`bool`, optional
       on some HPC/HTC systems occasionally GPUtil might not detect an 
       available GPU device. Use this if there is a GPU available but
       is not being used for the simulation. If set to True but a GPU
       is not available will lead to errors.
   serial_nodes: :obj:`bool`, optional
       only applicable to GPUs; uses one thread per simulation and do calculation
       of nodes serially. This is an experimental feature which is generally not 
       recommended and has significantly slower performance in typical use cases. 
       Only may provide performance benefits with very large grids as computing 
       time does not scale with the number of simulations as much as the 
       parallel (default) mode.
   gof_terms: :obj:`list` of :obj:`str`, optional
       list of goodness-of-fit terms to be used for scoring. May include:
       - '-fcd_ks': negative Kolmogorov-Smirnov distance of FCDs
       - '+fc_corr': Pearson correlation of FCs
       - '-fc_diff': negative absolute difference of FC means
       - '-fc_normec': negative Euclidean distance of FCs                 divided by max EC [sqrt(n_pairs*4)]
   bw_params: {'friston2003' or 'heinzle2016-3T' or :obj:`dict`}, optional
       see :func:`cubnm.utils.get_bw_params` for details
   bold_remove_s: :obj:`float`, optional
       remove the first bold_remove_s seconds from the simulated BOLD
       in FC, FCD and mean state calculations (but the entire BOLD will
       be returned to .sim_bold)
   fcd_drop_edges: :obj:`bool`, optional
       drop the edge windows in FCD calculations
   noise_segment_length: :obj:`float` or None, optional
       in seconds, length of the noise segments in the simulations
       The noise segment will be repeated after shuffling
       of nodes and time points. To generate noise for the entire
       simulation without repetition, set this to None.
       Note that varying the noise segment length will result
       in a different noise array even if seed is fixed (but
       fixed combination of seed and noise_segment_length will
       result in reproducible noise)
   sim_verbose: :obj:`bool`, optional
       verbose output of the simulation including details of
       simulations and a progress bar. This may slightly make
       the simulations slower.
   progress_interval: :obj:`int`, optional
       msec; interval of progress updates in the simulation
       Only used if sim_verbose is True

   Attributes
   ---------
   param_lists: :obj:`dict` of :obj:`np.ndarray`
       dictionary of parameter lists, including
           - global parameters with shape (N_SIMS,)
           - regional parameters with shape (N_SIMS, nodes)
           - 'v': conduction velocity. Shape: (N_SIMS,)
   Additional attributes will be added after running the simulations.
   See :func:`cubnm.sim.SimGroup._process_out` for details.

   Notes
   -----
   Derived classes must set the following attributes:
       model_name: :obj:`str`
           name of the model used in the simulations
       global_param_names: :obj:`list` of :obj:`str`
           names of global parameters
       regional_param_names: :obj:`list` of :obj:`str`
           names of regional parameters
       state_names: :obj:`list` of :obj:`str`
           names of the state variables
       sel_state_var: :obj:`str`
           name of the state variable used in the tests
       n_noise: :obj:`int`
           number of noise elements per node per time point
           (e.g. 2 if there are noise to E and I neuronal populations)
   And they must implement the following methods:
       _set_default_params: set default (example) parameters for the simulations

   .. py:method:: _check_dt()

      Check if integrations steps are valid


   .. py:method:: _set_default_params()

      Set default parameters for the simulations.
      This is used in tests.


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

      Get the configuration of the simulation group

      Parameters
      ----------
      include_N: :obj:`bool`, optional
          include N in the output config
          is ignored when for_reinit is True
      for_reinit: :obj:`bool`, optional
          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`, optional
          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 `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)
      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)
      If `ext_out` is True, additionally includes:
          - 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


   .. 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, time_steps, 10)


   .. 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`, optional
          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`, optional
          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`, optional
          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`, optional
          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



.. py:class:: rWWSimGroup(*args, do_fic=True, max_fic_trials=0, fic_penalty=True, fic_i_sampling_start=1000, fic_i_sampling_end=10000, fic_init_delta=0.02, **kwargs)


   Bases: :py:obj:`SimGroup`

   Group of reduced Wong-Wang simulations (Deco 2014) 
   that are executed in parallel

   Parameters
   ---------
   do_fic: :obj:`bool`, optional
       do analytical (Demirtas 2019) & numerical (Deco 2014) 
       Feedback Inhibition Control.
       If provided wIE parameters will be ignored
   max_fic_trials: :obj:`int`, optional
       maximum number of trials for FIC numerical adjustment.
       If set to 0, FIC will be done only analytically
   fic_penalty: :obj:`bool`, optional
       penalize deviation from FIC target mean rE of 3 Hz
   fic_i_sampling_start: :obj:`int`, optional
       starting time of numerical FIC I_E sampling (msec)
   fic_i_sampling_end: :obj:`int`, optional
       end time of numerical FIC I_E sampling (msec)
   fic_init_delta: :obj:`float`, optional
   *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. Shape: (N_SIMS,)
           - 'wEE': local excitatory self-connection strength. Shape: (N_SIMS, nodes)
           - 'wEI': local inhibitory self-connection strength. Shape: (N_SIMS, nodes)
           - 'wIE': local excitatory to inhibitory connection strength. Shape: (N_SIMS, nodes)
           - 'v': conduction velocity. Shape: (N_SIMS,)

   Example
   -------
   To see example usage in grid search and evolutionary algorithms
   see :mod:`cubnm.optimize`.

   Here, as an example on how to use SimGroup independently, we
   will run a single simulation and save the outputs to disk. ::

       from cubnm import sim, datasets

       sim_group = sim.rWWSimGroup(
           duration=60,
           TR=1,
           sc=datasets.load_sc('strength', 'schaefer-100'),
       )
       sim_group.N = 1
       sim_group.param_lists['G'] = np.repeat(0.5, N_SIMS)
       sim_group.param_lists['wEE'] = np.full((N_SIMS, nodes), 0.21)
       sim_group.param_lists['wEI'] = np.full((N_SIMS, nodes), 0.15)
       sim_group.run()

   .. py:attribute:: model_name
      :value: 'rWW'

      

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

      

   .. py:attribute:: regional_param_names
      :value: ['wEE', 'wEI', 'wIE']

      

   .. py:attribute:: state_names
      :value: ['I_E', 'I_I', 'r_E', 'r_I', 'S_E', 'S_I']

      

   .. py:attribute:: sel_state_var
      :value: 'r_E'

      

   .. py:attribute:: n_noise
      :value: 2

      

   .. py:method:: get_config(*args, **kwargs)

      Get the configuration of the simulation group

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

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


   .. py:method:: _set_default_params()

      Set default parameters for the simulations.
      This is used in tests.


   .. 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 `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)
      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)
      If `ext_out` is True, additionally includes:
          - 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


   .. py:method:: score(emp_fc_tril=None, emp_fcd_tril=None, emp_bold=None, fic_penalty_scale=2)

      Calcualates individual goodness-of-fit terms and aggregates them.
      In FIC models also calculates fic_penalty.

      Parameters
      --------
      emp_fc_tril: :obj:`np.ndarray`
          1D array of empirical FC lower triangle. Shape: (edges,)
      emp_fcd_tril: :obj:`np.ndarray`
          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.
      fic_penalty_scale: :obj:`float`, optional
          scale of the FIC penalty term.
          Set to 0 to disable the FIC penalty term.
          Note that while it is included in the cost function of
          optimizer, it is not included in the aggregate GOF

      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:: clear()

      Clear the simulation outputs


   .. py:method:: _problem_init(problem)

      Extends BNMProblem initialization and includes FIC penalty
      if indicated.

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


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

      Extends BNMProblem evaluation and includes FIC penalty
      in the cost function if indicated.

      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:: _get_test_configs(cpu_gpu_identity=False)
      :classmethod:

      Get configs for testing the simulations

      Parameters
      ----------
      cpu_gpu_identity: :obj:`bool`, optional
          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.rWWSimGroup`
          simulation group object of the test simulation
          which is not run yet


   .. py:method:: _check_dt()

      Check if integrations steps are valid


   .. 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`, optional
          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:: 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, time_steps, 10)


   .. 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`, optional
          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:: save()

      Save simulation outputs to disk as an npz file.



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


   Bases: :py:obj:`SimGroup`

   Group of reduced Wong-Wang simulations (excitatory only, Deco 2013) 
   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. Shape: (N_SIMS,)
           - 'w': local excitatory self-connection strength. Shape: (N_SIMS, nodes)
           - 'I0': local external input current. Shape: (N_SIMS, nodes)
           - 'sigma': local noise sigma. Shape: (N_SIMS, nodes)
           - 'v': conduction velocity. Shape: (N_SIMS,)

   Example
   -------
   To see example usage in grid search and evolutionary algorithms
   see :mod:`cubnm.optimize`.

   Here, as an example on how to use SimGroup independently, we
   will run a single simulation and save the outputs to disk. ::

       from cubnm import sim, datasets

       sim_group = sim.rWWExSimGroup(
           duration=60,
           TR=1,
           sc=datasets.load_sc('strength', 'schaefer-100'),
       )
       sim_group.N = 1
       sim_group.param_lists['G'] = np.repeat(0.5, N_SIMS)
       sim_group.param_lists['w'] = np.full((N_SIMS, nodes), 0.9)
       sim_group.param_lists['I0'] = np.full((N_SIMS, nodes), 0.3)
       sim_group.param_lists['sigma'] = np.full((N_SIMS, nodes), 0.001)
       sim_group.run()

   .. py:attribute:: model_name
      :value: 'rWWEx'

      

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

      

   .. py:attribute:: regional_param_names
      :value: ['w', 'I0', 'sigma']

      

   .. py:attribute:: state_names
      :value: ['x', 'r', 'S']

      

   .. py:attribute:: sel_state_var
      :value: 'r'

      

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

      

   .. py:method:: _set_default_params()

      Set default parameters for the simulations.
      This is used in tests.


   .. py:method:: _check_dt()

      Check if integrations steps are valid


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

      Get the configuration of the simulation group

      Parameters
      ----------
      include_N: :obj:`bool`, optional
          include N in the output config
          is ignored when for_reinit is True
      for_reinit: :obj:`bool`, optional
          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`, optional
          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 `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)
      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)
      If `ext_out` is True, additionally includes:
          - 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


   .. 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, time_steps, 10)


   .. 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`, optional
          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`, optional
          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`, optional
          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`, optional
          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



.. py:class:: KuramotoSimGroup(*args, random_init_theta=True, **kwargs)


   Bases: :py:obj:`SimGroup`

   Group of Kuramoto simulations that are executed in parallel

   Parameters
   ---------
   *args, **kwargs:
       see :class:`cubnm.sim.SimGroup` for details
   random_init_theta : :obj:`bool`, optional
       Set initial theta by randomly sampling from a uniform distribution 
       [0, 2*pi].

   Attributes
   ----------
   param_lists: :obj:`dict` of :obj:`np.ndarray`
       dictionary of parameter lists, including
           - 'G': global coupling. Shape: (N_SIMS,)
           - 'init_theta': initial theta. Randomly sampled from a uniform distribution 
               [0, 2*pi] by default. Shape: (N_SIMS, nodes)
           - 'omega': intrinsic frequency. Shape: (N_SIMS, nodes)
           - 'sigma': local noise sigma. Shape: (N_SIMS, nodes)
           - 'v': conduction velocity. Shape: (N_SIMS,)

   Example
   -------
   To see example usage in grid search and evolutionary algorithms
   see :mod:`cubnm.optimize`.

   Here, as an example on how to use SimGroup independently, we
   will run a single simulation and save the outputs to disk. ::

       from cubnm import sim, datasets

       sim_group = sim.KuramotoSimGroup(
           duration=60,
           TR=1,
           sc=datasets.load_sc('strength', 'schaefer-100'),
       )
       sim_group.N = 1
       sim_group.param_lists['G'] = np.repeat(0.5, N_SIMS)
       sim_group.param_lists['omega'] = np.full((N_SIMS, nodes), np.pi)
       sim_group.param_lists['sigma'] = np.full((N_SIMS, nodes), 0.17)
       sim_group.run()

   .. py:attribute:: model_name
      :value: 'Kuramoto'

      

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

      

   .. py:attribute:: regional_param_names
      :value: ['init_theta', 'omega', 'sigma']

      

   .. py:attribute:: state_names
      :value: ['theta']

      

   .. py:attribute:: sel_state_var
      :value: 'theta'

      

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

      

   .. py:method:: _set_default_params()

      Set default parameters for the simulations.
      This is used in tests.


   .. py:method:: _check_dt()

      Check if integrations steps are valid


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

      Get the configuration of the simulation group

      Parameters
      ----------
      include_N: :obj:`bool`, optional
          include N in the output config
          is ignored when for_reinit is True
      for_reinit: :obj:`bool`, optional
          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`, optional
          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 `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)
      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)
      If `ext_out` is True, additionally includes:
          - 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


   .. 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, time_steps, 10)


   .. 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`, optional
          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`, optional
          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`, optional
          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`, optional
          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



.. py:class:: MultiSimGroupMixin(sim_groups)


   Mixin for combining multiple simulation groups into one,
   which is intended for batch optimization, i.e. running multiple
   optimizations at the same time on GPU.

   .. py:method:: run(**kwargs)

      Runs merged simulations of all children
      by concatenating SCs and parameters
      and then running all simulations in parallel
      as a single merged simulation group.

      Parameters
      ----------
      **kwargs
          keyword arguments to be passed to `cubnm.sim.SimGroup.run` method


   .. py:method:: _process_out(out)

      Divides simulations outputs across individual
      children SimGroup objects which will respectively
      convert the output to attributes with correct shapes,
      names and types. See `cubnm.sim.SimGroup._process_out`
      for details.

      Parameters
      ----------
      out: :obj:`tuple`
          output of `run_simulations` function



.. py:function:: create_multi_sim_group(sim_group_cls)

   Dynamically creates a MultiSimGroup class by combining
   a model's specific <Model>SimGroup class with MultiSimGroupMixin,
   which can be used in batch optimization.

   Parameters
   ----------
   sim_group_cls: :obj:`type`
       e.g. rWWSimGroup, rWWExSimGroup, KuramotoSimGroup

   Returns
   -------
   MultiSimGroup: :obj:`type`
       MultiSimGroup class that can be used in batch optimization


