class_docstr: Multi-task Sampler with different task sampling probabilities over time
  
  We describe the individual unnormalized probabilities using numexpr expressions,
  using t as the variable, e.g.:
  * 1             (constant)
  * 2 * t         (linear)
  * 1/sqrt(t)     (inverse square-root)
  
  These are computed for all tasks for each time step, and then normalized to sum to 1.
  
  Attributes:
      task_dict: Dictionary of tasks
      rng: Random seed, or NumPy RandomState for sampling
      task_to_unnormalized_prob_funcs_dict: map from task names to strings, which are
                                            numexpr expressions
      max_steps: Maximum number of steps allows (in the case where some functions
                 are not valid after a given t.
code: def get_task_p(self, steps=None) -> np.ndarray:
      p_ls = np.empty(len(self.task_names))
  
      # t is the variable in the numexpr expression
      t = steps if steps is not None else self.steps
  
      for i, task_name in enumerate(self.task_names):
          p_ls[i] = numexpr.evaluate(
              self.task_to_unnormalized_prob_funcs_dict[task_name],
              local_dict={"t": t},
          )
      p_ls /= p_ls.sum()
      return p_ls
  
imports: import abc
  import numexpr
  import numpy
  from typing import Union, Optional, Dict
label_desc: Use image node_id 3 for calling the TimeDependentProbMultiTaskSampler obj's underlying member method code with example usage: obj.get_task_p(steps) and returns: p_ls