예제 #1
0
    def testTransformOptimizationConfig(self):
        m1 = Metric(name="m1")
        m2 = Metric(name="m2")
        m3 = Metric(name="m3")
        objective = Objective(metric=m3, minimize=False)
        cons = [
            OutcomeConstraint(metric=m1,
                              op=ComparisonOp.GEQ,
                              bound=2.0,
                              relative=False),
            OutcomeConstraint(metric=m2,
                              op=ComparisonOp.LEQ,
                              bound=3.5,
                              relative=False),
            ScalarizedOutcomeConstraint(
                metrics=[m1, m2],
                weights=[0.5, 0.5],
                op=ComparisonOp.LEQ,
                bound=3.5,
                relative=False,
            ),
        ]
        oc = OptimizationConfig(objective=objective, outcome_constraints=cons)
        oc = self.t.transform_optimization_config(oc, None, None)
        cons_t = [
            OutcomeConstraint(metric=m1,
                              op=ComparisonOp.GEQ,
                              bound=1.0,
                              relative=False),
            OutcomeConstraint(
                metric=m2,
                op=ComparisonOp.LEQ,
                bound=2.0 * sqrt(3),  # (3.5 - 1.5) / sqrt(1/3)
                relative=False,
            ),
            ScalarizedOutcomeConstraint(
                metrics=[m1, m2],
                weights=[0.5 * 1.0, 0.5 * sqrt(1 / 3)],
                op=ComparisonOp.LEQ,
                bound=2.25,  # 3.5 - (0.5 * 1.0 + 0.5 * 1.5)
                relative=False,
            ),
        ]
        self.assertTrue(oc.outcome_constraints == cons_t)
        self.assertTrue(oc.objective == objective)

        # Check fail with relative
        con = OutcomeConstraint(metric=m1,
                                op=ComparisonOp.GEQ,
                                bound=2.0,
                                relative=True)
        oc = OptimizationConfig(objective=objective, outcome_constraints=[con])
        with self.assertRaises(ValueError):
            oc = self.t.transform_optimization_config(oc, None, None)
예제 #2
0
 def setUp(self):
     self.metrics = [
         Metric(name="m1", lower_is_better=True),
         Metric(name="m2", lower_is_better=True),
         Metric(name="m3", lower_is_better=True),
     ]
     self.weights = [0.1, 0.3, 0.6]
     self.bound = 0
     self.constraint = ScalarizedOutcomeConstraint(
         metrics=self.metrics,
         weights=self.weights,
         op=ComparisonOp.GEQ,
         bound=self.bound,
     )
예제 #3
0
    def test_extract_outcome_constraints(self):
        outcomes = ["m1", "m2", "m3"]
        # pass no outcome constraints
        self.assertIsNone(extract_outcome_constraints([], outcomes))

        outcome_constraints = [
            OutcomeConstraint(metric=Metric("m1"),
                              op=ComparisonOp.LEQ,
                              bound=0)
        ]
        res = extract_outcome_constraints(outcome_constraints, outcomes)
        self.assertEqual(res[0].shape, (1, 3))
        self.assertListEqual(list(res[0][0]), [1, 0, 0])
        self.assertEqual(res[1][0][0], 0)

        outcome_constraints = [
            OutcomeConstraint(metric=Metric("m1"),
                              op=ComparisonOp.LEQ,
                              bound=0),
            ScalarizedOutcomeConstraint(
                metrics=[Metric("m2"), Metric("m3")],
                weights=[0.5, 0.5],
                op=ComparisonOp.GEQ,
                bound=1,
            ),
        ]
        res = extract_outcome_constraints(outcome_constraints, outcomes)
        self.assertEqual(res[0].shape, (2, 3))
        self.assertListEqual(list(res[0][0]), [1, 0, 0])
        self.assertListEqual(list(res[0][1]), [0, -0.5, -0.5])
        self.assertEqual(res[1][0][0], 0)
        self.assertEqual(res[1][1][0], -1)
예제 #4
0
    def testEq(self):
        constraint1 = ScalarizedOutcomeConstraint(
            metrics=self.metrics,
            weights=self.weights,
            op=ComparisonOp.GEQ,
            bound=self.bound,
        )
        self.assertEqual(constraint1, self.constraint)

        constraint2 = ScalarizedOutcomeConstraint(
            metrics=self.metrics,
            weights=[0.2, 0.2, 0.6],
            op=ComparisonOp.LEQ,
            bound=self.bound,
        )
        self.assertNotEqual(constraint2, self.constraint)
