code: def test_extract_robust_digest(self) -> None:
      # Test with non-robust search space.
      ss = get_search_space()
      self.assertIsNone(extract_robust_digest(ss, list(ss.parameters)))
      # Test with non-environmental search space.
      for multiplicative in (True, False):
          rss = get_robust_search_space(num_samples=8)
          if multiplicative:
              for p in rss.parameter_distributions:
                  p.multiplicative = True
              rss.multiplicative = True
          robust_digest = not_none(
              extract_robust_digest(rss, list(rss.parameters))
          )
          self.assertEqual(robust_digest.multiplicative, multiplicative)
          self.assertEqual(robust_digest.environmental_variables, [])
          self.assertIsNone(robust_digest.sample_environmental)
          samples = not_none(robust_digest.sample_param_perturbations)()
          self.assertEqual(samples.shape, (8, 4))
          constructor = np.ones if multiplicative else np.zeros
          self.assertTrue(
              np.equal(samples[:, 2:], constructor((8, 2))).all()
          )
          # Exponential distribution is non-negative, so we can check for that.
          self.assertTrue(np.all(samples[:, 1] > 0))
          # Check that it works as expected if param_names is missing some
          # non-distributional parameters.
          robust_digest = not_none(
              extract_robust_digest(rss, list(rss.parameters)[:-1])
          )
          samples = not_none(robust_digest.sample_param_perturbations)()
          self.assertEqual(samples.shape, (8, 3))
          self.assertTrue(
              np.equal(samples[:, 2:], constructor((8, 1))).all()
          )
          self.assertTrue(np.all(samples[:, 1] > 0))
          # Check that it errors if we're missing distributional parameters.
          with self.assertRaisesRegex(
              RuntimeError, "All distributional"
          ):
              extract_robust_digest(rss, list(rss.parameters)[1:])
      # Test with environmental search space.
      all_params = list(rss.parameters.values())
      rss = RobustSearchSpace(
          parameters=all_params[2:],
          parameter_distributions=rss.parameter_distributions,
          num_samples=8,
          environmental_variables=all_params[:2],
      )
      robust_digest = not_none(
          extract_robust_digest(rss, list(rss.parameters))
      )
      self.assertFalse(robust_digest.multiplicative)
      self.assertIsNone(robust_digest.sample_param_perturbations)
      self.assertEqual(
          robust_digest.environmental_variables, ["x", "y"]
      )
      samples = not_none(robust_digest.sample_environmental)()
      self.assertEqual(samples.shape, (8, 2))
      # Both are continuous distributions, should be non-zero.
      self.assertTrue(np.all(samples != 0))
      # Check for error if environmental variables are not at the end.
      with self.assertRaisesRegex(RuntimeError, "last entries"):
          extract_robust_digest(rss, list(rss.parameters)[::-1])
      # Test with mixed search space.
      rss = RobustSearchSpace(
          parameters=all_params[1:],
          parameter_distributions=rss.parameter_distributions,
          num_samples=8,
          environmental_variables=all_params[:1],
      )
      robust_digest = not_none(
          extract_robust_digest(rss, list(rss.parameters))
      )
      self.assertFalse(robust_digest.multiplicative)
      self.assertEqual(
          not_none(robust_digest.sample_param_perturbations)().shape,
          (8, 3),
      )
      self.assertEqual(
          not_none(robust_digest.sample_environmental)().shape, (8, 1)
      )
      self.assertEqual(robust_digest.environmental_variables, ["x"])
  
imports: from dataclasses import dataclass
  from typing import List, Union
  import numpy
  import torch
  from ax.core.metric import Metric
  from ax.core.objective import MultiObjective
  from ax.core.optimization_config import MultiObjectiveOptimizationConfig
  from ax.core.outcome_constraint import ObjectiveThreshold, OutcomeConstraint
  from ax.core.parameter import ChoiceParameter, ParameterType, RangeParameter
  from ax.core.risk_measures import RiskMeasure
  from ax.core.search_space import RobustSearchSpace, SearchSpace
  from ax.core.types import ComparisonOp
  from ax.exceptions.core import UserInputError
  from ax.modelbridge.modelbridge_utils import _array_to_tensor, extract_risk_measure, extract_robust_digest, feasible_hypervolume, process_contextual_datasets, RISK_MEASURE_NAME_TO_CLASS, transform_search_space
  from ax.modelbridge.registry import Cont_X_trans, Y_trans
  from ax.utils.common.testutils import TestCase
  from ax.utils.common.typeutils import not_none
  from ax.utils.testing.core_stubs import get_robust_search_space, get_search_space
  from botorch.acquisition.risk_measures import VaR
  from botorch.utils.datasets import ContextualDataset, SupervisedDataset
label_desc: Use image node_id 3 for calling the TestModelBridgeUtils obj's underlying member method code with example usage: obj.test_extract_robust_digest() without return types