:py:mod:`cubnm.optimize`
#######################

.. py:module:: cubnm.optimize

.. autoapi-nested-parse::

   Optimizers of the model free parameters



.. autoapisummary::

   cubnm.optimize.BNMProblem
   cubnm.optimize.Optimizer
   cubnm.optimize.GridOptimizer
   cubnm.optimize.PymooOptimizer
   cubnm.optimize.CMAESOptimizer
   cubnm.optimize.NSGA2Optimizer



.. autoapisummary::

   cubnm.optimize.batch_optimize



.. py:class:: BNMProblem(model, params, emp_fc_tril=None, emp_fcd_tril=None, emp_bold=None, het_params=[], het_params_range='same', maps=None, maps_coef_range='auto', node_grouping=None, multiobj=False, **kwargs)


   Bases: :py:obj:`pymoo.core.problem.Problem`

   Brain network model problem. A :class:`pymoo.core.problem.Problem` 
   that defines the model, free parameters and their ranges, and target empirical
   data (FC and FCD), and the simulation configurations (through 
   :class:`cubnm.sim.base.SimGroup`). 
   :class:`cubnm.optimize.Optimizer` classes can be
   used to optimize the free parameters of this problem.

   Parameters
   ----------
   model: :obj:`str`, {'rWW', 'rWWEx', 'Kuramoto'}
       model name
   params: :obj:`dict` of :obj:`tuple` or :obj:`float`
       a dictionary including parameter names as keys and their
       fixed values (:obj:`float`) or continuous range of
       values (:obj:`tuple` of (min, max)) as values.
   emp_fc_tril: :obj:`np.ndarray` or :obj:`None`
       lower triangular part of empirical FC. Shape: (edges,)
   emp_fcd_tril: :obj:`np.ndarray` or :obj:`None`
       lower triangular part of empirical FCD. Shape: (window_pairs,)
   emp_bold: :obj:`np.ndarray` or None
       cleaned and parcellated empirical BOLD time series. Shape: (nodes, volumes)
       Motion outliers can 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.
   het_params: :obj:`list` of :obj:`str`
       which regional parameters are heterogeneous across nodes
   maps: :obj:`str`
       path to heterogeneity maps as a text file or a numpy array.
       Shape: (n_maps, nodes).
       If provided one free parameter per regional parameter per 
       each map will be added.
   maps_coef_range: 'auto' or :obj:`tuple` or :obj:`list` of :obj:`tuple`
       Range of coefficients for the maps in map-based heterogeneity 
       (i.e., when ``maps`` is provided).

       - ``'auto'``: uses (-1/max, -1/min) for maps with positive and negative
         values (assuming they are z-scored) and (0, 1) otherwise
       - :obj:`tuple`: uses the same range for all maps
       - :obj:`list` of :obj:`tuple`: n-map element list specifying the range
         of coefficients for each map

   het_params_range: 'same' or :obj:`dict` of :obj:`tuple` or None
       Forced range of regional parameters (after map-based
       heterogeneity is applied). The regional parameter values
       across nodes will be normalized into this range (if out
       of range).

       - ``'same'``: uses the same range as provided in `params`.
       - :obj:`dict` of :obj:`tuple`: uses the specified range
         for each regional parameter. The keys must be the all
         ``het_params`` and the values must be tuples of (min, max).
       - ``None``: does not normalize the regional parameters.
         This may lead to infeasible parameters, e.g. negative
         values, with some combinations of maps and map coeficients.

   node_grouping: {None, 'node', 'sym', :obj:`str`, :obj:`np.ndarray`}
       Defines groups of nodes which have the same regional
       parameters.

       - ``None``: does not use region-/group-specific parameters
       - ``'node'``: each node has its own regional free parameters
       - ``'sym'``: uses the same regional free parameters for each pair of symmetric nodes
         (e.g. L and R hemispheres). Assumes symmetry  of parcels between L and R
         hemispheres.
       - :obj:`str`: path to a text file including node grouping array. Shape: (nodes,)
       - :obj:`np.ndarray`: a numpy array. Shape: (nodes,)

   multiobj: :obj:`bool`
       instead of combining the objectives into a single objective function
       (via summation) defines each objective separately. This must not be used
       with single-objective optimizers
   **kwargs
       Keyword arguments passed to :class:`cubnm.sim.base.SimGroup`

   .. py:method:: get_config(include_sim_group=True, include_N=False)

      Get the problem configuration

      Parameters
      ----------
      include_sim_group: :obj:`bool`
          whether to include the configuration of the
          associated :class:`cubnm.sim.base.SimGroup`
      include_N: :obj:`bool`
          whether to include the current population size
          in the configuration

      Returns
      -------
      :obj:`dict`
          the configuration of the problem


   .. py:method:: _get_Xt(X)

      Transforms normalized parameters in range [0, 1] to
      the actual parameter ranges

      Parameters
      ----------
      X: :obj:`np.ndarray`
          the normalized parameters of current population. 
          Shape: (N, ndim)

      Returns
      -------
      :obj:`np.ndarray`
          the transformed parameters of current population. 
          Shape: (N, ndim)


   .. py:method:: _get_X(Xt)

      Normalizes parameters to range [0, 1]

      Parameters
      ----------
      Xt: :obj:`np.ndarray`
          the parameters of current population. 
          Shape: (N, ndim)

      Returns
      -------
      :obj:`np.ndarray`
          the normalized parameters current population. 
          Shape: (N, ndim)


   .. py:method:: _get_sim_params(X)

      Gets the global and regional parameters of the problem's 
      :class:`cubnm.sim.base.SimGroup` based on the
      problem free and fixed parameters and type of regional parameter
      heterogeneity (map-based, group-based or none).

      Parameters
      ----------
      X: :obj:`np.ndarray`
          the normalized parameters of current population in range [0, 1]. 
          Shape: (N, ndim)
      Returns
      -------
      :obj:`dict` of :obj:`np.ndarray`
          keys correspond to model parameters


   .. py:method:: _set_sim_params(X)

      Sets the global and regional parameters of the problem's 
      :class:`cubnm.sim.base.SimGroup` based on the
      problem free and fixed parameters and type of regional parameter
      heterogeneity (map-based, group-based or none).

      Parameters
      ----------
      X: :obj:`np.ndarray`
          the normalized parameters of current population in range [0, 1]. 
          Shape: (N, ndim)


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

      Ovewrites the :meth:`pymoo.core.problem.Problem._evaluate` method
      to evaluate the goodness of fit of the simulations based on parameters
      `X` and store the results in `out`.

      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
          additional arguments passed to the evaluation function,
          which may include:

          - scores: :obj:`list`
              an empty list passed on to evaluation function to store
              the individual goodness of fit measures
          - skip_run: :obj:`bool`
              will only be true in batch optimization where the simulations
              are already run and only the GOF calculation is needed


   .. py:method:: eval(X, skip_run=False)

      Runs the simulations based on normalized candidate free
      parameters `X` and evaluates their goodness of fit to
      the empirical FC and FCD of the problem.

      Parameters
      ----------
      X: :obj:`np.ndarray`
          the normalized parameters of current population in range [0, 1]. 
          Shape: (N, ndim)
      skip_run: :obj:`bool`
          will only be true in batch optimization where the simulations
          are already run and only the GOF calculation is needed


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



