Source code for ax.modelbridge.transforms.derelativize

#!/usr/bin/env python3
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.

from typing import TYPE_CHECKING, Optional

from ax.core.observation import ObservationFeatures
from ax.core.optimization_config import OptimizationConfig
from ax.modelbridge.base import unwrap_observation_data
from ax.modelbridge.transforms.base import Transform
from ax.modelbridge.transforms.ivw import ivw_metric_merge


if TYPE_CHECKING:
    # import as module to make sphinx-autodoc-typehints happy
    from ax import modelbridge as modelbridge_module  # noqa F401  # pragma: no cover


[docs]class Derelativize(Transform): """Changes relative constraints to not-relative constraints using a plug-in estimate of the status quo value. If status quo is in-design, uses model estimate at status quo. If not, uses raw observation at status quo. Transform is done in-place. """
[docs] def transform_optimization_config( self, optimization_config: OptimizationConfig, modelbridge: Optional["modelbridge_module.base.ModelBridge"], fixed_features: ObservationFeatures, ) -> OptimizationConfig: has_relative_constraint = any( c.relative for c in optimization_config.outcome_constraints ) if not has_relative_constraint: return optimization_config # Else, we have at least one relative constraint. # Estimate the value at the status quo. if modelbridge is None: raise ValueError("ModelBridge not supplied to transform.") if modelbridge.status_quo is None: raise ValueError( "Optimization config has relative constraint, but model was " "not fit with status quo." ) try: # pyre-fixme[16]: `Optional` has no attribute `features`. f, _ = modelbridge.predict([modelbridge.status_quo.features]) except Exception: # Check if it is out-of-design. if not modelbridge.model_space.check_membership( modelbridge.status_quo.features.parameters ): # Out-of-design: use the raw observation sq_data = ivw_metric_merge( # pyre-fixme[16]: `Optional` has no attribute `data`. obsd=modelbridge.status_quo.data, conflicting_noiseless="raise", ) f, _ = unwrap_observation_data([sq_data]) else: # Should have worked. raise # pragma: no cover # Plug in the status quo value to each relative constraint. for c in optimization_config.outcome_constraints: if c.relative: # Compute new bound. c.bound = (1 + c.bound / 100.0) * f[c.metric.name][0] c.relative = False return optimization_config