code: def optimize_acqf_mixed(
      acq_function: AcquisitionFunction,
      bounds: Tensor,
      q: int,
      num_restarts: int,
      fixed_features_list: List[Dict[int, float]],
      raw_samples: Optional[int] = None,
      options: Optional[Dict[str, Union[bool, float, int, str]]] = None,
      inequality_constraints: Optional[
          List[Tuple[Tensor, Tensor, float]]
      ] = None,
      equality_constraints: Optional[
          List[Tuple[Tensor, Tensor, float]]
      ] = None,
      nonlinear_inequality_constraints: Optional[
          List[Tuple[Callable, bool]]
      ] = None,
      post_processing_func: Optional[Callable[[Tensor], Tensor]] = None,
      batch_initial_conditions: Optional[Tensor] = None,
      ic_generator: Optional[TGenInitialConditions] = None,
      ic_gen_kwargs: Optional[Dict] = None,
      **kwargs: Any,
  ) -> Tuple[Tensor, Tensor]:
      r"""Optimize over a list of fixed_features and returns the best solution.
  
      This is useful for optimizing over mixed continuous and discrete domains.
      For q > 1 this function always performs sequential greedy optimization (with
      proper conditioning on generated candidates).
  
      Args:
          acq_function: An AcquisitionFunction
          bounds: A `2 x d` tensor of lower and upper bounds for each column of `X`
              (if inequality_constraints is provided, these bounds can be -inf and
              +inf, respectively).
          q: The number of candidates.
          num_restarts:  Number of starting points for multistart acquisition
              function optimization.
          raw_samples: Number of samples for initialization. This is required
              if `batch_initial_conditions` is not specified.
          fixed_features_list: A list of maps `{feature_index: value}`. The i-th
              item represents the fixed_feature for the i-th optimization.
          options: Options for candidate generation.
          inequality constraints: A list of tuples (indices, coefficients, rhs),
              with each tuple encoding an inequality constraint of the form
              `\sum_i (X[indices[i]] * coefficients[i]) >= rhs`
          equality constraints: A list of tuples (indices, coefficients, rhs),
              with each tuple encoding an inequality constraint of the form
              `\sum_i (X[indices[i]] * coefficients[i]) = rhs`
          nonlinear_inequality_constraints: A list of tuples representing the nonlinear
              inequality constraints. The first element in the tuple is a callable
              representing a constraint of the form `callable(x) >= 0`. In case of an
              intra-point constraint, `callable()`takes in an one-dimensional tensor of
              shape `d` and returns a scalar. In case of an inter-point constraint,
              `callable()` takes a two dimensional tensor of shape `q x d` and again
              returns a scalar. The second element is a boolean, indicating if it is an
              intra-point or inter-point constraint (`True` for intra-point. `False` for
              inter-point). For more information on intra-point vs inter-point
              constraints, see the docstring of the `inequality_constraints` argument to
              `optimize_acqf()`. The constraints will later be passed to the scipy
              solver. You need to pass in `batch_initial_conditions` in this case.
              Using non-linear inequality constraints also requires that `batch_limit`
              is set to 1, which will be done automatically if not specified in
              `options`.
          post_processing_func: A function that post-processes an optimization
              result appropriately (i.e., according to `round-trip`
              transformations).
          batch_initial_conditions: A tensor to specify the initial conditions. Set
              this if you do not want to use default initialization strategy.
          ic_generator: Function for generating initial conditions. Not needed when
              `batch_initial_conditions` are provided. Defaults to
              `gen_one_shot_kg_initial_conditions` for `qKnowledgeGradient` acquisition
              functions and `gen_batch_initial_conditions` otherwise. Must be specified
              for nonlinear inequality constraints.
          ic_gen_kwargs: Additional keyword arguments passed to function specified by
              `ic_generator`
          kwargs: kwargs do nothing. This is provided so that the same arguments can
              be passed to different acquisition functions without raising an error.
  
      Returns:
          A two-element tuple containing
  
          - a `q x d`-dim tensor of generated candidates.
          - an associated acquisition value.
      """
      if not fixed_features_list:
          raise ValueError("fixed_features_list must be non-empty.")
  
      if isinstance(acq_function, OneShotAcquisitionFunction):
          if not hasattr(acq_function, "evaluate") and q > 1:
              raise ValueError(
                  "`OneShotAcquisitionFunction`s that do not implement `evaluate` "
                  "are currently not supported when `q > 1`. This is needed to "
                  "compute the joint acquisition value."
              )
      _raise_deprecation_warning_if_kwargs(
          "optimize_acqf_mixed", kwargs
      )
  
      ic_gen_kwargs = ic_gen_kwargs or {}
  
      if q == 1:
          ff_candidate_list, ff_acq_value_list = [], []
          for fixed_features in fixed_features_list:
              candidate, acq_value = optimize_acqf(
                  acq_function=acq_function,
                  bounds=bounds,
                  q=q,
                  num_restarts=num_restarts,
                  raw_samples=raw_samples,
                  options=options or {},
                  inequality_constraints=inequality_constraints,
                  equality_constraints=equality_constraints,
                  nonlinear_inequality_constraints=nonlinear_inequality_constraints,
                  fixed_features=fixed_features,
                  post_processing_func=post_processing_func,
                  batch_initial_conditions=batch_initial_conditions,
                  ic_generator=ic_generator,
                  return_best_only=True,
                  **ic_gen_kwargs,
              )
              ff_candidate_list.append(candidate)
              ff_acq_value_list.append(acq_value)
  
          ff_acq_values = torch.stack(ff_acq_value_list)
          best = torch.argmax(ff_acq_values)
          return ff_candidate_list[best], ff_acq_values[best]
  
      # For batch optimization with q > 1 we do not want to enumerate all n_combos^n
      # possible combinations of discrete choices. Instead, we use sequential greedy
      # optimization.
      base_X_pending = acq_function.X_pending
      candidates = torch.tensor(
          [], device=bounds.device, dtype=bounds.dtype
      )
  
      for _ in range(q):
          candidate, acq_value = optimize_acqf_mixed(
              acq_function=acq_function,
              bounds=bounds,
              q=1,
              num_restarts=num_restarts,
              raw_samples=raw_samples,
              fixed_features_list=fixed_features_list,
              options=options or {},
              inequality_constraints=inequality_constraints,
              equality_constraints=equality_constraints,
              nonlinear_inequality_constraints=nonlinear_inequality_constraints,
              post_processing_func=post_processing_func,
              batch_initial_conditions=batch_initial_conditions,
              ic_generator=ic_generator,
              ic_gen_kwargs=ic_gen_kwargs,
          )
          candidates = torch.cat([candidates, candidate], dim=-2)
          acq_function.set_X_pending(
              torch.cat([base_X_pending, candidates], dim=-2)
              if base_X_pending is not None
              else candidates
          )
  
      acq_function.set_X_pending(base_X_pending)
  
      # compute joint acquisition value
      if isinstance(acq_function, OneShotAcquisitionFunction):
          acq_value = acq_function.evaluate(X=candidates, bounds=bounds)
      else:
          acq_value = acq_function(candidates)
      return candidates, acq_value
  