.. py:class:: Optimizer(**kwargs)


   Bases: :py:obj:`abc.ABC`

   Base class for evolutionary optimizers

   .. py:method:: optimize()
      :abstractmethod:


   .. py:method:: save(save_opt=True, save_obj=False)

      Saves the output of the optimizer, including history
      of particles, history of optima, the optimal point,
      and its simulation data. The output will be saved
      to `out_dir` of the problem's :class:`cubnm.sim.base.SimGroup`.
      If a directory with the same type of optimizer already
      exists, a new directory with a new index will be created.

      Parameters
      ---------
      save_opt: :obj:`bool`
          reruns and saves the optimal simulation(s) data
      save_obj: :obj:`bool`
          saves the optimizer object which also includes the simulation
          data of all simulations and therefore can be large file.
          Warning: this file is very large.


   .. py:method:: get_config()

      Get the optimizer configuration

      Returns
      -------
      :obj:`dict`
          the configuration of the optimizer


   .. py:method:: _plot_space_3d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 3D parameter space colored by `measure`


   .. py:method:: _plot_space_2d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 2D parameter space colored by `measure`


   .. py:method:: _plot_space_1d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 1D parameter space colored by `measure`


   .. py:method:: plot_space(measure=None, title='default', opt=False, gen=None, config={}, ax=None)

      Plot parameter space colored by ``measure``. Can only be used
      for 1D, 2D and 3D parameter spaces.

      Parameters
      ----------
      measure: :obj:`str` or :obj:`None`
          the measure to color the points by. If None, the points
          will be colored by the ``config['color']`` parameter and
          have the same color
      title: :obj:`str`
          the title of the plot. If 'default', the title will be
          set to the measure clean label. If None, no title will be set.
      opt: :obj:`bool`
          whether to mark the optimum in the plot. Will
          be ignored if ``gen`` is provided.
      gen: :obj:`int` or :obj:`None`
          the generation to plot. If None, the entire history
          will be plotted. When using a :class:`cubnm.optimize.GridOptimizer`
          this is ignored.
      config: :obj:`dict`
          plotting configuration. The following keys are available
          with default values:
          
          - figsize: :obj:`tuple`, (4.5, 4.5) for 2D and 3D plots, None for 1D
              figure size. Ignored if ``ax`` is provided
          - size: :obj:`float`, 30
              size of the sphere
          - alpha: :obj:`float`, 0.2 
              transparency of the sphere
          - color: :obj:`str`, 'red'
              color of the points if measure is None or space is 1D
          - opt_facecolor: :obj:`str`, 'none'
              face color of the optimum point
          - opt_edgecolor: :obj:`str`, 'black'
              edge color of the optimum point
          - full_lim: :obj:`bool`, True
              whether to set the axes limits to the full range
              of the parameters defined by the problem

          Specific to 2D and 3D plots:
          
          - cmap: :obj:`str`, 'viridis'
              colormap to use for the measure
          - vmin: :obj:`float`, None
              minimum value of the color range
          - vmax: :obj:`float`, None
              maximum value of the color range

          Specific to 1D and 2D plots:

          - marker: :obj:`str`, 'o'
          
          Specific to 3D plots:

          - elev: :obj:`float`, 15
              elevation angle in degrees
          - azim: :obj:`float`, -15
              azimuth angle in degrees
          - roll: :obj:`float` or :obj:`None`, None
              roll angle in degrees
          - aspects: :obj:`tuple`, (1, 1, 1)
              aspect ratio of the axes
          - zoom: :obj:`float`, 0.85
              zoom level of the plot

      ax: :obj:`matplotlib.axes.Axes`
          the axes to plot on. For 3D must have
          ``projection='3d'``.
          If None, a new figure and axes will be created. 

      Returns
      -------
      :obj:`tuple`
          the axes of the plot


   .. py:method:: plot_history(measure, legend=True, ax=None, line_kws={}, scatter_kws={})

      Plot the history of ``measure`` across the optimization generations.

      Parameters
      ----------
      measure: :obj:`str`
          the measure to plot
      legend: :obj:`bool`
          whether to show the legend
      ax: :obj:`matplotlib.axes.Axes`
          the axes to plot on. If None, a new figure and axes will be created.
      line_kws: :obj:`dict`
          additional keyword arguments passed to the line plot
          of the median across generations
      scatter_kws: :obj:`dict`
          additional keyword arguments passed to the scatter plot
          of the individual particles across generations



