Skip to content

Commit

Permalink
problem docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
cahity committed Nov 13, 2024
1 parent 6210a0e commit b3a002b
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 12 deletions.
9 changes: 5 additions & 4 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ Welcome to VectOptAL, an open-source Python library built to tackle the challeng
:maxdepth: 1
:caption: Package Reference

order
models
algorithms
models
order
ordering_cone
acquisition
datasets
utils
design_space
confidence_region
datasets
utils


Indices and tables
Expand Down
6 changes: 3 additions & 3 deletions vectoptal/datasets/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ class Dataset(ABC):
Abstract base class for datasets that handles min-max scaling of input and standardization of
output. Any class inheriting from this class should implement the following properties:
- _in_dim: int
- _out_dim: int
- _cardinality: int
- :obj:`_in_dim`: :type:`int`
- :obj:`_out_dim`: :type:`int`
- :obj:`_cardinality`: :type:`int`
"""

@property
Expand Down
17 changes: 15 additions & 2 deletions vectoptal/design_space.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import numpy as np

from vectoptal.confidence_region import (
ConfidenceRegion,
RectangularConfidenceRegion,
EllipsoidalConfidenceRegion,
)
Expand Down Expand Up @@ -42,10 +43,22 @@ class DiscreteDesignSpace(DesignSpace):
This class is an abstract implementation of the `DesignSpace` abstract base class. It
represents a design space where the points are discrete. The class also maintains a list of
confidence regions associated with the design points.
A derived class must define the following properties:
- :obj:`points`: :type:`np.ndarray`
- :obj:`confidence_regions`: :type:`list[ConfidenceRegion]`
"""

points: np.ndarray
confidence_regions: list
@property
@abstractmethod
def points(self) -> np.ndarray:
pass

@property
@abstractmethod
def confidence_regions(self) -> list[ConfidenceRegion]:
pass

def __init__(self):
super().__init__()
Expand Down
156 changes: 154 additions & 2 deletions vectoptal/maximization_problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,44 @@
# It should both accomodate evaluations that are noisy in themselves and synthetically noisy ones.
# Maybe distinguish real problems from test problems.
class Problem(ABC):
"""
Abstract base class for defining optimization problems. Provides a template
for evaluating solutions in a given problem space.
.. note::
Classes derived from :class:`Problem` must implement the :meth:`evaluate` method.
"""

def __init__(self) -> None:
super().__init__()

@abstractmethod
def evaluate(self, x: np.ndarray) -> np.ndarray:
"""
Evaluates the problem at a given point (or array of points) :obj:`x`.
:param x: The input for where to evaluate the problem.
:type x: np.ndarray
:return: The evaluation result as an array, representing the objective values at :obj:`x`.
:rtype: np.ndarray
"""
pass


class ProblemFromDataset(Problem):
"""
Define an evaluatable optimization problem using data from a given dataset.
This class enables the evaluation of points based on nearest neighbor lookup
from an offline dataset, with optional Gaussian noise.
:param dataset: The dataset containing input and output data for the problem.
:type dataset: Dataset
:param noise_var: The variance of the noise to add to the outputs.
:type noise_var: float
"""

def __init__(self, dataset: Dataset, noise_var: float) -> None:
super().__init__()

Expand All @@ -30,6 +59,18 @@ def __init__(self, dataset: Dataset, noise_var: float) -> None:
self.noise_cholesky = np.linalg.cholesky(noise_covar)

def evaluate(self, x: np.ndarray, noisy: bool = True) -> np.ndarray:
"""
Evaluates the problem at given points by finding the nearest points
in the dataset and optionally adding Gaussian noise.
:param x: The input points to evaluate, given as an array of shape (N, in_dim).
:type x: np.ndarray
:param noisy: If `True`, adds Gaussian noise to the output based on the specified
noise variance. Defaults to `True`.
:type noisy: bool
:return: An array of shape (N, out_dim) representing the evaluated output.
:rtype: np.ndarray
"""
if x.ndim <= 1:
x = x.reshape(1, -1)

Expand All @@ -44,6 +85,21 @@ def evaluate(self, x: np.ndarray, noisy: bool = True) -> np.ndarray:


class ContinuousProblem(Problem):
"""
Abstract base class for continuous optimization problems. It includes noise handling for
outputs based on a specified noise variance. It should have the following property defined:
- :obj:`out_dim`: :type:`int`
:param noise_var: The variance of the noise to be added to the outputs.
:type noise_var: float
"""

@property
@abstractmethod
def out_dim(self) -> int:
pass

def __init__(self, noise_var: float) -> None:
super().__init__()

Expand All @@ -54,13 +110,41 @@ def __init__(self, noise_var: float) -> None:


def get_continuous_problem(name: str, noise_var: float) -> ContinuousProblem:
"""
Retrieves an instance of a continuous problem by name. If the
problem name is not recognized, a ValueError is raised.
:param name: The name of the continuous problem class to instantiate.
:type name: str
:param noise_var: The variance of the noise to apply in the problem.
:type noise_var: float
:return: An instance of the specified continuous problem.
:rtype: ContinuousProblem
:raises ValueError: If the specified problem name does not exist in the global scope.
"""
if name in globals():
return globals()[name](noise_var)

raise ValueError(f"Unknown continuous problem: {name}")