예제 #5
0
 def setUp(self):
     self.metrics = {"m1": Metric(name="m1"), "m2": Metric(name="m2")}
     self.objective = Objective(metric=self.metrics["m1"], minimize=False)
     self.alt_objective = Objective(metric=self.metrics["m2"],
                                    minimize=False)
     self.multi_objective = MultiObjective(
         metrics=[self.metrics["m1"], self.metrics["m2"]])
     self.m2_objective = ScalarizedObjective(
         metrics=[self.metrics["m1"], self.metrics["m2"]])
     self.outcome_constraint = OutcomeConstraint(metric=self.metrics["m2"],
                                                 op=ComparisonOp.GEQ,
                                                 bound=-0.25)
     self.additional_outcome_constraint = OutcomeConstraint(
         metric=self.metrics["m2"], op=ComparisonOp.LEQ, bound=0.25)
     self.scalarized_outcome_constraint = ScalarizedOutcomeConstraint(
         metrics=[self.metrics["m1"], self.metrics["m2"]],
         weights=[0.5, 0.5],
         op=ComparisonOp.GEQ,
         bound=-0.25,
     )
     self.outcome_constraints = [
         self.outcome_constraint,
         self.additional_outcome_constraint,
         self.scalarized_outcome_constraint,
     ]
예제 #6
0
def get_scalarized_outcome_constraint() -> ScalarizedOutcomeConstraint:
    return ScalarizedOutcomeConstraint(
        metrics=[Metric(name="oc_m3"), Metric(name="oc_m4")],
        weights=[0.2, 0.8],
        op=ComparisonOp.GEQ,
        bound=-0.25,
    )
예제 #7
0
    def test_best_point(
        self,
        _mock_gen,
        _mock_best_point,
        _mock_fit,
        _mock_predict,
        _mock_gen_arms,
        _mock_unwrap,
        _mock_obs_from_data,
    ):
        exp = Experiment(search_space=get_search_space_for_range_value(),
                         name="test")
        modelbridge = ArrayModelBridge(
            search_space=get_search_space_for_range_value(),
            model=NumpyModel(),
            transforms=[t1, t2],
            experiment=exp,
            data=Data(),
        )
        self.assertEqual(list(modelbridge.transforms.keys()),
                         ["Cast", "t1", "t2"])
        # _fit is mocked, which typically sets this.
        modelbridge.outcomes = ["a"]
        run = modelbridge.gen(
            n=1,
            optimization_config=OptimizationConfig(
                objective=Objective(metric=Metric("a"), minimize=False),
                outcome_constraints=[],
            ),
        )
        arm, predictions = run.best_arm_predictions
        self.assertEqual(arm.parameters, {})
        self.assertEqual(predictions[0], {"m": 1.0})
        self.assertEqual(predictions[1], {"m": {"m": 2.0}})
        # test check that optimization config is required
        with self.assertRaises(ValueError):
            run = modelbridge.gen(n=1, optimization_config=None)

        # test optimization config validation - raise error when
        # ScalarizedOutcomeConstraint contains a metric that is not in the outcomes
        with self.assertRaises(ValueError):
            run = modelbridge.gen(
                n=1,
                optimization_config=OptimizationConfig(
                    objective=Objective(metric=Metric("a"), minimize=False),
                    outcome_constraints=[
                        ScalarizedOutcomeConstraint(
                            metrics=[Metric("wrong_metric_name")],
                            weights=[1.0],
                            op=ComparisonOp.LEQ,
                            bound=0,
                        )
                    ],
                ),
            )
예제 #8
0
    def testRaiseError(self):
        # set a wrong weights
        with self.assertRaises(ValueError):
            ScalarizedOutcomeConstraint(
                metrics=self.metrics,
                weights=[0.2, 0.8],
                op=ComparisonOp.LEQ,
                bound=self.bound,
            )

        with self.assertRaises(NotImplementedError):
            return self.constraint.metric

        with self.assertRaises(NotImplementedError):
            self.constraint.metric = self.metrics[0]
