def test_delete_study(storage_mode: str) -> None: with StorageSupplier(storage_mode) as storage: # Get storage object because delete_study does not accept None. storage = get_storage(storage=storage) assert storage is not None # Test deleting a non-existing study. with pytest.raises(KeyError): delete_study("invalid-study-name", storage) # Test deleting an existing study. study = create_study(storage=storage, load_if_exists=False) delete_study(study.study_name, storage) # Test failed to delete the study which is already deleted. with pytest.raises(KeyError): delete_study(study.study_name, storage)
def __init__( self, study_name: str, storage: Union[str, storages.BaseStorage], sampler: Optional["samplers.BaseSampler"] = None, pruner: Optional[pruners.BasePruner] = None, ) -> None: self.study_name = study_name storage = storages.get_storage(storage) study_id = storage.get_study_id_from_name(study_name) super().__init__(study_id, storage) self.sampler = sampler or samplers.TPESampler() self.pruner = pruner or pruners.MedianPruner() self._optimize_lock = threading.Lock() self._stop_flag = False
def __init__( self, study_name, # type: str storage, # type: Union[str, storages.BaseStorage] sampler=None, # type: samplers.BaseSampler pruner=None # type: pruners.BasePruner ): # type: (...) -> None self.study_name = study_name storage = storages.get_storage(storage) study_id = storage.get_study_id_from_name(study_name) super(Study, self).__init__(study_id, storage) self.sampler = sampler or samplers.TPESampler() self.pruner = pruner or pruners.MedianPruner() self._optimize_lock = threading.Lock()
def delete_study( study_name, # type: str storage, # type: Union[str, storages.BaseStorage] ): # type: (...) -> None """Delete a :class:`~optuna.study.Study` object. Args: study_name: Study's name. storage: Database URL such as ``sqlite:///example.db``. Please see also the documentation of :func:`~optuna.study.create_study` for further details. """ storage = storages.get_storage(storage) study_id = storage.get_study_id_from_name(study_name) storage.delete_study(study_id)
def test_get_trials_state_option(storage_mode: str) -> None: with StorageSupplier(storage_mode) as storage: storage = get_storage(storage=storage) study = create_study(storage=storage) def objective(trial: Trial) -> float: if trial.number == 0: return 0.0 # TrialState.COMPLETE. elif trial.number == 1: return 0.0 # TrialState.COMPLETE. elif trial.number == 2: raise TrialPruned # TrialState.PRUNED. else: assert False study.optimize(objective, n_trials=3) trials = study.get_trials(states=None) assert len(trials) == 3 trials = study.get_trials(states=(TrialState.COMPLETE, )) assert len(trials) == 2 assert all(t.state == TrialState.COMPLETE for t in trials) trials = study.get_trials(states=(TrialState.COMPLETE, TrialState.PRUNED)) assert len(trials) == 3 assert all(t.state in (TrialState.COMPLETE, TrialState.PRUNED) for t in trials) trials = study.get_trials(states=()) assert len(trials) == 0 other_states = [ s for s in list(TrialState) if s != TrialState.COMPLETE and s != TrialState.PRUNED ] for s in other_states: trials = study.get_trials(states=(s, )) assert len(trials) == 0
def get_all_study_summaries(storage): # type: (Union[str, storages.BaseStorage]) -> List[StudySummary] """Get all history of studies stored in a specified storage. Args: storage: Database URL such as ``sqlite:///example.db``. Please see also the documentation of :func:`~optuna.study.create_study` for further details. Returns: List of study history summarized as :class:`~optuna.study.StudySummary` objects. See also: :func:`optuna.get_all_study_summaries` is an alias of :func:`optuna.study.get_all_study_summaries`. """ storage = storages.get_storage(storage) return storage.get_all_study_summaries()
def delete_study( study_name, # type: str storage, # type: Union[str, storages.BaseStorage] ): # type: (...) -> None """Delete a :class:`~optuna.study.Study` object. Args: study_name: Study's name. storage: Database URL such as ``sqlite:///example.db``. Optuna internally uses `SQLAlchemy <https://www.sqlalchemy.org/>`_ to handle databases. Please refer to `SQLAlchemy's document <https://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls>`_ for further details. """ storage = storages.get_storage(storage) study_id = storage.get_study_id_from_name(study_name) storage.delete_study(study_id)
def __init__( self, study_name, # type: str storage, # type: Union[str, storages.BaseStorage] sampler=None, # type: samplers.BaseSampler pruner=None, # type: pruners.BasePruner force_garbage_collection=True, # type: bool ): # type: (...) -> None self.study_name = study_name storage = storages.get_storage(storage) study_id = storage.get_study_id_from_name(study_name) super(Study, self).__init__(study_id, storage) self.sampler = sampler or samplers.TPESampler() self.pruner = pruner or pruners.MedianPruner() self.logger = logging.get_logger(__name__) self._optimize_lock = threading.Lock() self.force_garbage_collection = force_garbage_collection
def test_get_trials(storage_mode: str) -> None: with StorageSupplier(storage_mode) as storage: storage = get_storage(storage=storage) study = create_study(storage=storage) study.optimize(lambda t: t.suggest_int("x", 1, 5), n_trials=5) with patch("copy.deepcopy", wraps=copy.deepcopy) as mock_object: trials0 = study.get_trials(deepcopy=False) assert mock_object.call_count == 0 assert len(trials0) == 5 trials1 = study.get_trials(deepcopy=True) assert mock_object.call_count > 0 assert trials0 == trials1 # `study.trials` is equivalent to `study.get_trials(deepcopy=True)`. old_count = mock_object.call_count trials2 = study.trials assert mock_object.call_count > old_count assert trials0 == trials2
def create_study( storage=None, # type: Union[None, str, storages.BaseStorage] sampler=None, # type: samplers.BaseSampler pruner=None, # type: pruners.BasePruner study_name=None, # type: Optional[str] direction='minimize', # type: str ): # type: (...) -> Study """Create a new :class:`~optuna.study.Study`. Args: storage: Database URL. If this argument is set to None, in-memory storage is used, and the :class:`~optuna.study.Study` will not be persistent. sampler: A sampler object that implements background algorithm for value suggestion. See also :class:`~optuna.samplers`. pruner: A pruner object that decides early stopping of unpromising trials. See also :class:`~optuna.pruners`. study_name: Study's name. If this argument is set to None, a unique name is generated automatically. direction: Direction of optimization. Set ``minimize`` for minimization and ``maximize`` for maximization. Note that ``maximize`` is currently unsupported. Returns: A :class:`~optuna.study.Study` object. """ storage = storages.get_storage(storage) study_name = storage.get_study_name_from_id(storage.create_new_study_id(study_name)) return Study(study_name=study_name, storage=storage, sampler=sampler, pruner=pruner, direction=direction)
def create_study( storage: Optional[Union[str, storages.BaseStorage]] = None, sampler: Optional["samplers.BaseSampler"] = None, pruner: Optional[pruners.BasePruner] = None, study_name: Optional[str] = None, direction: Optional[str] = None, load_if_exists: bool = False, *, directions: Optional[Sequence[str]] = None, ) -> Study: """Create a new :class:`~optuna.study.Study`. Example: .. testcode:: import optuna def objective(trial): x = trial.suggest_uniform("x", 0, 10) return x ** 2 study = optuna.create_study() study.optimize(objective, n_trials=3) Args: storage: Database URL. If this argument is set to None, in-memory storage is used, and the :class:`~optuna.study.Study` will not be persistent. .. note:: When a database URL is passed, Optuna internally uses `SQLAlchemy`_ to handle the database. Please refer to `SQLAlchemy's document`_ for further details. If you want to specify non-default options to `SQLAlchemy Engine`_, you can instantiate :class:`~optuna.storages.RDBStorage` with your desired options and pass it to the ``storage`` argument instead of a URL. .. _SQLAlchemy: https://www.sqlalchemy.org/ .. _SQLAlchemy's document: https://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls .. _SQLAlchemy Engine: https://docs.sqlalchemy.org/en/latest/core/engines.html sampler: A sampler object that implements background algorithm for value suggestion. If :obj:`None` is specified, :class:`~optuna.samplers.TPESampler` is used during single-objective optimization and :class:`~optuna.samplers.NSGAIISampler` during multi-objective optimization. See also :class:`~optuna.samplers`. pruner: A pruner object that decides early stopping of unpromising trials. If :obj:`None` is specified, :class:`~optuna.pruners.MedianPruner` is used as the default. See also :class:`~optuna.pruners`. study_name: Study's name. If this argument is set to None, a unique name is generated automatically. direction: Direction of optimization. Set ``minimize`` for minimization and ``maximize`` for maximization. .. note:: If none of `direction` and `directions` are specified, the direction of the study is set to "minimize". directions: A sequence of directions during multi-objective optimization. load_if_exists: Flag to control the behavior to handle a conflict of study names. In the case where a study named ``study_name`` already exists in the ``storage``, a :class:`~optuna.exceptions.DuplicatedStudyError` is raised if ``load_if_exists`` is set to :obj:`False`. Otherwise, the creation of the study is skipped, and the existing one is returned. Returns: A :class:`~optuna.study.Study` object. Raises: :exc:`ValueError`: If the length of ``directions`` is zero. Or, if ``direction`` is neither 'minimize' nor 'maximize' when it is a string. Or, if the element of ``directions`` is neither `minimize` nor `maximize`. Or, if both ``direction`` and ``directions`` are specified. See also: :func:`optuna.create_study` is an alias of :func:`optuna.study.create_study`. """ if direction is None and directions is None: directions = ["minimize"] elif direction is not None and directions is not None: raise ValueError("Specify only one of `direction` and `directions`.") elif direction is not None: directions = [direction] elif directions is not None: directions = list(directions) else: assert False if len(directions) < 1: raise ValueError("The number of objectives must be greater than 0.") elif any(d != "minimize" and d != "maximize" for d in directions): raise ValueError( "Please set either 'minimize' or 'maximize' to direction.") direction_objects = [ StudyDirection.MINIMIZE if d == "minimize" else StudyDirection.MAXIMIZE for d in directions ] storage = storages.get_storage(storage) try: study_id = storage.create_new_study(study_name) except exceptions.DuplicatedStudyError: if load_if_exists: assert study_name is not None _logger.info("Using an existing study with name '{}' instead of " "creating a new one.".format(study_name)) study_id = storage.get_study_id_from_name(study_name) else: raise if sampler is None and len(direction_objects) > 1: sampler = samplers.NSGAIISampler() study_name = storage.get_study_name_from_id(study_id) study = Study(study_name=study_name, storage=storage, sampler=sampler, pruner=pruner) study._storage.set_study_directions(study_id, direction_objects) return study
def create_study( storage=None, # type: Union[None, str, storages.BaseStorage] sampler=None, # type: samplers.BaseSampler pruner=None, # type: pruners.BasePruner study_name=None, # type: Optional[str] direction='minimize', # type: str load_if_exists=False, # type: bool force_garbage_collection=True, # type: bool ): # type: (...) -> Study """Create a new :class:`~optuna.study.Study`. Args: storage: Database URL. If this argument is set to None, in-memory storage is used, and the :class:`~optuna.study.Study` will not be persistent. sampler: A sampler object that implements background algorithm for value suggestion. If :obj:`None` is specified, :class:`~optuna.samplers.TPESampler` is used as the default. See also :class:`~optuna.samplers`. pruner: A pruner object that decides early stopping of unpromising trials. See also :class:`~optuna.pruners`. study_name: Study's name. If this argument is set to None, a unique name is generated automatically. direction: Direction of optimization. Set ``minimize`` for minimization and ``maximize`` for maximization. load_if_exists: Flag to control the behavior to handle a conflict of study names. In the case where a study named ``study_name`` already exists in the ``storage``, a :class:`~optuna.structs.DuplicatedStudyError` is raised if ``load_if_exists`` is set to :obj:`False`. Otherwise, the creation of the study is skipped, and the existing one is returned. force_garbage_collection: Flag to force gc.collect() for every trial. Returns: A :class:`~optuna.study.Study` object. """ storage = storages.get_storage(storage) try: study_id = storage.create_new_study(study_name) except structs.DuplicatedStudyError: if load_if_exists: assert study_name is not None logger = logging.get_logger(__name__) logger.info("Using an existing study with name '{}' instead of " "creating a new one.".format(study_name)) study_id = storage.get_study_id_from_name(study_name) else: raise study_name = storage.get_study_name_from_id(study_id) study = Study(study_name=study_name, storage=storage, sampler=sampler, pruner=pruner, force_garbage_collection=force_garbage_collection) if direction == 'minimize': _direction = structs.StudyDirection.MINIMIZE elif direction == 'maximize': _direction = structs.StudyDirection.MAXIMIZE else: raise ValueError( 'Please set either \'minimize\' or \'maximize\' to direction.') study._storage.set_study_direction(study_id, _direction) return study
def create_study( storage=None, # type: Union[None, str, storages.BaseStorage] sampler=None, # type: samplers.BaseSampler pruner=None, # type: pruners.BasePruner study_name=None, # type: Optional[str] direction="minimize", # type: str load_if_exists=False, # type: bool ): # type: (...) -> Study """Create a new :class:`~optuna.study.Study`. Args: storage: Database URL. If this argument is set to None, in-memory storage is used, and the :class:`~optuna.study.Study` will not be persistent. .. note:: When a database URL is passed, Optuna internally uses `SQLAlchemy`_ to handle the database. Please refer to `SQLAlchemy's document`_ for further details. If you want to specify non-default options to `SQLAlchemy Engine`_, you can instantiate :class:`~optuna.storages.RDBStorage` with your desired options and pass it to the ``storage`` argument instead of a URL. .. _SQLAlchemy: https://www.sqlalchemy.org/ .. _SQLAlchemy's document: https://docs.sqlalchemy.org/en/latest/core/engines.html#database-urls .. _SQLAlchemy Engine: https://docs.sqlalchemy.org/en/latest/core/engines.html sampler: A sampler object that implements background algorithm for value suggestion. If :obj:`None` is specified, :class:`~optuna.samplers.TPESampler` is used as the default. See also :class:`~optuna.samplers`. pruner: A pruner object that decides early stopping of unpromising trials. See also :class:`~optuna.pruners`. study_name: Study's name. If this argument is set to None, a unique name is generated automatically. direction: Direction of optimization. Set ``minimize`` for minimization and ``maximize`` for maximization. load_if_exists: Flag to control the behavior to handle a conflict of study names. In the case where a study named ``study_name`` already exists in the ``storage``, a :class:`~optuna.exceptions.DuplicatedStudyError` is raised if ``load_if_exists`` is set to :obj:`False`. Otherwise, the creation of the study is skipped, and the existing one is returned. Returns: A :class:`~optuna.study.Study` object. """ storage = storages.get_storage(storage) try: study_id = storage.create_new_study(study_name) except exceptions.DuplicatedStudyError: if load_if_exists: assert study_name is not None _logger.info("Using an existing study with name '{}' instead of " "creating a new one.".format(study_name)) study_id = storage.get_study_id_from_name(study_name) else: raise study_name = storage.get_study_name_from_id(study_id) study = Study(study_name=study_name, storage=storage, sampler=sampler, pruner=pruner) if direction == "minimize": _direction = structs.StudyDirection.MINIMIZE elif direction == "maximize": _direction = structs.StudyDirection.MAXIMIZE else: raise ValueError( "Please set either 'minimize' or 'maximize' to direction.") study._storage.set_study_direction(study_id, _direction) return study
def create_app(storage_or_url: Union[str, BaseStorage]) -> Bottle: app = Bottle() storage = get_storage(storage_or_url) @app.hook("before_request") def remove_trailing_slashes_hook() -> None: request.environ["PATH_INFO"] = request.environ["PATH_INFO"].rstrip("/") @app.get("/") def index() -> BottleViewReturn: return redirect("/dashboard", 302) # Status Found # Accept any following paths for client-side routing @app.get("/dashboard<:re:(/.*)?>") def dashboard() -> BottleViewReturn: response.content_type = "text/html" return INDEX_HTML @app.get("/api/studies") @handle_json_api_exception def list_study_summaries() -> BottleViewReturn: response.content_type = "application/json" summaries = [ serializer.serialize_study_summary(summary) for summary in storage.get_all_study_summaries() ] return { "study_summaries": summaries, } @app.post("/api/studies") @handle_json_api_exception def create_study() -> BottleViewReturn: response.content_type = "application/json" study_name = request.json.get("study_name", None) direction = request.json.get("direction", None) if study_name is None or direction not in ("minimize", "maximize"): response.status = 400 # Bad request return {"reason": "You need to set study_name and direction"} try: study_id = storage.create_new_study(study_name) except DuplicatedStudyError: response.status = 400 # Bad request return {"reason": f"'{study_name}' is already exists"} if direction.lower() == "maximize": storage.set_study_direction(study_id, StudyDirection.MAXIMIZE) else: storage.set_study_direction(study_id, StudyDirection.MINIMIZE) summary = get_study_summary(storage, study_id) if summary is None: response.status = 500 # Internal server error return {"reason": "Failed to create study"} response.status = 201 # Created return {"study_summary": serializer.serialize_study_summary(summary)} @app.delete("/api/studies/<study_id:int>") @handle_json_api_exception def delete_study(study_id: int) -> BottleViewReturn: response.content_type = "application/json" try: storage.delete_study(study_id) except KeyError: response.status = 404 # Not found return {"reason": f"study_id={study_id} is not found"} response.status = 204 # No content return "" @app.get("/api/studies/<study_id:int>") @handle_json_api_exception def get_study_detail(study_id: int) -> BottleViewReturn: response.content_type = "application/json" summary = get_study_summary(storage, study_id) if summary is None: response.status = 404 # Not found return {"reason": f"study_id={study_id} is not found"} trials = get_trials(storage, study_id) return serializer.serialize_study_detail(summary, trials) @app.get("/static/<filename:path>") def send_static(filename: str) -> BottleViewReturn: return static_file(filename, root=STATIC_DIR) return app