code: def rejection_sample(
      gen_unconstrained: Callable[
          [int, int, np.ndarray, Optional[Dict[int, float]]], np.ndarray
      ],
      n: int,
      d: int,
      tunable_feature_indices: np.ndarray,
      linear_constraints: Optional[
          Tuple[np.ndarray, np.ndarray]
      ] = None,
      deduplicate: bool = False,
      max_draws: Optional[int] = None,
      fixed_features: Optional[Dict[int, float]] = None,
      rounding_func: Optional[
          Callable[[np.ndarray], np.ndarray]
      ] = None,
      existing_points: Optional[np.ndarray] = None,
  ) -> Tuple[np.ndarray, int]:
      """Rejection sample in parameter space. Parameter space is typically
      [0, 1] for all tunable parameters.
  
      Models must implement a `gen_unconstrained` method in order to support
      rejection sampling via this utility.
  
      Args:
          gen_unconstrained: A callable that generates unconstrained points in
              the parameter space. This is typically the `_gen_unconstrained` method
              of a `RandomModel`.
          n: Number of samples to generate.
          d: Dimensionality of the parameter space.
          tunable_feature_indices: Indices of the tunable features in the
              parameter space.
          linear_constraints: A tuple of (A, b). For k linear constraints on
              d-dimensional x, A is (k x d) and b is (k x 1) such that
              A x <= b.
          deduplicate: If true, reject points that are duplicates of previously
              generated points. The points are deduplicated after applying the
              rounding function.
          max_draws: Maximum number of attemped draws before giving up.
          fixed_features: A map {feature_index: value} for features that
              should be fixed to a particular value during generation.
          rounding_func: A function that rounds an optimization result
              appropriately (e.g., according to `round-trip` transformations).
          existing_points: A set of previously generated points to use
              for deduplication. These should be provided in the parameter
              space model operates in.
      """
      # We need to perform the round trip transformation on our generated point
      # in order to deduplicate in the original search space.
      # The transformation is applied above.
      if deduplicate and rounding_func is None:
          raise ValueError(
              "Rounding function must be provided for deduplication."
          )
  
      # Rejection sample with parameter constraints.
      points = np.zeros((n, d))
      attempted_draws = 0
      successful_draws = 0
      if max_draws is None:
          max_draws = DEFAULT_MAX_RS_DRAWS
  
      while successful_draws < n and attempted_draws <= max_draws:
          # _gen_unconstrained returns points including fixed features.
          # pyre-ignore: Anonymous function w/ named args.
          point = gen_unconstrained(
              n=1,
              d=d,
              tunable_feature_indices=tunable_feature_indices,
              fixed_features=fixed_features,
          )[0]
  
          # Check parameter constraints, always in raw transformed space.
          if linear_constraints is not None:
              all_constraints_satisfied, _ = check_param_constraints(
                  linear_constraints=linear_constraints, point=point
              )
          else:
              all_constraints_satisfied = True
  
          if all_constraints_satisfied:
              # Apply the rounding function if the point satisfies the linear constraints.
              if rounding_func is not None:
                  # NOTE: This could still fail rounding with a logger warning. But this
                  # should be rare since the point is feasible in the continuous space.
                  point = rounding_func(point)
  
              # Deduplicate: don't add the same point twice.
              duplicate = False
              if deduplicate:
                  if existing_points is not None:
                      prev_points = np.vstack(
                          [
                              points[:successful_draws, :],
                              existing_points,
                          ]
                      )
                  else:
                      prev_points = points[:successful_draws, :]
                  duplicate = check_duplicate(
                      point=point, points=prev_points
                  )
  
              # Add point if valid.
              if not duplicate:
                  points[successful_draws] = point
                  successful_draws += 1
          attempted_draws += 1
  
      if successful_draws < n:
          # Only possible if attempted_draws >= max_draws.
          raise SearchSpaceExhausted(
              f"Rejection sampling error (specified maximum draws ({max_draws}) exhausted"
              f", without finding sufficiently many ({n}) candidates). This likely means "
              "that there are no new points left in the search space."
          )
      else:
          return (points, attempted_draws)
  
imports: from __future__ import annotations
  import itertools
  import warnings
  from typing import Callable, Dict, List, Optional, Protocol, Set, Tuple, Union
  import numpy
  import torch
  from ax.core.search_space import SearchSpaceDigest
  from ax.exceptions.core import SearchSpaceExhausted, UnsupportedError
  from ax.models.types import TConfig
  from ax.utils.common.typeutils import checked_cast
  from botorch.acquisition.risk_measures import RiskMeasureMCObjective
  from torch import Tensor
label_desc: Use image node_id 1 for calling a global function with example usage: rejection_sample(gen_unconstrained, n, d, tunable_feature_indices, linear_constraints, deduplicate, max_draws, fixed_features, rounding_func, existing_points) and returns: points,  attempted_draws