{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n\nUser-Defined Sampler\n====================\n\nThanks to user-defined samplers, you can:\n\n- experiment your own sampling algorithms,\n- implement task-specific algorithms to refine the optimization performance, or\n- wrap other optimization libraries to integrate them into Optuna pipelines (e.g., :class:`~optuna.integration.SkoptSampler`).\n\nThis section describes the internal behavior of sampler classes and shows an example of implementing a user-defined sampler.\n\n\nOverview of Sampler\n-------------------\n\nA sampler has the responsibility to determine the parameter values to be evaluated in a trial.\nWhen a `suggest` API (e.g., :func:`~optuna.trial.Trial.suggest_uniform`) is called inside an objective function, the corresponding distribution object (e.g., :class:`~optuna.distributions.UniformDistribution`) is created internally. A sampler samples a parameter value from the distribution. The sampled value is returned to the caller of the `suggest` API and evaluated in the objective function.\n\nTo create a new sampler, you need to define a class that inherits :class:`~optuna.samplers.BaseSampler`.\nThe base class has three abstract methods;\n:meth:`~optuna.samplers.BaseSampler.infer_relative_search_space`,\n:meth:`~optuna.samplers.BaseSampler.sample_relative`, and\n:meth:`~optuna.samplers.BaseSampler.sample_independent`.\n\nAs the method names imply, Optuna supports two types of sampling: one is **relative sampling** that can consider the correlation of the parameters in a trial, and the other is **independent sampling** that samples each parameter independently.\n\nAt the beginning of a trial, :meth:`~optuna.samplers.BaseSampler.infer_relative_search_space` is called to provide the relative search space for the trial. Then, :meth:`~optuna.samplers.BaseSampler.sample_relative` is invoked to sample relative parameters from the search space. During the execution of the objective function, :meth:`~optuna.samplers.BaseSampler.sample_independent` is used to sample parameters that don't belong to the relative search space.\n\n<div class=\"alert alert-info\"><h4>Note</h4><p>Please refer to the document of :class:`~optuna.samplers.BaseSampler` for further details.</p></div>\n\n\nAn Example: Implementing SimulatedAnnealingSampler\n--------------------------------------------------\n\nFor example, the following code defines a sampler based on\n`Simulated Annealing (SA) <https://en.wikipedia.org/wiki/Simulated_annealing>`_:\n\n.. code-block:: python\n\n    import numpy as np\n    import optuna\n\n\n    class SimulatedAnnealingSampler(optuna.samplers.BaseSampler):\n        def __init__(self, temperature=100):\n            self._rng = np.random.RandomState()\n            self._temperature = temperature  # Current temperature.\n            self._current_trial = None  # Current state.\n\n        def sample_relative(self, study, trial, search_space):\n            if search_space == {}:\n                return {}\n\n            #\n            # An implementation of SA algorithm.\n            #\n\n            # Calculate transition probability.\n            prev_trial = study.trials[-2]\n            if self._current_trial is None or prev_trial.value <= self._current_trial.value:\n                probability = 1.0\n            else:\n                probability = np.exp((self._current_trial.value - prev_trial.value) / self._temperature)\n            self._temperature *= 0.9  # Decrease temperature.\n\n            # Transit the current state if the previous result is accepted.\n            if self._rng.uniform(0, 1) < probability:\n                self._current_trial = prev_trial\n\n            # Sample parameters from the neighborhood of the current point.\n            #\n            # The sampled parameters will be used during the next execution of\n            # the objective function passed to the study.\n            params = {}\n            for param_name, param_distribution in search_space.items():\n                if not isinstance(param_distribution, optuna.distributions.UniformDistribution):\n                    raise NotImplementedError('Only suggest_uniform() is supported')\n\n                current_value = self._current_trial.params[param_name]\n                width = (param_distribution.high - param_distribution.low) * 0.1\n                neighbor_low = max(current_value - width, param_distribution.low)\n                neighbor_high = min(current_value + width, param_distribution.high)\n                params[param_name] = self._rng.uniform(neighbor_low, neighbor_high)\n\n            return params\n\n        #\n        # The rest is boilerplate code and unrelated to SA algorithm.\n        #\n        def infer_relative_search_space(self, study, trial):\n            return optuna.samplers.intersection_search_space(study)\n\n        def sample_independent(self, study, trial, param_name, param_distribution):\n            independent_sampler = optuna.samplers.RandomSampler()\n            return independent_sampler.sample_independent(study, trial, param_name, param_distribution)\n\n\n<div class=\"alert alert-info\"><h4>Note</h4><p>In favor of code simplicity, the above implementation doesn't support some features (e.g., maximization).\n   If you're interested in how to support those features, please see\n   `examples/samplers/simulated_annealing.py\n   <https://github.com/optuna/optuna/blob/master/examples/samplers/simulated_annealing_sampler.py>`_.</p></div>\n\n\nYou can use ``SimulatedAnnealingSampler`` in the same way as built-in samplers as follows:\n\n.. code-block:: python\n\n    def objective(trial):\n        x = trial.suggest_uniform('x', -10, 10)\n        y = trial.suggest_uniform('y', -5, 5)\n        return x**2 + y\n\n    sampler = SimulatedAnnealingSampler()\n    study = optuna.create_study(sampler=sampler)\n    study.optimize(objective, n_trials=100)\n\n\nIn this optimization, the values of ``x`` and ``y`` parameters are sampled by using\n``SimulatedAnnealingSampler.sample_relative`` method.\n\n<div class=\"alert alert-info\"><h4>Note</h4><p>Strictly speaking, in the first trial,\n    ``SimulatedAnnealingSampler.sample_independent`` method is used to sample parameter values.\n    Because :func:`~optuna.samplers.intersection_search_space` used in\n    ``SimulatedAnnealingSampler.infer_relative_search_space`` cannot infer the search space\n    if there are no complete trials.</p></div>\n"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.7.7"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}