Coverage for mlos_core/mlos_core/tests/optimizers/optimizer_multiobj_test.py: 100%
60 statements
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-21 01:50 +0000
« prev ^ index » next coverage.py v7.6.10, created at 2025-01-21 01:50 +0000
1#
2# Copyright (c) Microsoft Corporation.
3# Licensed under the MIT License.
4#
5"""Test multi-target optimization."""
7import logging
9import ConfigSpace as CS
10import pandas as pd
11import pytest
13from mlos_core.data_classes import Observations, Suggestion
14from mlos_core.optimizers import BaseOptimizer, OptimizerType
15from mlos_core.tests import SEED
17_LOG = logging.getLogger(__name__)
20@pytest.mark.parametrize(
21 ("optimizer_class", "kwargs"),
22 [
23 *[(member.value, {}) for member in OptimizerType],
24 ],
25)
26def test_multi_target_opt_wrong_weights(
27 optimizer_class: type[BaseOptimizer],
28 kwargs: dict,
29) -> None:
30 """Make sure that the optimizer raises an error if the number of objective weights
31 does not match the number of optimization targets.
32 """
33 with pytest.raises(ValueError):
34 optimizer_class(
35 parameter_space=CS.ConfigurationSpace(seed=SEED),
36 optimization_targets=["main_score", "other_score"],
37 objective_weights=[1],
38 **kwargs,
39 )
42@pytest.mark.parametrize(
43 ("objective_weights"),
44 [
45 [2, 1],
46 [0.5, 0.5],
47 None,
48 ],
49)
50@pytest.mark.parametrize(
51 ("optimizer_class", "kwargs"),
52 [
53 *[(member.value, {}) for member in OptimizerType],
54 ],
55)
56def test_multi_target_opt(
57 objective_weights: list[float] | None,
58 optimizer_class: type[BaseOptimizer],
59 kwargs: dict,
60) -> None:
61 """Toy multi-target optimization problem to test the optimizers with mixed numeric
62 types to ensure that original dtypes are retained.
63 """
64 # pylint: disable=too-many-locals
65 max_iterations = 10
67 def objective(point: pd.Series) -> pd.Series:
68 # mix of hyperparameters, optimal is to select the highest possible
69 ret: pd.Series = pd.Series(
70 {
71 "main_score": point.x + point.y,
72 "other_score": point.x**2 + point.y**2,
73 }
74 )
75 return ret
77 input_space = CS.ConfigurationSpace(seed=SEED)
78 # add a mix of numeric datatypes
79 input_space.add(CS.UniformIntegerHyperparameter(name="x", lower=0, upper=5))
80 input_space.add(CS.UniformFloatHyperparameter(name="y", lower=0.0, upper=5.0))
82 optimizer = optimizer_class(
83 parameter_space=input_space,
84 optimization_targets=["main_score", "other_score"],
85 objective_weights=objective_weights,
86 **kwargs,
87 )
89 with pytest.raises(ValueError, match="No observations"):
90 optimizer.get_best_observations()
92 with pytest.raises(ValueError, match="No observations"):
93 optimizer.get_observations()
95 for _ in range(max_iterations):
96 suggestion = optimizer.suggest()
97 assert isinstance(suggestion, Suggestion)
98 assert isinstance(suggestion.config, pd.Series)
99 assert suggestion.metadata is None or isinstance(suggestion.metadata, pd.Series)
100 assert set(suggestion.config.index) == {"x", "y"}
101 # Check suggestion values are the expected dtype
102 assert isinstance(suggestion.config["x"], int)
103 assert isinstance(suggestion.config["y"], float)
104 # Check that suggestion is in the space
105 config_dict: dict = suggestion.config.to_dict()
106 test_configuration = CS.Configuration(optimizer.parameter_space, config_dict)
107 # Raises an error if outside of configuration space
108 test_configuration.check_valid_configuration()
109 # Test registering the suggested configuration with a score.
110 observation = objective(suggestion.config)
111 assert isinstance(observation, pd.Series)
112 assert set(observation.index) == {"main_score", "other_score"}
113 optimizer.register(observations=suggestion.complete(observation))
115 best_observations = optimizer.get_best_observations()
116 assert isinstance(best_observations, Observations)
117 assert isinstance(best_observations.configs, pd.DataFrame)
118 assert isinstance(best_observations.scores, pd.DataFrame)
119 assert best_observations.contexts is None
120 assert set(best_observations.configs.columns) == {"x", "y"}
121 assert set(best_observations.scores.columns) == {"main_score", "other_score"}
122 assert best_observations.configs.shape == (1, 2)
123 assert best_observations.scores.shape == (1, 2)
125 all_observations = optimizer.get_observations()
126 assert isinstance(all_observations, Observations)
127 assert isinstance(all_observations.configs, pd.DataFrame)
128 assert isinstance(all_observations.scores, pd.DataFrame)
129 assert all_observations.contexts is None
130 assert set(all_observations.configs.columns) == {"x", "y"}
131 assert set(all_observations.scores.columns) == {"main_score", "other_score"}
132 assert all_observations.configs.shape == (max_iterations, 2)
133 assert all_observations.scores.shape == (max_iterations, 2)