class_docstr: The Knowledge Gradient with one shot optimization.
  
  Args:
      cost_intercept: The cost intercept for the affine cost of the form
          `cost_intercept + n`, where `n` is the number of generated points.
          Only used for multi-fidelity optimzation (i.e., if fidelity_features
          are present).
      linear_truncated: If `False`, use an alternate downsampling + exponential
          decay Kernel instead of the default `LinearTruncatedFidelityKernel`
          (only relevant for multi-fidelity optimization).
      kwargs: Model-specific kwargs.
class_docstr_tok: ["The","Knowledge","Gradient","with","one","shot","optimization",".","Args",":","cost_intercept",":","The","cost","intercept","for","the","affine","cost","of","the","form","`","cost_intercept","+","n","`",",","where","`","n","`","is","the","number","of","generated","points",".","Only","used","for","multi-fidelity","optimzation","(","i.e.",",","if","fidelity_features","are","present",")",".","linear_truncated",":","If","`","False","`",",","use","an","alternate","downsampling","+","exponential","decay","Kernel","instead","of","the","default","`","LinearTruncatedFidelityKernel","`","(","only","relevant","for","multi-fidelity","optimization",")",".","kwargs",":","Model-specific","kwargs","."]
docstr: Generate new candidates.
  
  Args:
      n: Number of candidates to generate.
      search_space_digest: A SearchSpaceDigest object containing metadata
          about the search space (e.g. bounds, parameter types).
      torch_opt_config: A TorchOptConfig object containing optimization
          arguments (e.g., objective weights, constraints).
  
  Returns:
      A TorchGenResults container, containing
  
      - (n x d) tensor of generated points.
      - n-tensor of weights for each point.
      - Dictionary of model-specific metadata for the given
          generation candidates.