imports: from __future__ import annotations
  import dataclasses
  import warnings
  from typing import Any, Callable, Dict, List, Optional, Tuple, Union
  import torch
  from botorch.acquisition.acquisition import AcquisitionFunction, OneShotAcquisitionFunction
  from botorch.acquisition.knowledge_gradient import qKnowledgeGradient
  from botorch.acquisition.multi_objective.hypervolume_knowledge_gradient import qHypervolumeKnowledgeGradient
  from botorch.exceptions import InputDataError, UnsupportedError
  from botorch.exceptions.warnings import OptimizationWarning
  from botorch.generation.gen import gen_candidates_scipy, TGenCandidates
  from botorch.logging import logger
  from botorch.optim.initializers import gen_batch_initial_conditions, gen_one_shot_hvkg_initial_conditions, gen_one_shot_kg_initial_conditions, TGenInitialConditions
  from botorch.optim.stopping import ExpMAStoppingCriterion
  from botorch.optim.utils import _filter_kwargs
  from torch import Tensor
label_desc: Use image node_id 10 for calling a global function with example usage: optimize_acqf_mixed(acq_function, bounds, q, num_restarts, fixed_features_list, raw_samples, options, inequality_constraints, equality_constraints, nonlinear_inequality_constraints, post_processing_func, batch_initial_conditions, ic_generator, ic_gen_kwargs) and returns: candidates,  acq_value, ff_candidate_list,  ff_acq_values