.. py:class:: GridOptimizer(**kwargs)


   Bases: :py:obj:`Optimizer`

   Grid search optimizer

   Example
   -------
   Run a grid search of rWW model with 100 simulations running
   a 4 by 5 by 5 grid search of parameters G, w_p and J_N: ::

       from cubnm import datasets, optimize

       problem = optimize.BNMProblem(
           model = 'rWW',
           params = {
               'G': (0.001, 10.0),
               'w_p': (0, 2.0),
               'J_N': (0.001, 0.5),
           },
           duration = 60,
           TR = 1,
           window_size=10,
           window_step=2,
           sc = datasets.load_sc('strength', 'schaefer-100'),
           emp_bold = datasets.load_bold('schaefer-100'),
       )
       go = optimize.GridOptimizer()
       go.optimize(problem, grid_shape={'G': 4, 'w_p': 5, 'J_N': 5})
       go.save()

   .. py:method:: optimize(problem, grid_shape)

      Runs a grid search optimization on the given problem.

      Parameters
      ----------
      problem: :obj:`cubnm.optimizer.BNMProblem`
          The problem to be set up with the algorithm.

      grid_shape: :obj:`int` or :obj:`dict`
          Shape of the grid search. If an integer is provided
          the same number of points are used for each parameter. 
          If a dictionary is provided, the keys should be the 
          parameter names and the values should be the number of points 
          within the range of each parameter.


   .. py:method:: save(save_avg_states=True, **kwargs)

      Saves the output of the optimizer, including history
      of particles, history of optima, the optimal point,
      and its simulation data. The output will be saved
      to `out_dir` of the problem's :class:`cubnm.sim.base.SimGroup`.

      Parameters
      ---------
      save_avg_states: :obj:`bool`
          saves the average states of all simulations
      **kwargs
          additional keyword arguments passed to the :meth:`cubnm.optimizer.Optimizer.save`


   .. py:method:: get_config()

      Get the optimizer configuration

      Returns
      -------
      :obj:`dict`
          the configuration of the optimizer


   .. py:method:: _plot_space_3d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 3D parameter space colored by `measure`


   .. py:method:: _plot_space_2d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 2D parameter space colored by `measure`


   .. py:method:: _plot_space_1d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 1D parameter space colored by `measure`


   .. py:method:: plot_space(measure=None, title='default', opt=False, gen=None, config={}, ax=None)

      Plot parameter space colored by ``measure``. Can only be used
      for 1D, 2D and 3D parameter spaces.

      Parameters
      ----------
      measure: :obj:`str` or :obj:`None`
          the measure to color the points by. If None, the points
          will be colored by the ``config['color']`` parameter and
          have the same color
      title: :obj:`str`
          the title of the plot. If 'default', the title will be
          set to the measure clean label. If None, no title will be set.
      opt: :obj:`bool`
          whether to mark the optimum in the plot. Will
          be ignored if ``gen`` is provided.
      gen: :obj:`int` or :obj:`None`
          the generation to plot. If None, the entire history
          will be plotted. When using a :class:`cubnm.optimize.GridOptimizer`
          this is ignored.
      config: :obj:`dict`
          plotting configuration. The following keys are available
          with default values:
          
          - figsize: :obj:`tuple`, (4.5, 4.5) for 2D and 3D plots, None for 1D
              figure size. Ignored if ``ax`` is provided
          - size: :obj:`float`, 30
              size of the sphere
          - alpha: :obj:`float`, 0.2 
              transparency of the sphere
          - color: :obj:`str`, 'red'
              color of the points if measure is None or space is 1D
          - opt_facecolor: :obj:`str`, 'none'
              face color of the optimum point
          - opt_edgecolor: :obj:`str`, 'black'
              edge color of the optimum point
          - full_lim: :obj:`bool`, True
              whether to set the axes limits to the full range
              of the parameters defined by the problem

          Specific to 2D and 3D plots:
          
          - cmap: :obj:`str`, 'viridis'
              colormap to use for the measure
          - vmin: :obj:`float`, None
              minimum value of the color range
          - vmax: :obj:`float`, None
              maximum value of the color range

          Specific to 1D and 2D plots:

          - marker: :obj:`str`, 'o'
          
          Specific to 3D plots:

          - elev: :obj:`float`, 15
              elevation angle in degrees
          - azim: :obj:`float`, -15
              azimuth angle in degrees
          - roll: :obj:`float` or :obj:`None`, None
              roll angle in degrees
          - aspects: :obj:`tuple`, (1, 1, 1)
              aspect ratio of the axes
          - zoom: :obj:`float`, 0.85
              zoom level of the plot

      ax: :obj:`matplotlib.axes.Axes`
          the axes to plot on. For 3D must have
          ``projection='3d'``.
          If None, a new figure and axes will be created. 

      Returns
      -------
      :obj:`tuple`
          the axes of the plot


   .. py:method:: plot_history(measure, legend=True, ax=None, line_kws={}, scatter_kws={})

      Plot the history of ``measure`` across the optimization generations.

      Parameters
      ----------
      measure: :obj:`str`
          the measure to plot
      legend: :obj:`bool`
          whether to show the legend
      ax: :obj:`matplotlib.axes.Axes`
          the axes to plot on. If None, a new figure and axes will be created.
      line_kws: :obj:`dict`
          additional keyword arguments passed to the line plot
          of the median across generations
      scatter_kws: :obj:`dict`
          additional keyword arguments passed to the scatter plot
          of the individual particles across generations