class BraninCurrin(ContinuousProblem):
"""
A continuous optimization problem combining the Branin and Currin functions.
This problem was first utilized by [Belakaria2019]_ for multi-objective
optimization tasks, where both objectives are evaluated over the same input domain.
:param noise_var: The variance of the noise added to the output evaluations.
:type noise_var: float
References:
.. [Belakaria2019]
Belakaria, Deshwal, Doppa.
Max-value Entropy Search for Multi-Objective Bayesian Optimization.
Neural Information Processing Systems (NeurIPS), 2019.
"""

bounds = [(0.0, 1.0), (0.0, 1.0)]
in_dim = len(bounds)
out_dim = 2
Expand All @@ -71,6 +155,14 @@ def __init__(self, noise_var: float) -> None:
super().__init__(noise_var)

def _branin(self, X):
"""
Computes the Branin function.
:param X: The input array of shape (N, 2).
:type X: np.ndarray
:return: The evaluated Branin function values.
:rtype: np.ndarray
"""
x_0 = 15 * X[..., 0] - 5
x_1 = 15 * X[..., 1]
X = np.stack([x_0, x_1], axis=1)
Expand All @@ -80,6 +172,14 @@ def _branin(self, X):
return t1**2 + t2 + 10

def _currin(self, X):
"""
Computes the Currin function.
:param X: The input array of shape (N, 2).
:type X: np.ndarray
:return: The evaluated Currin function values.
:rtype: np.ndarray
"""
x_0 = X[..., 0]
x_1 = X[..., 1]
x_1[x_1 == 0] += 1e-9
Expand All @@ -89,6 +189,15 @@ def _currin(self, X):
return factor1 * numer / denom

def evaluate_true(self, x: np.ndarray) -> np.ndarray:
"""
Evaluates the true (noiseless) outputs of the Branin and Currin functions,
normalized for each output dimension.
:param x: Input points to evaluate, with shape (N, 2).
:type x: np.ndarray
:return: A 2D array with normalized Branin and Currin function values for each input.
:rtype: np.ndarray
"""
branin = self._branin(x)
currin = self._currin(x)

Expand All @@ -100,6 +209,18 @@ def evaluate_true(self, x: np.ndarray) -> np.ndarray:
return Y

def evaluate(self, x: np.ndarray, noisy: bool = True) -> np.ndarray:
"""
Evaluates the problem at given points with optional Gaussian noise.
:param x: Input points to evaluate, given as an array of shape (N, 2).
:type x: np.ndarray
:param noisy: If `True`, adds Gaussian noise to the output based on the specified
noise variance. Defaults to `True`.
:type noisy: bool
:return: A 2D array with evaluated Branin and Currin values for each input,
with optional noise.
:rtype: np.ndarray
"""
if x.ndim == 1:
x = x.reshape(1, -1)

Expand All @@ -113,13 +234,46 @@ def evaluate(self, x: np.ndarray, noisy: bool = True) -> np.ndarray:


class DecoupledEvaluationProblem(Problem):
"""
Wrapper around a :class:`Problem` instance that allows for decoupled evaluations of
objective functions. This class enables selective evaluation of specific objectives
by indexing into the output of the underlying problem.
:param problem: An instance of :class:`Problem` to wrap and decouple evaluations.
:type problem: Problem
"""

def __init__(self, problem: Problem) -> None:
super().__init__()
self.problem = problem

def evaluate(
self, x: np.ndarray, evaluation_index: Optional[Union[int, List[int]]] = None
) -> np.ndarray:
"""
Evaluates the underlying problem at the given points and returns either the full
output or specific objectives as specified by `evaluation_index`.
:param x: The input points to evaluate, given as an array of shape (N, in_dim).
:type x: np.ndarray
:param evaluation_index: Specifies which objectives to return. Can be:
- `None` (default) to return all objectives,
- an `int` to return a specific objective across all points,
- a list of indices to return specific objectives for each point.
:type evaluation_index: Optional[Union[int, List[int]]]
:return: An array of evaluated values, either the full output or specific objectives.
:rtype: np.ndarray
:raises ValueError: If :obj:`evaluation_index` has an invalid format or length.
"""
if (
evaluation_index is not None
and not isinstance(evaluation_index, int)
and len(x) != len(evaluation_index)
):
raise ValueError(
"evaluation_index must; be None, have type int or have the same length as x."
)

values = self.problem.evaluate(x)

if evaluation_index is None:
Expand All @@ -128,7 +282,5 @@ def evaluate(
if isinstance(evaluation_index, int):
return values[:, evaluation_index]

assert len(x) == len(evaluation_index), "evaluation_index should be the same length as data"

evaluation_index = np.array(evaluation_index, dtype=np.int32)
return values[np.arange(len(evaluation_index)), evaluation_index]
2 changes: 1 addition & 1 deletion vectoptal/ordering_cone.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
class OrderingCone:
r"""
Represents a polyhedral ordering cone in the form :math:`C := \{ x | \mathbf{W}x \geq 0 \}`,
where :math`\mathbf{W}` is a matrix defining the cone's boundaries. The ordering cone is
where :math:`\mathbf{W}` is a matrix defining the cone's boundaries. The ordering cone is
used to check if points lie within the cone by testing if they satisfy the inequality.
:param W: A 2D array (matrix) that defines the ordering cone. The shape of
Expand Down

0 comments on commit b3a002b

Please sign in to comment.