docstr_tok: ["Generate","new","candidates",".","Args",":","n",":","Number","of","candidates","to","generate",".","search_space_digest",":","A","SearchSpaceDigest","object","containing","metadata","about","the","search","space","(","e.g",".","bounds",",","parameter","types",")",".","torch_opt_config",":","A","TorchOptConfig","object","containing","optimization","arguments","(","e.g.",",","objective","weights",",","constraints",")",".","Returns",":","A","TorchGenResults","container",",","containing","-","(","n","x","d",")","tensor","of","generated","points",".","-","n-tensor","of","weights","for","each","point",".","-","Dictionary","of","model-specific","metadata","for","the","given","generation","candidates","."]
code: def gen(
      self,
      n: int,
      search_space_digest: SearchSpaceDigest,
      torch_opt_config: TorchOptConfig,
  ) -> TorchGenResults:
      r"""Generate new candidates.
  
      Args:
          n: Number of candidates to generate.
          search_space_digest: A SearchSpaceDigest object containing metadata
              about the search space (e.g. bounds, parameter types).
          torch_opt_config: A TorchOptConfig object containing optimization
              arguments (e.g., objective weights, constraints).
  
      Returns:
          A TorchGenResults container, containing
  
          - (n x d) tensor of generated points.
          - n-tensor of weights for each point.
          - Dictionary of model-specific metadata for the given
              generation candidates.
      """
      options = torch_opt_config.model_gen_options or {}
      acf_options = options.get("acquisition_function_kwargs", {})
      optimizer_options = options.get("optimizer_kwargs", {})
  
      X_pending, X_observed = _get_X_pending_and_observed(
          Xs=self.Xs,
          objective_weights=torch_opt_config.objective_weights,
          bounds=search_space_digest.bounds,
          pending_observations=torch_opt_config.pending_observations,
          outcome_constraints=torch_opt_config.outcome_constraints,
          linear_constraints=torch_opt_config.linear_constraints,
          fixed_features=torch_opt_config.fixed_features,
      )
  
      # subset model only to the outcomes we need for the optimization
      model = not_none(self.model)
      if options.get("subset_model", True):
          subset_model_results = subset_model(
              model=model,
              objective_weights=torch_opt_config.objective_weights,
              outcome_constraints=torch_opt_config.outcome_constraints,
          )
          model = subset_model_results.model
          objective_weights = subset_model_results.objective_weights
          outcome_constraints = subset_model_results.outcome_constraints
      else:
          objective_weights = torch_opt_config.objective_weights
          outcome_constraints = torch_opt_config.outcome_constraints
  
      (
          objective,
          posterior_transform,
      ) = get_botorch_objective_and_transform(
          botorch_acqf_class=qKnowledgeGradient,
          model=model,
          objective_weights=objective_weights,
          outcome_constraints=outcome_constraints,
          X_observed=X_observed,
      )
  
      inequality_constraints = _to_inequality_constraints(
          torch_opt_config.linear_constraints
      )
      # TODO: update optimizers to handle inequality_constraints
      if inequality_constraints is not None:
          raise UnsupportedError(
              "Inequality constraints are not yet supported for KnowledgeGradient!"
          )
  
      # extract a few options
      n_fantasies = acf_options.get("num_fantasies", 64)
      qmc = acf_options.get("qmc", True)
      seed_inner = acf_options.get("seed_inner", None)
      num_restarts = optimizer_options.get("num_restarts", 40)
      raw_samples = optimizer_options.get("raw_samples", 1024)
  
      # get current value
      current_value = self._get_current_value(
          model=model,
          search_space_digest=search_space_digest,
          torch_opt_config=dataclasses.replace(
              torch_opt_config,
              objective_weights=objective_weights,
              outcome_constraints=outcome_constraints,
          ),
          X_observed=not_none(X_observed),
          seed_inner=seed_inner,
          qmc=qmc,
      )
  
      bounds_ = torch.tensor(
          search_space_digest.bounds,
          dtype=self.dtype,
          device=self.device,
      )
      bounds_ = bounds_.transpose(0, 1)
  
      target_fidelities = {
          k: v
          for k, v in search_space_digest.target_values.items()
          if k in search_space_digest.fidelity_features
      }
      # get acquisition function
      acq_function = _instantiate_KG(
          model=model,
          objective=objective,
          posterior_transform=posterior_transform,
          qmc=qmc,
          n_fantasies=n_fantasies,
          num_trace_observations=options.get(
              "num_trace_observations", 0
          ),
          mc_samples=acf_options.get("mc_samples", 256),
          seed_inner=seed_inner,
          seed_outer=acf_options.get("seed_outer", None),
          X_pending=X_pending,
          target_fidelities=target_fidelities,
          fidelity_weights=options.get("fidelity_weights"),
          current_value=current_value,
          cost_intercept=self.cost_intercept,
      )
  
      # optimize and get new points
      new_x = _optimize_and_get_candidates(
          acq_function=acq_function,
          bounds_=bounds_,
          n=n,
          num_restarts=num_restarts,
          raw_samples=raw_samples,
          optimizer_options=optimizer_options,
          rounding_func=torch_opt_config.rounding_func,
          inequality_constraints=inequality_constraints,
          fixed_features=torch_opt_config.fixed_features,
      )
  
      return TorchGenResults(
          points=new_x, weights=torch.ones(n, dtype=self.dtype)
      )
  
imports: import dataclasses
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
  import torch
  from ax.core.search_space import SearchSpaceDigest
  from ax.models.torch.botorch import BotorchModel, get_rounding_func
  from ax.models.torch.botorch_defaults import recommend_best_out_of_sample_point
  from ax.models.torch.utils import _get_X_pending_and_observed, _to_inequality_constraints, get_botorch_objective_and_transform, get_out_of_sample_best_point_acqf, subset_model
  from ax.models.torch_base import TorchGenResults, TorchOptConfig
  from ax.utils.common.typeutils import not_none
  from botorch.acquisition.acquisition import AcquisitionFunction
  from botorch.acquisition.cost_aware import InverseCostWeightedUtility
  from botorch.acquisition.knowledge_gradient import qKnowledgeGradient, qMultiFidelityKnowledgeGradient
  from botorch.acquisition.objective import MCAcquisitionObjective, PosteriorTransform
  from botorch.acquisition.utils import expand_trace_observations, project_to_target_fidelity
  from botorch.exceptions.errors import UnsupportedError
  from botorch.models.cost import AffineFidelityCostModel
  from botorch.models.model import Model
  from botorch.optim.initializers import gen_one_shot_kg_initial_conditions
  from botorch.optim.optimize import optimize_acqf
  from botorch.sampling.normal import IIDNormalSampler, SobolQMCNormalSampler
  from torch import Tensor
label_desc: Use image node_id 2 for calling the KnowledgeGradient obj's underlying member method code with example usage: obj.gen(n, search_space_digest, torch_opt_config) and returns: TorchGenResults