.. py:class:: PymooOptimizer(termination=None, n_iter=2, seed=0, print_history=True, save_history_sim=False, **kwargs)


   Bases: :py:obj:`Optimizer`

   Generic wrapper for `pymoo` optimizers.

   Parameters:
   ----------
   termination: :obj:`pymoo.termination.Termination`
       The termination object that defines the stopping criteria for 
       the optimization process.
       If not provided, the termination criteria will be based on the 
       number of iterations (`n_iter`).
   n_iter: :obj:`int`
       The maximum number of iterations for the optimization process.
       This parameter is only used if `termination` is not provided.
   seed: :obj:`int`
       The seed value for the random number generator used by the optimizer.
   print_history: :obj:`bool`
       Flag indicating whether to print the optimization history during the 
       optimization process.
   save_history_sim: :obj:`bool`
       Flag indicating whether to save the simulation data of each iteration.
       Default is False to avoid consuming too much memory across iterations.
   **kwargs
       Additional keyword arguments that can be passed to the `pymoo` optimizer.        

   .. py:method:: setup_problem(problem, pymoo_verbose=False, **kwargs)

      Registers a :class:`cubnm.optimizer.BNMProblem` 
      with the optimizer, so that the optimizer can optimize
      its free parameters.

      Parameters
      ----------
      problem: :obj:`cubnm.optimizer.BNMProblem`
          The problem to be set up with the algorithm.
      pymoo_verbose: :obj:`bool`
          Flag indicating whether to enable verbose output from pymoo. Default is False.
      **kwargs
          Additional keyword arguments to be passed to the algorithm setup method.


   .. py:method:: optimize()

      Optimizes the associated :class:`cubnm.optimizer.BNMProblem`
      free parameters through an evolutionary optimization approach by
      running multiple generations of parallel simulations until the
      termination criteria is met or maximum number of iterations is reached.


   .. py:method:: save(save_opt=True, save_obj=False)

      Saves the output of the optimizer, including history
      of particles, history of optima, the optimal point,
      and its simulation data. The output will be saved
      to `out_dir` of the problem's :class:`cubnm.sim.base.SimGroup`.
      If a directory with the same type of optimizer already
      exists, a new directory with a new index will be created.

      Parameters
      ---------
      save_opt: :obj:`bool`
          reruns and saves the optimal simulation(s) data
      save_obj: :obj:`bool`
          saves the optimizer object which also includes the simulation
          data of all simulations and therefore can be large file.
          Warning: this file is very large.


   .. py:method:: get_config()

      Get the optimizer configuration

      Returns
      -------
      :obj:`dict`
          the configuration of the optimizer


   .. py:method:: _plot_space_3d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 3D parameter space colored by `measure`


   .. py:method:: _plot_space_2d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 2D parameter space colored by `measure`


   .. py:method:: _plot_space_1d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 1D parameter space colored by `measure`


   .. py:method:: plot_space(measure=None, title='default', opt=False, gen=None, config={}, ax=None)

      Plot parameter space colored by ``measure``. Can only be used
      for 1D, 2D and 3D parameter spaces.

      Parameters
      ----------
      measure: :obj:`str` or :obj:`None`
          the measure to color the points by. If None, the points
          will be colored by the ``config['color']`` parameter and
          have the same color
      title: :obj:`str`
          the title of the plot. If 'default', the title will be
          set to the measure clean label. If None, no title will be set.
      opt: :obj:`bool`
          whether to mark the optimum in the plot. Will
          be ignored if ``gen`` is provided.
      gen: :obj:`int` or :obj:`None`
          the generation to plot. If None, the entire history
          will be plotted. When using a :class:`cubnm.optimize.GridOptimizer`
          this is ignored.
      config: :obj:`dict`
          plotting configuration. The following keys are available
          with default values:
          
          - figsize: :obj:`tuple`, (4.5, 4.5) for 2D and 3D plots, None for 1D
              figure size. Ignored if ``ax`` is provided
          - size: :obj:`float`, 30
              size of the sphere
          - alpha: :obj:`float`, 0.2 
              transparency of the sphere
          - color: :obj:`str`, 'red'
              color of the points if measure is None or space is 1D
          - opt_facecolor: :obj:`str`, 'none'
              face color of the optimum point
          - opt_edgecolor: :obj:`str`, 'black'
              edge color of the optimum point
          - full_lim: :obj:`bool`, True
              whether to set the axes limits to the full range
              of the parameters defined by the problem

          Specific to 2D and 3D plots:
          
          - cmap: :obj:`str`, 'viridis'
              colormap to use for the measure
          - vmin: :obj:`float`, None
              minimum value of the color range
          - vmax: :obj:`float`, None
              maximum value of the color range

          Specific to 1D and 2D plots:

          - marker: :obj:`str`, 'o'
          
          Specific to 3D plots:

          - elev: :obj:`float`, 15
              elevation angle in degrees
          - azim: :obj:`float`, -15
              azimuth angle in degrees
          - roll: :obj:`float` or :obj:`None`, None
              roll angle in degrees
          - aspects: :obj:`tuple`, (1, 1, 1)
              aspect ratio of the axes
          - zoom: :obj:`float`, 0.85
              zoom level of the plot

      ax: :obj:`matplotlib.axes.Axes`
          the axes to plot on. For 3D must have
          ``projection='3d'``.
          If None, a new figure and axes will be created. 

      Returns
      -------
      :obj:`tuple`
          the axes of the plot


   .. py:method:: plot_history(measure, legend=True, ax=None, line_kws={}, scatter_kws={})

      Plot the history of ``measure`` across the optimization generations.

      Parameters
      ----------
      measure: :obj:`str`
          the measure to plot
      legend: :obj:`bool`
          whether to show the legend
      ax: :obj:`matplotlib.axes.Axes`
          the axes to plot on. If None, a new figure and axes will be created.
      line_kws: :obj:`dict`
          additional keyword arguments passed to the line plot
          of the median across generations
      scatter_kws: :obj:`dict`
          additional keyword arguments passed to the scatter plot
          of the individual particles across generations



