class_docstr: Allocate the time resources when tuning multiple tasks together.
  This implements two strategies: "round-robin" and "gradient".
  
  Parameters
  ----------
  tasks: List[SearchTask]
      All tasks to tune
  task_weights: Optional[List[float]]
      The weights of tasks.
      If provided, the task scheduler will set the objective function to
      sum(weight[t] * latency[t]), where weight[t] is the weight of a task
      and the lantecy[t] is the lantecy of the task.
      If not provided, the task scheduer will assign equal weights to all
      tasks (i.e., the objective function is sum(latency[t])).
  objective_func: Optional[Callable[List[float] -> float]]
      The objective function to be minimized.
      The objective function accepts the current latencies of all tasks and returns the
      objective.
      If not provided, the objective is the weighted sum of the latencies of all tasks.
  strategy: str = "gradient"
      The scheduling strategy.
      "round-robin": Tune tasks in round robin order.
      "gradient" : Tune tasks with gradient descent.
  load_model_file: Optional[str]
      Load pre-trained model from this file. If this is None, the cost model will
      be trained from scratch.
  load_log_file: Optional[str]
      Load measurement records from this file. If it is not None, the status of the
      task scheduler, search policies and cost models will be restored according to this file.
  verbose: int = 1
      The level of verbosity. 0 means silent.
  alpha: float = 0.2
      The parameter used for 'gradient' strategy
  beta: float = 2
      The parameter used for 'gradient' strategy
  backward_window_size: int = 3
      The parameter used for 'gradient' strategy
  callbacks: Optional[List[TaskSchedulerCallback]]
      The task scheduler callbacks that will be called before and after tuning a task.
      If None, PrintTableInfo and LogEstimatedLatency callback will be used.
class_docstr_tok: ["Allocate","the","time","resources","when","tuning","multiple","tasks","together",".","This","implements","two","strategies",":","``","round-robin","''","and","``","gradient","''",".","Parameters","--","--","--","--","--","tasks",":","List","[","SearchTask","]","All","tasks","to","tune","task_weights",":","Optional","[","List","[","float","]","]","The","weights","of","tasks",".","If","provided",",","the","task","scheduler","will","set","the","objective","function","to","sum","(","weight","[","t","]","*","latency","[","t","]",")",",","where","weight","[","t","]","is","the","weight","of","a","task","and","the","lantecy","[","t","]","is","the","lantecy","of","the","task",".","If","not","provided",",","the","task","scheduer","will","assign","equal","weights","to","all","tasks","(","i.e.",",","the","objective","function","is","sum","(","latency","[","t","]",")",")",".","objective_func",":","Optional","[","Callable","[","List","[","float","]","-",">","float","]","]","The","objective","function","to","be","minimized",".","The","objective","function","accepts","the","current","latencies","of","all","tasks","and","returns","the","objective",".","If","not","provided",",","the","objective","is","the","weighted","sum","of","the","latencies","of","all","tasks",".","strategy",":","str","=","``","gradient","''","The","scheduling","strategy",".","``","round-robin","''",":","Tune","tasks","in","round","robin","order",".","``","gradient","''",":","Tune","tasks","with","gradient","descent",".","load_model_file",":","Optional","[","str","]","Load","pre-trained","model","from","this","file",".","If","this","is","None",",","the","cost","model","will","be","trained","from","scratch",".","load_log_file",":","Optional","[","str","]","Load","measurement","records","from","this","file",".","If","it","is","not","None",",","the","status","of","the","task","scheduler",",","search","policies","and","cost","models","will","be","restored","according","to","this","file",".","verbose",":","int","=","1","The","level","of","verbosity",".","0","means","silent",".","alpha",":","float","=","0.2","The","parameter","used","for","'gradient","'","strategy","beta",":","float","=","2","The","parameter","used","for","'gradient","'","strategy","backward_window_size",":","int","=","3","The","parameter","used","for","'gradient","'","strategy","callbacks",":","Optional","[","List","[","TaskSchedulerCallback","]","]","The","task","scheduler","callbacks","that","will","be","called","before","and","after","tuning","a","task",".","If","None",",","PrintTableInfo","and","LogEstimatedLatency","callback","will","be","used","."]
docstr: Tune a batch of tasks together.
  
  Parameters
  ----------
  tune_option: TuningOptions
      The tuning options applied to all tasks.
  search_policy: : Union[str, List[SearchPolicy]] = "default"
      The list of search policies.
      If it is str,
      "default" for the default policy (SketchPolicy + XGBModel),
      "sketch.xgb" for SketchPolicy + XGBModel,
      "sketch.random" for SketchPolicy + RandomModel.
  search_policy_params : Optional[Dict[str, Any]]
      The parameters of the search policy
  adaptive_training : bool = False
      Option used by XGBModel to reduce the model training frequency when there're
      too many logs.
  per_task_early_stopping : Optional[int]
      Stop tuning a task early if getting no improvement after n measurements.
