class_docstr: Sample in a low-dimensional linear embedding, to initialize ALEBO.
  
  Generates points on a linear subspace of [-1, 1]^D by generating points in
  [-b, b]^D, projecting them down with a matrix B, and then projecting them
  back up with the pseudoinverse of B. Thus points thus all lie in a linear
  subspace defined by B. Points whose up-projection falls outside of [-1, 1]^D
  are thrown out, via rejection sampling.
  
  To generate n points, we start with nsamp points in [-b, b]^D, which are
  mapped down to the embedding and back up as described above. If >=n points
  fall within [-1, 1]^D after being mapped up, then the first n are returned.
  If there are less than n points in [-1, 1]^D, then b is constricted
  (halved) and the process is repeated until there are at least n points in
  [-1, 1]^D. There exists a b small enough that all points will project to
  [-1, 1]^D, so this is guaranteed to terminate, typically after few rounds.
  
  Args:
      B: A (dxD) projection down.
      nsamp: Number of samples to use for rejection sampling.
      init_bound: b for the initial sampling space described above.
      seed: seed for UniformGenerator
code: def gen(
      self,
      n: int,
      bounds: List[Tuple[float, float]],
      linear_constraints: Optional[
          Tuple[np.ndarray, np.ndarray]
      ] = None,
      fixed_features: Optional[Dict[int, float]] = None,
      model_gen_options: Optional[TConfig] = None,
      rounding_func: Optional[
          Callable[[np.ndarray], np.ndarray]
      ] = None,
  ) -> Tuple[np.ndarray, np.ndarray]:
      if n > self.nsamp:
          raise ValueError("n > nsamp")
      # The projection is from [-1, 1]^D.
      for b in bounds:
          assert b == (-1.0, 1.0)
      # The following can be easily handled in the future when needed
      assert linear_constraints is None
      assert fixed_features is None
      # Do gen in the high-dimensional space.
      X01, w = super().gen(
          n=self.nsamp,
          bounds=[(0.0, 1.0)] * self.Q.shape[0],
          model_gen_options={"max_rs_draws": self.nsamp},
      )
      finished = False
      b = float(self.init_bound)
      while not finished:
          # Map to [-b, b]
          X_b = 2 * b * X01 - b
          # Project down to B and back up
          X = X_b @ np.transpose(self.Q)
          # Filter out to points in [-1, 1]^D
          X = X[(X >= -1.0).all(axis=1) & (X <= 1.0).all(axis=1)]
          if X.shape[0] >= n:
              finished = True
          else:
              b = b / 2.0  # Constrict the space
      X = X[:n, :]
      return X, np.ones(n)
  
file_path: Ax/ax/models/random/alebo_initializer.py
imports: from typing import Callable, Dict, List, Optional, Tuple
  from warnings import warn
  import numpy
  from ax.models.random.uniform import UniformGenerator
  from ax.models.types import TConfig
  from ax.utils.common.docutils import copy_doc
label_desc: Use image node_id 2 for calling the ALEBOInitializer obj's underlying member method code with example usage: obj.gen(n, bounds, linear_constraints, fixed_features, model_gen_options, rounding_func) and returns: X,  np