code: def get_schedule_with_warmup_and_decay(
      config: LRSchedulerConfig,
      optimizer: Optimizer,
      step_info: StepInfo,
  ) -> LambdaLR:
      """Creates a learning rate scheduler that updates each training step."""
      schedulers = []
  
      # Warmup scheduler.
      if step_info.num_warmup_steps > 0:
          warmup_scheduler = LambdaLR(
              optimizer,
              lambda current_step: float(current_step)
              / float(max(1, step_info.num_warmup_steps)),
          )
          schedulers.append(warmup_scheduler)
  
      # Decay scheduler.
      decay = config.decay
      decay_scheduler = decay_registry[decay](
          config, optimizer, step_info
      )
      schedulers.append(decay_scheduler)
  
      if len(schedulers) == 1:
          # Only one scheduler, so no need to wrap in a SequentialLR.
          return schedulers[0]
  
      # Return a SequentialLR that applies the warmup and decay schedulers in order
      # with the warmup scheduler only applied for the first num_warmup_steps steps.
      return SequentialLR(
          optimizer,
          schedulers=schedulers,
          milestones=[step_info.num_warmup_steps],
      )
  
imports: import logging
  import math
  from typing import Any, Callable, Dict
  from torch.optim import Optimizer
  from torch.optim.lr_scheduler import CosineAnnealingWarmRestarts, LambdaLR, ReduceLROnPlateau, SequentialLR
  from ludwig.constants import MINIMIZE, TRAINING, VALIDATION
  from ludwig.modules.metric_registry import get_metric_objective
  from ludwig.schema.lr_scheduler import LRSchedulerConfig
  from ludwig.utils.metric_utils import TrainerMetric
  from ludwig.utils.trainer_utils import ProgressTracker
label_desc: Use image node_id 1 for calling a global function with example usage: get_schedule_with_warmup_and_decay(config, optimizer, step_info) and returns: SequentialLR, schedulers