<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def MultifactorSchedule(history=None, factors="constant * linear_warmup * rsqrt_decay", constant=0.1, warmup_steps=100, decay_factor=0.5, steps_per_decay=20000):
 """Factor-based learning rate schedule. Interprets factors in the factors string which can consist of: * constant: interpreted as the constant value, * linear_warmup: interpreted as linear warmup until warmup_steps, * rsqrt_decay: divide by square root of max(step, warmup_steps) * decay_every: Every k steps decay the learning rate by decay_factor. Args: history: the history of training and evaluation (History object). factors: a string with factors separated by "*" that defines the schedule. constant: float, the starting constant for the learning rate schedule. warmup_steps: how many steps to warm up for in the warmup schedule. decay_factor: The amount to decay the learning rate by. steps_per_decay: How often to decay the learning rate. Returns: a function learning_rate(step):
 float -> float, the step-dependent lr. """

  del history

  cache_args = (factors, constant, warmup_steps)
  if cache_args in _memoized_multifactor_schedules:
    return _memoized_multifactor_schedules[cache_args]

  factors = [n.strip() for n in factors.split("*")]

  def learning_rate(step):  # pylint: disable=invalid-name
    """Step to learning rate function."""
    ret = 1.0
    for name in factors:
      if name == "constant":
        ret *= constant
      elif name == "linear_warmup":
        ret *= np.minimum(1.0, step / warmup_steps)
      elif name == "rsqrt_decay":
        ret /= np.sqrt(np.maximum(step, warmup_steps))
      elif name == "decay_every":
        ret *= (decay_factor ** (step//steps_per_decay))
      else:
        raise ValueError("Unknown factor %s." % name)
    return ret

  _memoized_multifactor_schedules[cache_args] = learning_rate
  return learning_rate