예제 #9
0
 def testInit(self):
     self.assertListEqual(self.constraint.metrics, self.metrics)
     self.assertListEqual(self.constraint.weights, self.weights)
     self.assertEqual(len(list(self.constraint.metric_weights)),
                      len(self.metrics))
     self.assertEqual(
         str(self.constraint),
         ("ScalarizedOutcomeConstraint(metric_names=['m1', 'm2', 'm3'], "
          "weights=[0.1, 0.3, 0.6], >= 0%)"),
     )
     # check that weights are set uniformly by default
     con = ScalarizedOutcomeConstraint(
         metrics=[
             Metric(name="m1", lower_is_better=True),
             Metric(name="m2", lower_is_better=True),
         ],
         op=ComparisonOp.LEQ,
         bound=self.bound,
     )
     self.assertListEqual(con.weights, [0.5, 0.5])
예제 #10
0
    def metric_from_sqa(
            self, metric_sqa: SQAMetric
    ) -> Union[Metric, Objective, OutcomeConstraint]:
        """Convert SQLAlchemy Metric to Ax Metric, Objective, or OutcomeConstraint."""

        metric = self.metric_from_sqa_util(metric_sqa)

        if metric_sqa.intent == MetricIntent.TRACKING:
            return metric
        elif metric_sqa.intent == MetricIntent.OBJECTIVE:
            if metric_sqa.minimize is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to Objective because minimize is None."
                )
            if metric_sqa.scalarized_objective_weight is not None:
                raise SQADecodeError(  # pragma: no cover
                    "The metric corresponding to regular objective does not \
                    have weight attribute")
            return Objective(metric=metric, minimize=metric_sqa.minimize)
        elif (metric_sqa.intent == MetricIntent.MULTI_OBJECTIVE
              ):  # metric_sqa is a parent whose children are individual
            # metrics in MultiObjective
            if metric_sqa.minimize is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to MultiObjective \
                    because minimize is None.")
            metrics_sqa_children = metric_sqa.scalarized_objective_children_metrics
            if metrics_sqa_children is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to MultiObjective \
                    because the parent metric has no children metrics.")

            # Extracting metric and weight for each child
            metrics = [
                self.metric_from_sqa_util(child)
                for child in metrics_sqa_children
            ]

            return MultiObjective(
                metrics=list(metrics),
                # pyre-fixme[6]: Expected `bool` for 2nd param but got `Optional[bool]`.
                minimize=metric_sqa.minimize,
            )
        elif (metric_sqa.intent == MetricIntent.SCALARIZED_OBJECTIVE
              ):  # metric_sqa is a parent whose children are individual
            # metrics in Scalarized Objective
            if metric_sqa.minimize is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to Scalarized Objective \
                    because minimize is None.")
            metrics_sqa_children = metric_sqa.scalarized_objective_children_metrics
            if metrics_sqa_children is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to Scalarized Objective \
                    because the parent metric has no children metrics.")

            # Extracting metric and weight for each child
            metrics, weights = zip(*[(
                self.metric_from_sqa_util(child),
                child.scalarized_objective_weight,
            ) for child in metrics_sqa_children])
            return ScalarizedObjective(
                metrics=list(metrics),
                weights=list(weights),
                # pyre-fixme[6]: Expected `bool` for 3nd param but got `Optional[bool]`.
                minimize=metric_sqa.minimize,
            )
        elif metric_sqa.intent == MetricIntent.OUTCOME_CONSTRAINT:
            if (metric_sqa.bound is None or metric_sqa.op is None
                    or metric_sqa.relative is None):
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to OutcomeConstraint because "
                    "bound, op, or relative is None.")
            return OutcomeConstraint(
                metric=metric,
                # pyre-fixme[6]: Expected `float` for 2nd param but got
                #  `Optional[float]`.
                bound=metric_sqa.bound,
                op=metric_sqa.op,
                relative=metric_sqa.relative,
            )
        elif metric_sqa.intent == MetricIntent.SCALARIZED_OUTCOME_CONSTRAINT:
            if (metric_sqa.bound is None or metric_sqa.op is None
                    or metric_sqa.relative is None):
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to Scalarized OutcomeConstraint because "
                    "bound, op, or relative is None.")
            metrics_sqa_children = (
                metric_sqa.scalarized_outcome_constraint_children_metrics)
            if metrics_sqa_children is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to Scalarized OutcomeConstraint \
                    because the parent metric has no children metrics.")

            # Extracting metric and weight for each child
            metrics, weights = zip(*[(
                self.metric_from_sqa_util(child),
                child.scalarized_outcome_constraint_weight,
            ) for child in metrics_sqa_children])
            return ScalarizedOutcomeConstraint(
                metrics=list(metrics),
                weights=list(weights),
                # pyre-fixme[6]: Expected `float` for 2nd param but got
                #  `Optional[float]`.
                bound=metric_sqa.bound,
                op=metric_sqa.op,
                relative=metric_sqa.relative,
            )

        elif metric_sqa.intent == MetricIntent.OBJECTIVE_THRESHOLD:
            if metric_sqa.bound is None or metric_sqa.relative is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to ObjectiveThreshold because "
                    "bound, op, or relative is None.")
            return ObjectiveThreshold(
                metric=metric,
                # pyre-fixme[6]: Expected `float` for 2nd param but got
                #  `Optional[float]`.
                bound=metric_sqa.bound,
                relative=metric_sqa.relative,
                op=metric_sqa.op,
            )
        else:
            raise SQADecodeError(
                f"Cannot decode SQAMetric because {metric_sqa.intent} "
                f"is an invalid intent.")
