Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework factory #681

Merged
merged 10 commits into from
Oct 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/src/code/algo/asha.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ Asynchronous Successive Halving Algorithm

Can't build documentation because of import order.
Sphinx is loading ``orion.algo.asha`` before ``orion.algo`` and therefore
there is a cycle between the definition of ``OptimizationAlgorithm`` and
there is a cycle between the definition of ``BaseAlgorithm`` and
``ASHA`` as the meta-class ``Factory`` is trying to import ``ASHA``.
`PR #135 <https://github.com/Epistimio/orion/pull/135/files>`_ should get rid of this problem.
2 changes: 0 additions & 2 deletions docs/src/code/core/io/database.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,3 @@ Databases
.. automodule:: orion.core.io.database
:members:
:show-inheritance:


2 changes: 1 addition & 1 deletion docs/src/user/parallel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ Executor backends
It is also possible to execute multiple workers using the argument ``--n-workers`` in commandline
or ``experiment.workon(n_workers)`` using the python API. The workers will work together
using the same mechanisms explained above, but an
:class:`orion.executor.base.Executor` backend will be used in addition
:class:`orion.executor.base.BaseExecutor` backend will be used in addition
to spawn the workers and maintain them alive. The default backend is :ref:`executor-joblib`.

You can configure it
Expand Down
4 changes: 4 additions & 0 deletions docs/src/user/storage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -477,18 +477,22 @@ Here's an example on how you could remove an experiment
--------------

.. automethod:: orion.core.io.database.Database.read
:noindex:

:hidden:`write`
---------------

.. automethod:: orion.core.io.database.Database.write
:noindex:

:hidden:`remove`
----------------

.. automethod:: orion.core.io.database.Database.remove
:noindex:

:hidden:`read_and_write`
------------------------