.. py:class:: CMAESOptimizer(popsize, x0=None, sigma=0.5, use_bound_penalty=False, algorithm_kws={}, **kwargs)


   Bases: :py:obj:`PymooOptimizer`

   Covariance Matrix Adaptation Evolution Strategy (CMA-ES) optimizer

   Parameters
   ----------
   popsize: :obj:`int`
       The population size for the optimizer
   x0: array-like
       The initial guess for the optimization.
       If None (default), the initial guess will be estimated based on
       `popsize` random samples as the first generation
   sigma: :obj:`float`
       The initial step size for the optimization
   use_bound_penalty: :obj:`bool`
       Whether to use a bound penalty for the optimization
   algorithm_kws: :obj:`dict`
       Additional keyword arguments for the CMAES algorithm
   **kwargs
       Additional keyword arguments

   Example
   -------
   Run a CMAES optimization for 10 iterations with 
   a population size of 20 on a 3D model (rWW, homogeneous): ::

       from cubnm import datasets, optimize

       problem = optimize.BNMProblem(
           model = 'rWW',
           params = {
               'G': (0.001, 10.0),
               'w_p': (0, 2.0),
               'J_N': (0.001, 0.5),
           },
           emp_bold = datasets.load_bold('schaefer-100'),
           duration = 60,
           TR = 1,
           sc_path = datasets.load_sc('strength', 'schaefer-100'),
           states_ts = True
       )
       cmaes = optimize.CMAESOptimizer(popsize=20, n_iter=10, seed=1)
       cmaes.setup_problem(problem)
       cmaes.optimize()
       cmaes.save()

   .. py:attribute:: max_obj
      :value: 1

      

   .. py:method:: setup_problem(problem, **kwargs)

      Extends :meth:`cubnm.optimizer.PymooOptimizer.setup_problem` to
      set up the optimizer with the problem and set the bound penalty
      option based on the optimizer's `use_bound_penalty` attribute.

      Parameters
      ----------
      problem: :obj:`cubnm.optimizer.BNMProblem`
          The problem to be set up with the algorithm.
      **kwargs
          Additional keyword arguments to be passed to 
          :meth:`cubnm.optimizer.PymooOptimizer.setup_problem`


   .. py:method:: optimize()

      Optimizes the associated :class:`cubnm.optimizer.BNMProblem`
      free parameters through an evolutionary optimization approach by
      running multiple generations of parallel simulations until the
      termination criteria is met or maximum number of iterations is reached.


   .. py:method:: save(save_opt=True, save_obj=False)

      Saves the output of the optimizer, including history
      of particles, history of optima, the optimal point,
      and its simulation data. The output will be saved
      to `out_dir` of the problem's :class:`cubnm.sim.base.SimGroup`.
      If a directory with the same type of optimizer already
      exists, a new directory with a new index will be created.

      Parameters
      ---------
      save_opt: :obj:`bool`
          reruns and saves the optimal simulation(s) data
      save_obj: :obj:`bool`
          saves the optimizer object which also includes the simulation
          data of all simulations and therefore can be large file.
          Warning: this file is very large.


   .. py:method:: get_config()

      Get the optimizer configuration

      Returns
      -------
      :obj:`dict`
          the configuration of the optimizer


   .. py:method:: _plot_space_3d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 3D parameter space colored by `measure`


   .. py:method:: _plot_space_2d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 2D parameter space colored by `measure`


   .. py:method:: _plot_space_1d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 1D parameter space colored by `measure`


   .. py:method:: plot_space(measure=None, title='default', opt=False, gen=None, config={}, ax=None)

      Plot parameter space colored by ``measure``. Can only be used
      for 1D, 2D and 3D parameter spaces.

      Parameters
      ----------
      measure: :obj:`str` or :obj:`None`
          the measure to color the points by. If None, the points
          will be colored by the ``config['color']`` parameter and
          have the same color
      title: :obj:`str`
          the title of the plot. If 'default', the title will be
          set to the measure clean label. If None, no title will be set.
      opt: :obj:`bool`
          whether to mark the optimum in the plot. Will
          be ignored if ``gen`` is provided.
      gen: :obj:`int` or :obj:`None`
          the generation to plot. If None, the entire history
          will be plotted. When using a :class:`cubnm.optimize.GridOptimizer`
          this is ignored.
      config: :obj:`dict`
          plotting configuration. The following keys are available
          with default values:
          
          - figsize: :obj:`tuple`, (4.5, 4.5) for 2D and 3D plots, None for 1D
              figure size. Ignored if ``ax`` is provided
          - size: :obj:`float`, 30
              size of the sphere
          - alpha: :obj:`float`, 0.2 
              transparency of the sphere
          - color: :obj:`str`, 'red'
              color of the points if measure is None or space is 1D
          - opt_facecolor: :obj:`str`, 'none'
              face color of the optimum point
          - opt_edgecolor: :obj:`str`, 'black'
              edge color of the optimum point
          - full_lim: :obj:`bool`, True
              whether to set the axes limits to the full range
              of the parameters defined by the problem

          Specific to 2D and 3D plots:
          
          - cmap: :obj:`str`, 'viridis'
              colormap to use for the measure
          - vmin: :obj:`float`, None
              minimum value of the color range
          - vmax: :obj:`float`, None
              maximum value of the color range

          Specific to 1D and 2D plots:

          - marker: :obj:`str`, 'o'
          
          Specific to 3D plots:

          - elev: :obj:`float`, 15
              elevation angle in degrees
          - azim: :obj:`float`, -15
              azimuth angle in degrees
          - roll: :obj:`float` or :obj:`None`, None
              roll angle in degrees
          - aspects: :obj:`tuple`, (1, 1, 1)
              aspect ratio of the axes
          - zoom: :obj:`float`, 0.85
              zoom level of the plot

      ax: :obj:`matplotlib.axes.Axes`
          the axes to plot on. For 3D must have
          ``projection='3d'``.
          If None, a new figure and axes will be created. 

      Returns
      -------
      :obj:`tuple`
          the axes of the plot


   .. py:method:: plot_history(measure, legend=True, ax=None, line_kws={}, scatter_kws={})

      Plot the history of ``measure`` across the optimization generations.

      Parameters
      ----------
      measure: :obj:`str`
          the measure to plot
      legend: :obj:`bool`
          whether to show the legend
      ax: :obj:`matplotlib.axes.Axes`
          the axes to plot on. If None, a new figure and axes will be created.
      line_kws: :obj:`dict`
          additional keyword arguments passed to the line plot
          of the median across generations
      scatter_kws: :obj:`dict`
          additional keyword arguments passed to the scatter plot
          of the individual particles across generations