예제 #11
0
class ScalarizedOutcomeConstraintTest(TestCase):
    def setUp(self):
        self.metrics = [
            Metric(name="m1", lower_is_better=True),
            Metric(name="m2", lower_is_better=True),
            Metric(name="m3", lower_is_better=True),
        ]
        self.weights = [0.1, 0.3, 0.6]
        self.bound = 0
        self.constraint = ScalarizedOutcomeConstraint(
            metrics=self.metrics,
            weights=self.weights,
            op=ComparisonOp.GEQ,
            bound=self.bound,
        )

    def testInit(self):
        self.assertListEqual(self.constraint.metrics, self.metrics)
        self.assertListEqual(self.constraint.weights, self.weights)
        self.assertEqual(len(list(self.constraint.metric_weights)),
                         len(self.metrics))
        self.assertEqual(
            str(self.constraint),
            ("ScalarizedOutcomeConstraint(metric_names=['m1', 'm2', 'm3'], "
             "weights=[0.1, 0.3, 0.6], >= 0%)"),
        )
        # check that weights are set uniformly by default
        con = ScalarizedOutcomeConstraint(
            metrics=[
                Metric(name="m1", lower_is_better=True),
                Metric(name="m2", lower_is_better=True),
            ],
            op=ComparisonOp.LEQ,
            bound=self.bound,
        )
        self.assertListEqual(con.weights, [0.5, 0.5])

    def testEq(self):
        constraint1 = ScalarizedOutcomeConstraint(
            metrics=self.metrics,
            weights=self.weights,
            op=ComparisonOp.GEQ,
            bound=self.bound,
        )
        self.assertEqual(constraint1, self.constraint)

        constraint2 = ScalarizedOutcomeConstraint(
            metrics=self.metrics,
            weights=[0.2, 0.2, 0.6],
            op=ComparisonOp.LEQ,
            bound=self.bound,
        )
        self.assertNotEqual(constraint2, self.constraint)

    def testClone(self):
        self.assertEqual(self.constraint, self.constraint.clone())

    def testValidMutations(self):
        # updating constraint metric is ok as long as lower_is_better is compatible.
        self.constraint.metrics = [
            Metric(name="m2"),
            Metric(name="m4"),
        ]
        self.constraint.op = ComparisonOp.LEQ

    def testRaiseError(self):
        # set a wrong weights
        with self.assertRaises(ValueError):
            ScalarizedOutcomeConstraint(
                metrics=self.metrics,
                weights=[0.2, 0.8],
                op=ComparisonOp.LEQ,
                bound=self.bound,
            )

        with self.assertRaises(NotImplementedError):
            return self.constraint.metric

        with self.assertRaises(NotImplementedError):
            self.constraint.metric = self.metrics[0]
