code: def interactive_optimize(
      num_trials: int,
      candidate_queue_maxsize: int,
      candidate_generator_function: Callable[..., None],
      data_attacher_function: Callable[..., None],
      # pyre-ignore[2]: Missing parameter annotation
      elicitation_function: Callable[..., Any],
      candidate_generator_kwargs: Optional[Dict[str, Any]] = None,
      data_attacher_kwargs: Optional[Dict[str, Any]] = None,
      elicitation_function_kwargs: Optional[Dict[str, Any]] = None,
  ) -> bool:
      """
      Function to facilitate running Ax experiments with candidate pregeneration (the
      generation of candidate points while waiting for trial evaluation). This can be
      useful in many contexts, especially in interactive experiments in which trial
      evaluation entails eliciting a response from a human. Candidate pregeneration
      uses the time waiting for trail evaluation to generate new candidates from the
      data available. Note that this is a tradeoff -- a larger pregeneration queue
      will result in more trials being generated with less data compared to a smaller
      pregeneration queue (or no pregeneration as in conventional Ax usage) and should
      only be used in contexts where it is necessary for the user to not experience any
      "lag" while candidates are being generated.
  
      Args:
          num_trials: The total number of trials to be run.
          candidate_queue_maxsize: The maximum number of candidates to pregenerate.
          candidate_generator_function: A function taking in a queue and event that
              enqueues candidates (generated by any means). See
              `ax_client_candidate_generator` for an example.
          data_attacher_function: A function taking in a queue and event that attaches
              observations to Ax. See `ax_client_data_attacher` for an example.
          elicitation_function: Function from parameterization (as returned by
              `AxClient.get_next_trial`) to outcome (as expected by
              `AxClient.complete_trial`). If None, elicitation is aborted by the user.
          candidate_generator_kwargs: kwargs to be passed into
              `candidate_generator_function` when it is spawned as a thread.
          data_attacher_kwargs: kwargs to be passed into `data_attacher_function` when
              it is spawned as a thread.
          elicitation_function_kwargs: kwargs to be passed into `elicitation_function`
  
      Returns:
          True if optimization was completed and False if aborted.
      """
      optimization_completed = True
  
      # Construct queues to buffer arbitrary inputs and outputs
      candidate_queue = Queue(maxsize=candidate_queue_maxsize)
      data_queue = Queue()
  
      # Construct events to allow us to stop the generator and attacher threads
      candidate_generator_stop_event = Event()
      data_attacher_stop_event = Event()
  
      # Construct threads to run candidate thread-safe pregeneration and thread-safe
      # data attaching respectively
      candidate_generator_thread = Thread(
          target=candidate_generator_function,
          args=(
              candidate_queue,
              candidate_generator_stop_event,
              num_trials,
          ),
          kwargs=(candidate_generator_kwargs or {}),
      )
      data_attacher_thread = Thread(
          target=data_attacher_function,
          args=(
              data_queue,
              data_attacher_stop_event,
          ),
          kwargs=(data_attacher_kwargs or {}),
      )
  
      candidate_generator_thread.start()
      data_attacher_thread.start()
  
      for _i in range(num_trials):
          candidate_item = candidate_queue.get()
  
          response = elicitation_function(
              candidate_item, **(elicitation_function_kwargs or {})
          )
  
          candidate_queue.task_done()
          if response is not None:
              data_queue.put(response)
          else:
              # if resopnse is None, abort the optimization
              optimization_completed = False
              break
  
      # Clean up threads (if they have not been stopped already)
      candidate_generator_stop_event.set()
      data_queue.join()
      data_attacher_stop_event.set()
  
      return optimization_completed
  
file_path: Ax/ax/service/interactive_loop.py
imports: import time
  from logging import Logger
  from queue import Queue
  from threading import Event, Lock, Thread
  from typing import Any, Callable, Dict, Optional, Tuple
  from ax.core.types import TEvaluationOutcome, TParameterization
  from ax.exceptions.core import DataRequiredError
  from ax.exceptions.generation_strategy import MaxParallelismReachedException
  from ax.service.ax_client import AxClient
  from ax.utils.common.logger import get_logger
label_desc: Use image node_id 1 for calling a global function with example usage: interactive_optimize(num_trials, candidate_queue_maxsize, candidate_generator_function, data_attacher_function, elicitation_function, candidate_generator_kwargs, data_attacher_kwargs, elicitation_function_kwargs) and returns: optimization_completed