.. automethod:: orion.core.io.database.Database.read_and_write
:noindex:
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,19 @@
"console_scripts": [
"orion = orion.core.cli:main",
],
"OptimizationAlgorithm": [
"BaseAlgorithm": [
"random = orion.algo.random:Random",
"gridsearch = orion.algo.gridsearch:GridSearch",
"asha = orion.algo.asha:ASHA",
"hyperband = orion.algo.hyperband:Hyperband",
"tpe = orion.algo.tpe:TPE",
"EvolutionES = orion.algo.evolution_es:EvolutionES",
],
"Storage": [
"BaseStorageProtocol": [
"track = orion.storage.track:Track",
"legacy = orion.storage.legacy:Legacy",
],
"Executor": [
"BaseExecutor": [
"singleexecutor = orion.executor.single_backend:SingleExecutor",
"joblib = orion.executor.joblib_backend:Joblib",
"dask = orion.executor.dask_backend:Dask",
Expand Down
51 changes: 19 additions & 32 deletions src/orion/algo/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,14 @@
=====================

Formulation of a general search algorithm with respect to some objective.
Algorithm implementations must inherit from `orion.algo.base.OptimizationAlgorithm`.
Algorithm implementations must inherit from `orion.algo.base.BaseAlgorithm`.

Algorithms can be created using `algo_factory.create()`.

Examples
--------
>>> algo_factory.create('random', space, seed=1)
>>> algo_factory.create('some_fancy_algo', space, **some_fancy_algo_config)

"""
import copy
Expand All @@ -13,7 +20,7 @@
from abc import ABCMeta, abstractmethod

from orion.algo.space import Fidelity
from orion.core.utils import Factory
from orion.core.utils import GenericFactory

log = logging.getLogger(__name__)

Expand All @@ -24,7 +31,7 @@ def infer_trial_id(point):


# pylint: disable=too-many-public-methods
class BaseAlgorithm(object, metaclass=ABCMeta):
class BaseAlgorithm:
"""Base class describing what an algorithm can do.

Parameters
Expand Down Expand Up @@ -109,22 +116,12 @@ def __init__(self, space, **kwargs):
self._param_names = list(kwargs.keys())
# Instantiate tunable parameters of an algorithm
for varname, param in kwargs.items():
# Check if tunable element is another algorithm
if isinstance(param, dict) and len(param) == 1:
subalgo_type = list(param)[0]
subalgo_kwargs = param[subalgo_type]
if isinstance(subalgo_kwargs, dict):
param = OptimizationAlgorithm(subalgo_type, space, **subalgo_kwargs)
elif (
isinstance(param, str) and param.lower() in OptimizationAlgorithm.types
):
# pylint: disable=too-many-function-args
param = OptimizationAlgorithm(param, space)
elif varname == "seed":
self.seed_rng(param)

setattr(self, varname, param)

# TODO: move this inside an initialization function.
if hasattr(self, "seed"):
self.seed_rng(self.seed)

def seed_rng(self, seed):
"""Seed the state of the random number generator.

Expand Down Expand Up @@ -394,10 +391,7 @@ def configuration(self):
for attrname in self._param_names:
if attrname.startswith("_"): # Do not log _space or others in conf
continue
attr = getattr(self, attrname)
if isinstance(attr, BaseAlgorithm):
attr = attr.configuration
dict_form[attrname] = attr
dict_form[attrname] = getattr(self, attrname)

return {self.__class__.__name__.lower(): dict_form}

Expand All @@ -407,16 +401,9 @@ def space(self):
return self._space

@space.setter
def space(self, space_):
"""Propagate changes in defined space to possibly nested algorithms."""
self._space = space_
for attr in self.__dict__.values():
if isinstance(attr, BaseAlgorithm):
attr.space = space_

def space(self, space):
"""Set space."""
self._space = space

# pylint: disable=too-few-public-methods,abstract-method
class OptimizationAlgorithm(BaseAlgorithm, metaclass=Factory):
"""Class used to inject dependency on an algorithm implementation."""

pass
algo_factory = GenericFactory(BaseAlgorithm)
6 changes: 3 additions & 3 deletions src/orion/benchmark/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

import orion.core
from orion.client import create_experiment
from orion.executor.base import Executor
from orion.executor.base import executor_factory


class Benchmark:
Expand Down Expand Up @@ -51,7 +51,7 @@ class Benchmark:

storage: dict, optional
Configuration of the storage backend.
executor: `orion.executor.base.Executor`, optional
executor: `orion.executor.base.BaseExecutor`, optional
Executor to run the benchmark experiments
"""

Expand All @@ -62,7 +62,7 @@ def __init__(self, name, algorithms, targets, storage=None, executor=None):
self.targets = targets
self.metadata = {}
self.storage_config = storage
self.executor = executor or Executor(
self.executor = executor or executor_factory.create(
orion.core.config.worker.executor,
n_workers=orion.core.config.worker.n_workers,
**orion.core.config.worker.executor_configuration,
Expand Down
9 changes: 7 additions & 2 deletions src/orion/benchmark/assessment/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@

from .averagerank import AverageRank
from .averageresult import AverageResult
from .base import BaseAssess
from .base import BenchmarkAssessment, bench_assessment_factory

__all__ = ["BaseAssess", "AverageRank", "AverageResult"]
__all__ = [
"bench_assessment_factory",
"BenchmarkAssessment",
"AverageRank",
"AverageResult",
]
4 changes: 2 additions & 2 deletions src/orion/benchmark/assessment/averagerank.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@

from collections import defaultdict

from orion.benchmark.assessment.base import BaseAssess
from orion.benchmark.assessment.base import BenchmarkAssessment
from orion.plotting.base import rankings


class AverageRank(BaseAssess):
class AverageRank(BenchmarkAssessment):
"""
Evaluate the average performance (objective value) between different search algorithms from
the rank perspective at different time steps (trial number).
Expand Down
4 changes: 2 additions & 2 deletions src/orion/benchmark/assessment/averageresult.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
"""
from collections import defaultdict

from orion.benchmark.assessment.base import BaseAssess
from orion.benchmark.assessment.base import BenchmarkAssessment
from orion.plotting.base import regrets


class AverageResult(BaseAssess):
class AverageResult(BenchmarkAssessment):
"""
Evaluate the average performance (objective value) for each search algorithm
at different time steps (trial number).
Expand Down
10 changes: 3 additions & 7 deletions src/orion/benchmark/assessment/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@

from abc import ABC, abstractmethod

from orion.core.utils import Factory
from orion.core.utils import GenericFactory


class BaseAssess(ABC):
class BenchmarkAssessment(ABC):
"""Base class describing what an assessment can do.

Parameters
Expand Down Expand Up @@ -52,8 +52,4 @@ def configuration(self):
return {self.__class__.__qualname__: self._param_names}


# pylint: disable=too-few-public-methods,abstract-method
class BenchmarkAssessment(BaseAssess, metaclass=Factory):
"""Class used to inject dependency on an assessment implementation."""

pass
bench_assessment_factory = GenericFactory(BenchmarkAssessment)
10 changes: 5 additions & 5 deletions src/orion/benchmark/benchmark_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import logging

from orion.benchmark import Benchmark, Study
from orion.benchmark.assessment.base import BenchmarkAssessment
from orion.benchmark.task.base import BenchmarkTask
from orion.benchmark.assessment.base import bench_assessment_factory
from orion.benchmark.task.base import bench_task_factory
from orion.core.io.database import DuplicateKeyError
from orion.core.utils.exceptions import NoConfigurationError
from orion.storage.base import get_storage, setup_storage
Expand Down Expand Up @@ -38,7 +38,7 @@ def get_or_create_benchmark(
Task objects
storage: dict, optional
Configuration of the storage backend.
executor: `orion.executor.base.Executor`, optional
executor: `orion.executor.base.BaseExecutor`, optional
Executor to run the benchmark experiments
debug: bool, optional
If using in debug mode, the storage config is overrided with legacy:EphemeralDB.
Expand Down Expand Up @@ -94,11 +94,11 @@ def get_or_create_benchmark(


def _get_task(name, **kwargs):
return BenchmarkTask(of_type=name, **kwargs)
return bench_task_factory.create(of_type=name, **kwargs)


def _get_assessment(name, **kwargs):
return BenchmarkAssessment(of_type=name, **kwargs)
return bench_assessment_factory.create(of_type=name, **kwargs)


def _resolve_db_config(db_config):
Expand Down
11 changes: 9 additions & 2 deletions src/orion/benchmark/task/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,17 @@
===========================
"""

from .base import BaseTask
from .base import BenchmarkTask, bench_task_factory
from .branin import Branin
from .carromtable import CarromTable
from .eggholder import EggHolder
from .rosenbrock import RosenBrock

__all__ = ["BaseTask", "RosenBrock", "Branin", "CarromTable", "EggHolder"]
__all__ = [
"BenchmarkTask",
"RosenBrock",
"Branin",
"CarromTable",
"EggHolder",
"bench_task_factory",
]
10 changes: 3 additions & 7 deletions src/orion/benchmark/task/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@

from abc import ABC, abstractmethod

from orion.core.utils import Factory
from orion.core.utils import GenericFactory


class BaseTask(ABC):
class BenchmarkTask(ABC):
"""Base class describing what a task can do.
A task will define the objective function and search space of it.

Expand Down Expand Up @@ -59,8 +59,4 @@ def configuration(self):
return {self.__class__.__qualname__: self._param_names}


# pylint: disable=too-few-public-methods,abstract-method
class BenchmarkTask(BaseTask, metaclass=Factory):
"""Class used to inject dependency on an task implementation."""

pass
bench_task_factory = GenericFactory(BenchmarkTask)
4 changes: 2 additions & 2 deletions src/orion/benchmark/task/branin.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@

import numpy

from orion.benchmark.task.base import BaseTask
from orion.benchmark.task.base import BenchmarkTask


class Branin(BaseTask):
class Branin(BenchmarkTask):
"""`Branin function <http://infinity77.net/global_optimization/test_functions_nd_B.html#go_benchmark.Branin01>`_
as benchmark task
"""
Expand Down
4 changes: 2 additions & 2 deletions src/orion/benchmark/task/carromtable.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
"""
import numpy

from orion.benchmark.task.base import BaseTask
from orion.benchmark.task.base import BenchmarkTask


class CarromTable(BaseTask):
class CarromTable(BenchmarkTask):
"""`CarromTable function <http://infinity77.net/global_optimization/test_functions_nd_C.html#go_benchmark.CarromTable>`_
as benchmark task"""

Expand Down
4 changes: 2 additions & 2 deletions src/orion/benchmark/task/eggholder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
"""
import numpy

from orion.benchmark.task.base import BaseTask
from orion.benchmark.task.base import BenchmarkTask


class EggHolder(BaseTask):
class EggHolder(BenchmarkTask):
"""`EggHolder function <http://infinity77.net/global_optimization/test_functions_nd_E.html#go_benchmark.EggHolder>`_
as benchmark task"""

Expand Down
4 changes: 2 additions & 2 deletions src/orion/benchmark/task/rosenbrock.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
"""
import numpy

from orion.benchmark.task.base import BaseTask
from orion.benchmark.task.base import BenchmarkTask


class RosenBrock(BaseTask):
class RosenBrock(BenchmarkTask):
"""`RosenBrock function <http://infinity77.net/global_optimization/test_functions_nd_R.html#go_benchmark.Rosenbrock>`_
as benchmark task"""

Expand Down
2 changes: 1 addition & 1 deletion src/orion/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def build_experiment(
config_change_type: str, optional
How to resolve config change automatically. Must be one of 'noeffect', 'unsure' or
'break'. Defaults to 'break'.
executor: `orion.executor.base.Executor`, optional
executor: `orion.executor.base.BaseExecutor`, optional
Executor to run the experiment

Raises
Expand Down
Loading