예제 #12
0
파일: decoder.py 프로젝트: Balandat/Ax
    def metric_from_sqa(
            self, metric_sqa: SQAMetric
    ) -> Union[Metric, Objective, OutcomeConstraint]:
        """Convert SQLAlchemy Metric to Ax Metric, Objective, or OutcomeConstraint."""

        metric = self.metric_from_sqa_util(metric_sqa)

        if metric_sqa.intent == MetricIntent.TRACKING:
            return metric
        elif metric_sqa.intent == MetricIntent.OBJECTIVE:
            if metric_sqa.minimize is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to Objective because minimize is None."
                )
            if metric_sqa.scalarized_objective_weight is not None:
                raise SQADecodeError(  # pragma: no cover
                    "The metric corresponding to regular objective does not \
                    have weight attribute")
            return Objective(metric=metric, minimize=metric_sqa.minimize)
        elif (metric_sqa.intent == MetricIntent.MULTI_OBJECTIVE
              ):  # metric_sqa is a parent whose children are individual
            # metrics in MultiObjective
            try:
                metrics_sqa_children = metric_sqa.scalarized_objective_children_metrics
            except DetachedInstanceError:
                metrics_sqa_children = _get_scalarized_objective_children_metrics(
                    metric_id=metric_sqa.id, decoder=self)

            if metrics_sqa_children is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to MultiObjective \
                    because the parent metric has no children metrics.")

            # Extracting metric and weight for each child
            objectives = [
                Objective(
                    metric=self.metric_from_sqa_util(metric_sqa),
                    minimize=metric_sqa.minimize,
                ) for metric_sqa in metrics_sqa_children
            ]

            multi_objective = MultiObjective(objectives=objectives)
            multi_objective.db_id = metric_sqa.id
            return multi_objective
        elif (metric_sqa.intent == MetricIntent.SCALARIZED_OBJECTIVE
              ):  # metric_sqa is a parent whose children are individual
            # metrics in Scalarized Objective
            if metric_sqa.minimize is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to Scalarized Objective \
                    because minimize is None.")

            try:
                metrics_sqa_children = metric_sqa.scalarized_objective_children_metrics
            except DetachedInstanceError:
                metrics_sqa_children = _get_scalarized_objective_children_metrics(
                    metric_id=metric_sqa.id, decoder=self)

            if metrics_sqa_children is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to Scalarized Objective \
                    because the parent metric has no children metrics.")

            # Extracting metric and weight for each child
            metrics, weights = zip(*[(
                self.metric_from_sqa_util(child),
                child.scalarized_objective_weight,
            ) for child in metrics_sqa_children])
            scalarized_objective = ScalarizedObjective(
                metrics=list(metrics),
                weights=list(weights),
                minimize=not_none(metric_sqa.minimize),
            )
            scalarized_objective.db_id = metric_sqa.id
            return scalarized_objective
        elif metric_sqa.intent == MetricIntent.OUTCOME_CONSTRAINT:
            if (metric_sqa.bound is None or metric_sqa.op is None
                    or metric_sqa.relative is None):
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to OutcomeConstraint because "
                    "bound, op, or relative is None.")
            return OutcomeConstraint(
                metric=metric,
                bound=metric_sqa.bound,
                op=metric_sqa.op,
                relative=metric_sqa.relative,
            )
        elif metric_sqa.intent == MetricIntent.SCALARIZED_OUTCOME_CONSTRAINT:
            if (metric_sqa.bound is None or metric_sqa.op is None
                    or metric_sqa.relative is None):
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to Scalarized OutcomeConstraint because "
                    "bound, op, or relative is None.")

            try:
                metrics_sqa_children = (
                    metric_sqa.scalarized_outcome_constraint_children_metrics)
            except DetachedInstanceError:
                metrics_sqa_children = (
                    _get_scalarized_outcome_constraint_children_metrics(
                        metric_id=metric_sqa.id, decoder=self))

            if metrics_sqa_children is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to Scalarized OutcomeConstraint \
                    because the parent metric has no children metrics.")

            # Extracting metric and weight for each child
            metrics, weights = zip(*[(
                self.metric_from_sqa_util(child),
                child.scalarized_outcome_constraint_weight,
            ) for child in metrics_sqa_children])
            scalarized_outcome_constraint = ScalarizedOutcomeConstraint(
                metrics=list(metrics),
                weights=list(weights),
                bound=not_none(metric_sqa.bound),
                op=not_none(metric_sqa.op),
                relative=not_none(metric_sqa.relative),
            )
            scalarized_outcome_constraint.db_id = metric_sqa.id
            return scalarized_outcome_constraint
        elif metric_sqa.intent == MetricIntent.OBJECTIVE_THRESHOLD:
            if metric_sqa.bound is None or metric_sqa.relative is None:
                raise SQADecodeError(  # pragma: no cover
                    "Cannot decode SQAMetric to ObjectiveThreshold because "
                    "bound, op, or relative is None.")
            ot = ObjectiveThreshold(
                metric=metric,
                bound=metric_sqa.bound,
                relative=metric_sqa.relative,
                op=metric_sqa.op,
            )
            # ObjectiveThreshold constructor clones the passed-in metric, which means
            # the db id gets lost and so we need to reset it
            ot.metric._db_id = metric.db_id
            return ot
        else:
            raise SQADecodeError(
                f"Cannot decode SQAMetric because {metric_sqa.intent} "
                f"is an invalid intent.")