docstr_tok: ["Tune","a","batch","of","tasks","together",".","Parameters","--","--","--","--","--","tune_option",":","TuningOptions","The","tuning","options","applied","to","all","tasks",".","search_policy",":",":","Union","[","str",",","List","[","SearchPolicy","]","]","=","``","default","''","The","list","of","search","policies",".","If","it","is","str",",","``","default","''","for","the","default","policy","(","SketchPolicy","+","XGBModel",")",",","``","sketch.xgb","''","for","SketchPolicy","+","XGBModel",",","``","sketch.random","''","for","SketchPolicy","+","RandomModel",".","search_policy_params",":","Optional","[","Dict","[","str",",","Any","]","]","The","parameters","of","the","search","policy","adaptive_training",":","bool","=","False","Option","used","by","XGBModel","to","reduce","the","model","training","frequency","when","there're","too","many","logs",".","per_task_early_stopping",":","Optional","[","int","]","Stop","tuning","a","task","early","if","getting","no","improvement","after","n","measurements","."]
code: def tune(
      self,
      tune_option,
      search_policy="default",
      search_policy_params=None,
      adaptive_training=False,
      per_task_early_stopping=None,
  ):
      """Tune a batch of tasks together.
  
      Parameters
      ----------
      tune_option: TuningOptions
          The tuning options applied to all tasks.
      search_policy: : Union[str, List[SearchPolicy]] = "default"
          The list of search policies.
          If it is str,
          "default" for the default policy (SketchPolicy + XGBModel),
          "sketch.xgb" for SketchPolicy + XGBModel,
          "sketch.random" for SketchPolicy + RandomModel.
      search_policy_params : Optional[Dict[str, Any]]
          The parameters of the search policy
      adaptive_training : bool = False
          Option used by XGBModel to reduce the model training frequency when there're
          too many logs.
      per_task_early_stopping : Optional[int]
          Stop tuning a task early if getting no improvement after n measurements.
      """
      # init members
      self.tune_option = tune_option
      self.early_stopping_all = (
          1e20
          if tune_option.early_stopping < 0
          else tune_option.early_stopping
      )
      self.early_stopping_task = (
          1e20
          if per_task_early_stopping is None
          else per_task_early_stopping
      )
  
      self.measurer = ProgramMeasurer(
          tune_option.builder,
          tune_option.runner,
          tune_option.measure_callbacks,
          tune_option.verbose,
      )
      self.ct = self.best_ct = 0
      self.tic = time.time()
  
      # reset num_measures_per_round to make sure every task is tuned at least once
      self.num_measures_per_round = min(
          tune_option.num_measures_per_round,
          tune_option.num_measure_trials // len(self.tasks),
      )
      if self.num_measures_per_round <= 0:
          raise ValueError(
              "num_measure_trials is too small. Please set it to a higher value."
              f"It should be at least {len(self.tasks)} for this model."
          )
  
      # restore the status of the task scheduler from a log file
      if self.load_log_file:
          self._restore_status(
              self.load_log_file, self.num_measures_per_round
          )
  
      # make one search policy for one task
      self.search_policies = make_search_policies(
          search_policy,
          search_policy_params,
          self.tasks,
          self.num_measures_per_round,
          tune_option.verbose,
          self.load_model_file,
          self.load_log_file,
          adaptive_training,
      )
  
      # do a round robin first to warm up
      for idx in range(len(self.tasks)):
          # skip warming up this task if it has been tuned before (restored from the log file)
          if not self.task_cts[idx]:
              self._tune_task(idx)
      self.best_ct = self.ct
      self.best_score = self.cur_score
  
      # use the specific strategy to choose workload to tune
      task_idx = -1
      while self.ct < tune_option.num_measure_trials and len(
          self.dead_tasks
      ) < len(self.tasks):
          if self.strategy == "round-robin":
              task_idx = (task_idx + 1) % len(self.tasks)
              while task_idx in self.dead_tasks:
                  task_idx = (task_idx + 1) % len(self.tasks)
          elif self.strategy == "gradient":
              gradients = []
              for i in range(len(self.tasks)):
                  if i in self.dead_tasks:
                      gradients.append(0)
                      continue
  
                  # compute gradient from chain rule : (delta f / delta g_i)
                  delta = 1e-4
                  new_costs = list(self.best_costs)
                  new_costs[i] -= delta
                  chain_grad = (
                      self._compute_score(self.best_costs)
                      - self._compute_score(new_costs)
                  ) / delta
  
                  # compute (g_i(t_i) - g(t_i - \Delta t)) / (\Delta t)
                  if (
                      self.task_cts[i] - 1
                      < len(self.task_costs_history[i])
                      and self.task_cts[i]
                      - 1
                      - self.backward_window_size
                      >= 0
                  ):
                      backward_grad = (
                          self.task_costs_history[i][
                              self.task_cts[i] - 1
                          ]
                          - self.task_costs_history[i][
                              self.task_cts[i]
                              - 1
                              - self.backward_window_size
                          ]
                      ) / self.backward_window_size
                  else:
                      backward_grad = 0
  
                  # compute (g_i(t_i + \Delta t) - g(t_i)) / (\Delta t)
                  g_next_1 = self.best_costs[i] - (
                      self.best_costs[i] / self.task_cts[i]
                  )
  
                  g_next_2 = self.beta * 1e30
                  group_id = self.tag_to_group_id.get(
                      self.task_tags[i], None
                  )
                  if (
                      group_id is not None
                      and len(self.group_task_ids[group_id]) > 1
                  ):
                      best_flops = max(
                          [
                              self.flop_cts[j] / self.best_costs[j]
                              for j in self.group_task_ids[group_id]
                          ]
                      )
                      g_next_2 = (
                          self.beta * self.flop_cts[i] / best_flops
                      )
  
                  g_next = min(g_next_1, g_next_2)
                  forward_grad = g_next - self.best_costs[i]
  
                  # combine all grads
                  grad = chain_grad * (
                      self.alpha * backward_grad
                      + (1 - self.alpha) * forward_grad
                  )
                  assert grad <= 0
                  gradients.append(grad)
  
              if max(gradients) == min(gradients):
                  task_idx = np.random.choice(len(gradients))
              else:
                  task_idx = np.argmin(gradients)
          else:
              raise ValueError("Invalid strategy: " + self.strategy)
  
          self._tune_task(task_idx)
          self._adjust_similarity_group(task_idx)
  
          if self.cur_score < self.best_score:
              self.best_score = self.cur_score
              self.best_ct = self.ct
          elif (
              self.ct - self.best_ct >= self.early_stopping_all
              and all(cost < 1e9 for cost in self.best_costs)
          ):
              if self.tune_option.verbose >= 1:
                  print(
                      "Stop early since no performance improvement in the last "
                      + str(self.early_stopping_all)
                      + " measurement trials."
                  )
              break
  
imports: import os
  import time
  import math
  import logging
  import numpy
  from .search_policy import SearchPolicy, SketchPolicy, PreloadMeasuredStates
  from .cost_model import RandomModel, XGBModel
  from .utils import array_mean
  from .measure import ProgramMeasurer
  from .measure_record import RecordReader
  from .None import _ffi_api
label_desc: Use image node_id 2 for calling the TaskScheduler obj's underlying member method code with example usage: obj.tune(tune_option, search_policy, search_policy_params, adaptive_training, per_task_early_stopping) without return types