.. py:class:: NSGA2Optimizer(popsize, algorithm_kws={}, **kwargs)


   Bases: :py:obj:`PymooOptimizer`

   Non-dominated Sorting Genetic Algorithm II (NSGA-II) optimizer

   Parameters
   ----------
   popsize: int
       The population size for the optimizer
   algorithm_kws: dict
       Additional keyword arguments for the NSGA2 algorithm
   kwargs: dict
       Additional keyword arguments for the base class

   .. py:attribute:: max_obj
      :value: 3

      

   .. py:method:: setup_problem(problem, pymoo_verbose=False, **kwargs)

      Registers a :class:`cubnm.optimizer.BNMProblem` 
      with the optimizer, so that the optimizer can optimize
      its free parameters.

      Parameters
      ----------
      problem: :obj:`cubnm.optimizer.BNMProblem`
          The problem to be set up with the algorithm.
      pymoo_verbose: :obj:`bool`
          Flag indicating whether to enable verbose output from pymoo. Default is False.
      **kwargs
          Additional keyword arguments to be passed to the algorithm setup method.


   .. py:method:: optimize()

      Optimizes the associated :class:`cubnm.optimizer.BNMProblem`
      free parameters through an evolutionary optimization approach by
      running multiple generations of parallel simulations until the
      termination criteria is met or maximum number of iterations is reached.


   .. py:method:: save(save_opt=True, save_obj=False)

      Saves the output of the optimizer, including history
      of particles, history of optima, the optimal point,
      and its simulation data. The output will be saved
      to `out_dir` of the problem's :class:`cubnm.sim.base.SimGroup`.
      If a directory with the same type of optimizer already
      exists, a new directory with a new index will be created.

      Parameters
      ---------
      save_opt: :obj:`bool`
          reruns and saves the optimal simulation(s) data
      save_obj: :obj:`bool`
          saves the optimizer object which also includes the simulation
          data of all simulations and therefore can be large file.
          Warning: this file is very large.


   .. py:method:: get_config()

      Get the optimizer configuration

      Returns
      -------
      :obj:`dict`
          the configuration of the optimizer


   .. py:method:: _plot_space_3d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 3D parameter space colored by `measure`


   .. py:method:: _plot_space_2d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 2D parameter space colored by `measure`


   .. py:method:: _plot_space_1d(plot_df, measure, title='default', opt_params=None, config={}, ax=None)

      Plot 1D parameter space colored by `measure`


   .. py:method:: plot_space(measure=None, title='default', opt=False, gen=None, config={}, ax=None)

      Plot parameter space colored by ``measure``. Can only be used
      for 1D, 2D and 3D parameter spaces.

      Parameters
      ----------
      measure: :obj:`str` or :obj:`None`
          the measure to color the points by. If None, the points
          will be colored by the ``config['color']`` parameter and
          have the same color
      title: :obj:`str`
          the title of the plot. If 'default', the title will be
          set to the measure clean label. If None, no title will be set.
      opt: :obj:`bool`
          whether to mark the optimum in the plot. Will
          be ignored if ``gen`` is provided.
      gen: :obj:`int` or :obj:`None`
          the generation to plot. If None, the entire history
          will be plotted. When using a :class:`cubnm.optimize.GridOptimizer`
          this is ignored.
      config: :obj:`dict`
          plotting configuration. The following keys are available
          with default values:
          
          - figsize: :obj:`tuple`, (4.5, 4.5) for 2D and 3D plots, None for 1D
              figure size. Ignored if ``ax`` is provided
          - size: :obj:`float`, 30
              size of the sphere
          - alpha: :obj:`float`, 0.2 
              transparency of the sphere
          - color: :obj:`str`, 'red'
              color of the points if measure is None or space is 1D
          - opt_facecolor: :obj:`str`, 'none'
              face color of the optimum point
          - opt_edgecolor: :obj:`str`, 'black'
              edge color of the optimum point
          - full_lim: :obj:`bool`, True
              whether to set the axes limits to the full range
              of the parameters defined by the problem

          Specific to 2D and 3D plots:
          
          - cmap: :obj:`str`, 'viridis'
              colormap to use for the measure
          - vmin: :obj:`float`, None
              minimum value of the color range
          - vmax: :obj:`float`, None
              maximum value of the color range

          Specific to 1D and 2D plots:

          - marker: :obj:`str`, 'o'
          
          Specific to 3D plots:

          - elev: :obj:`float`, 15
              elevation angle in degrees
          - azim: :obj:`float`, -15
              azimuth angle in degrees
          - roll: :obj:`float` or :obj:`None`, None
              roll angle in degrees
          - aspects: :obj:`tuple`, (1, 1, 1)
              aspect ratio of the axes
          - zoom: :obj:`float`, 0.85
              zoom level of the plot

      ax: :obj:`matplotlib.axes.Axes`
          the axes to plot on. For 3D must have
          ``projection='3d'``.
          If None, a new figure and axes will be created. 

      Returns
      -------
      :obj:`tuple`
          the axes of the plot


   .. py:method:: plot_history(measure, legend=True, ax=None, line_kws={}, scatter_kws={})

      Plot the history of ``measure`` across the optimization generations.

      Parameters
      ----------
      measure: :obj:`str`
          the measure to plot
      legend: :obj:`bool`
          whether to show the legend
      ax: :obj:`matplotlib.axes.Axes`
          the axes to plot on. If None, a new figure and axes will be created.
      line_kws: :obj:`dict`
          additional keyword arguments passed to the line plot
          of the median across generations
      scatter_kws: :obj:`dict`
          additional keyword arguments passed to the scatter plot
          of the individual particles across generations