예제 #13
0
    def testDerelativizeTransform(self, mock_predict, mock_fit,
                                  mock_observations_from_data):
        t = Derelativize(search_space=None,
                         observation_features=None,
                         observation_data=None)

        # ModelBridge with in-design status quo
        search_space = SearchSpace(parameters=[
            RangeParameter("x", ParameterType.FLOAT, 0, 20),
            RangeParameter("y", ParameterType.FLOAT, 0, 20),
        ])
        g = ModelBridge(
            search_space=search_space,
            model=None,
            transforms=[],
            experiment=Experiment(search_space, "test"),
            data=Data(),
            status_quo_name="1_1",
        )

        # Test with no relative constraints
        objective = Objective(Metric("c"))
        oc = OptimizationConfig(
            objective=objective,
            outcome_constraints=[
                OutcomeConstraint(Metric("a"),
                                  ComparisonOp.LEQ,
                                  bound=2,
                                  relative=False),
                ScalarizedOutcomeConstraint(
                    metrics=[Metric("a"), Metric("b")],
                    op=ComparisonOp.LEQ,
                    bound=2,
                    weights=[0.5, 0.5],
                    relative=False,
                ),
            ],
        )
        oc2 = t.transform_optimization_config(oc, g, None)
        self.assertTrue(oc == oc2)

        # Test with relative constraint, in-design status quo
        oc = OptimizationConfig(
            objective=objective,
            outcome_constraints=[
                OutcomeConstraint(Metric("a"),
                                  ComparisonOp.LEQ,
                                  bound=2,
                                  relative=False),
                OutcomeConstraint(Metric("b"),
                                  ComparisonOp.LEQ,
                                  bound=-10,
                                  relative=True),
                ScalarizedOutcomeConstraint(
                    metrics=[Metric("a"), Metric("b")],
                    weights=[0.0, 1.0],
                    op=ComparisonOp.LEQ,
                    bound=-10,
                    relative=True,
                ),
            ],
        )
        oc = t.transform_optimization_config(oc, g, None)
        self.assertTrue(oc.outcome_constraints == [
            OutcomeConstraint(
                Metric("a"), ComparisonOp.LEQ, bound=2, relative=False),
            OutcomeConstraint(
                Metric("b"), ComparisonOp.LEQ, bound=4.5, relative=False),
            ScalarizedOutcomeConstraint(
                metrics=[Metric("a"), Metric("b")],
                weights=[0.0, 1.0],
                op=ComparisonOp.LEQ,
                bound=4.5,
                relative=False,
            ),
        ])
        obsf = mock_predict.mock_calls[0][1][1][0]
        obsf2 = ObservationFeatures(parameters={"x": 2.0, "y": 10.0})
        self.assertTrue(obsf == obsf2)

        # Test with relative constraint, out-of-design status quo
        mock_predict.side_effect = RuntimeError()
        g = ModelBridge(
            search_space=search_space,
            model=None,
            transforms=[],
            experiment=Experiment(search_space, "test"),
            data=Data(),
            status_quo_name="1_2",
        )
        oc = OptimizationConfig(
            objective=objective,
            outcome_constraints=[
                OutcomeConstraint(Metric("a"),
                                  ComparisonOp.LEQ,
                                  bound=2,
                                  relative=False),
                OutcomeConstraint(Metric("b"),
                                  ComparisonOp.LEQ,
                                  bound=-10,
                                  relative=True),
                ScalarizedOutcomeConstraint(
                    metrics=[Metric("a"), Metric("b")],
                    weights=[0.0, 1.0],
                    op=ComparisonOp.LEQ,
                    bound=-10,
                    relative=True,
                ),
            ],
        )
        oc = t.transform_optimization_config(oc, g, None)
        self.assertTrue(oc.outcome_constraints == [
            OutcomeConstraint(
                Metric("a"), ComparisonOp.LEQ, bound=2, relative=False),
            OutcomeConstraint(
                Metric("b"), ComparisonOp.LEQ, bound=3.6, relative=False),
            ScalarizedOutcomeConstraint(
                metrics=[Metric("a"), Metric("b")],
                weights=[0.0, 1.0],
                op=ComparisonOp.LEQ,
                bound=3.6,
                relative=False,
            ),
        ])
        self.assertEqual(mock_predict.call_count, 2)

        # Raises error if predict fails with in-design status quo
        g = ModelBridge(
            search_space=search_space,
            model=None,
            transforms=[],
            experiment=Experiment(search_space, "test"),
            data=Data(),
            status_quo_name="1_1",
        )
        oc = OptimizationConfig(
            objective=objective,
            outcome_constraints=[
                OutcomeConstraint(Metric("a"),
                                  ComparisonOp.LEQ,
                                  bound=2,
                                  relative=False),
                OutcomeConstraint(Metric("b"),
                                  ComparisonOp.LEQ,
                                  bound=-10,
                                  relative=True),
            ],
        )
        with self.assertRaises(RuntimeError):
            oc = t.transform_optimization_config(oc, g, None)

        # Bypasses error if use_raw_sq
        t2 = Derelativize(
            search_space=None,
            observation_features=None,
            observation_data=None,
            config={"use_raw_status_quo": True},
        )
        oc2 = t2.transform_optimization_config(deepcopy(oc), g, None)

        # Raises error with relative constraint, no status quo
        g = ModelBridge(
            search_space=search_space,
            model=None,
            transforms=[],
            experiment=Experiment(search_space, "test"),
            data=Data(),
        )
        with self.assertRaises(ValueError):
            oc = t.transform_optimization_config(oc, g, None)

        # Raises error with relative constraint, no modelbridge
        with self.assertRaises(ValueError):
            oc = t.transform_optimization_config(oc, None, None)
예제 #14
0
 def testTransformOptimizationConfig(self):
     # basic test
     m1 = Metric(name="m1")
     objective_m1 = Objective(metric=m1, minimize=False)
     oc = OptimizationConfig(objective=objective_m1, outcome_constraints=[])
     tf = PowerTransformY(
         search_space=None,
         observation_features=None,
         observation_data=[self.obsd1, self.obsd2],
         config={"metrics": ["m1"]},
     )
     oc_tf = tf.transform_optimization_config(deepcopy(oc), None, None)
     self.assertEqual(oc_tf, oc)
     # Output constraint on a different metric should not transform the bound
     m2 = Metric(name="m2")
     for bound in [-1.234, 0, 2.345]:
         oc = OptimizationConfig(
             objective=objective_m1,
             outcome_constraints=get_constraint(
                 metric=m2, bound=bound, relative=False
             ),
         )
         oc_tf = tf.transform_optimization_config(deepcopy(oc), None, None)
         self.assertEqual(oc_tf, oc)
     # Output constraint on the same metric should transform the bound
     objective_m2 = Objective(metric=m2, minimize=False)
     for bound in [-1.234, 0, 2.345]:
         oc = OptimizationConfig(
             objective=objective_m2,
             outcome_constraints=get_constraint(
                 metric=m1, bound=bound, relative=False
             ),
         )
         oc_tf = tf.transform_optimization_config(deepcopy(oc), None, None)
         oc_true = deepcopy(oc)
         tf_bound = (
             tf.power_transforms["m1"].transform(np.array(bound, ndmin=2)).item()
         )
         oc_true.outcome_constraints[0].bound = tf_bound
         self.assertEqual(oc_tf, oc_true)
     # Relative constraints aren't supported
     oc = OptimizationConfig(
         objective=objective_m2,
         outcome_constraints=get_constraint(metric=m1, bound=2.345, relative=True),
     )
     with self.assertRaisesRegex(
         ValueError,
         "PowerTransformY cannot be applied to metric m1 since it is "
         "subject to a relative constraint.",
     ):
         tf.transform_optimization_config(oc, None, None)
     # Support for scalarized outcome constraints isn't implemented
     m3 = Metric(name="m3")
     oc = OptimizationConfig(
         objective=objective_m2,
         outcome_constraints=[
             ScalarizedOutcomeConstraint(
                 metrics=[m1, m3], op=ComparisonOp.GEQ, bound=2.345, relative=False
             )
         ],
     )
     with self.assertRaises(NotImplementedError) as cm:
         tf.transform_optimization_config(oc, None, None)
     self.assertEqual(
         "PowerTransformY cannot be used for metric(s) {'m1'} "
         "that are part of a ScalarizedOutcomeConstraint.",
         str(cm.exception),
     )