.. py:function:: batch_optimize(optimizers, problems, save=True, setup_kwargs={})

   Optimize a batch of optimizers in parallel (without requiring
   multiple CPU cores when using GPUs).

   Parameters
   ----------
   optimizers: :obj:`list` of :obj:`cubnm.optimize.PymooOptimizer` or :obj:`cubnm.optimize.PymooOptimizer`
       A (list of) optimizer instance(s) to be run in parallel.
       If not a list, the same optimizer will be used for all problems
       and problems must be a list.
   problems: :obj:`list` of :obj:`cubnm.optimize.BNMProblem` or :obj:`cubnm.optimize.BNMProblem`
       A (list of) problem instance(s) to be set up with the optimizers.
       Will be mapped one-to-one with the optimizers.
       If not a list, the same problem will be used in all optimizers
       and optimizers must be a list.
   save: :obj:`bool`
       save the optimizers and their results. This is more efficient
       than saving each optimizer separately as saving involves
       rerunning the optimal simulations, which is done in a batch in
       this function.
   setup_kwargs: :obj:`dict`
       kwargs passed on to :meth:`cubnm.optimize.PymooOptimizer.setup_problem`
       
   Returns
   -------
   optimizers: :obj:`list` of :obj:`cubnm.optimize.PymooOptimizer`
       A list of optimizer instances which have been run in parallel


   Example
   -------
   Run CMAES for two subjects (with different SC and functional data) in a batch: ::

       from cubnm import datasets, optimize

       # assuming sub1 and sub2 SC and BOLD are available as
       # numpy arrays `sc_sub1`, `sc_sub2`, `bold_sub1`, `bold_sub2`

       # shared problem configuration
       problem_kwargs = dict(
           model = 'rWW',
           params = {
               'G': (0.001, 10.0),
               'w_p': (0, 2.0),
               'J_N': (0.001, 0.5),
           },
           duration = 60,
           TR = 1,
       )
       # problem for subject 1
       p_sub1 = optimize.BNMProblem(
           sc = sc_sub1,
           emp_bold = bold_sub1,
           **problem_kwargs
       )
       # problem for subject 2
       p_sub2 = optimize.BNMProblem(
           sc = sc_sub2,
           emp_bold = bold_sub2,
           **problem_kwargs
       )
       # optimizer
       cmaes = optimize.CMAESOptimizer(popsize=20, n_iter=10, seed=1)
       # batch optimization
       optimizers = optimize.batch_optimize(cmaes, [p_sub1, p_sub2])
       # print optima
       print(optimizers[0].opt)
       print(optimizers[1].opt)


