class_docstr: Directs the execution of a pipeline engine by generating sequences of
  :class:`PipeInstruction`.
  
  Schedules are generators that yield sequences of
  :class:`PipeInstruction` to process the micro-batches in one batch.
  Each yielded step is atomic in the sense that a barrier
  synchronization can be placed between successive steps without
  deadlock.
  
  Below is an example schedule that implements data parallelism with gradient accumulation:
  
  .. code-block:: python
  
      class DataParallelSchedule(PipeSchedule):
          def steps(self):
              for step_id in range(self.micro_batches):
                  cmds = [
                      LoadMicroBatch(buffer_id=0),
                      ForwardPass(buffer_id=0),
                      BackwardPass(buffer_id=0),
                  ]
                  if step_id == self.micro_batches - 1:
                      cmds.extend([
                          ReduceGrads(),
                          OptimizerStep(),
                      ])
                  yield cmds
  
          def num_pipe_buffers(self):
              return 1
  
  Args:
      micro_batches (int): The number of micro-batches that comprise a batch.
      stages (int): The number of pipeline stages.
      stage_id (int): The pipe stage that will execute the generated schedule.
class_docstr_tok: ["Directs","the","execution","of","a","pipeline","engine","by","generating","sequences","of",":","class",":","`","PipeInstruction","`",".","Schedules","are","generators","that","yield","sequences","of",":","class",":","`","PipeInstruction","`","to","process","the","micro-batches","in","one","batch",".","Each","yielded","step","is","atomic","in","the","sense","that","a","barrier","synchronization","can","be","placed","between","successive","steps","without","deadlock",".","Below","is","an","example","schedule","that","implements","data","parallelism","with","gradient","accumulation",":","..","code-block",":",":","python","class","DataParallelSchedule","(","PipeSchedule",")",":","def","steps","(","self",")",":","for","step_id","in","range","(","self.micro_batches",")",":","cmds","=","[","LoadMicroBatch","(","buffer_id=0",")",",","ForwardPass","(","buffer_id=0",")",",","BackwardPass","(","buffer_id=0",")",",","]","if","step_id","==","self.micro_batches","-","1",":","cmds.extend","(","[","ReduceGrads","(",")",",","OptimizerStep","(",")",",","]",")","yield","cmds","def","num_pipe_buffers","(","self",")",":","return","1","Args",":","micro_batches","(","int",")",":","The","number","of","micro-batches","that","comprise","a","batch",".","stages","(","int",")",":","The","number","of","pipeline","stages",".","stage_id","(","int",")",":","The","pipe","stage","that","will","execute","the","generated","schedule","."]
docstr: Yield a list of :class:`PipeInstruction` for each step in the schedule.
  
  .. note::
      Schedules must implement ``steps()`` to define the schedule.
  
  Returns:
      Instructions to be executed as one step of the pipeline
code: def steps(self):
      """Yield a list of :class:`PipeInstruction` for each step in the schedule.
  
      .. note::
          Schedules must implement ``steps()`` to define the schedule.
  
      Returns:
          Instructions to be executed as one step of the pipeline
      """
      pass
  
imports: from ..utils import call_to_str
  from abc import ABC, abstractmethod
label_desc: Use image node_id 2 for calling the PipeSchedule obj's underlying member method code with example usage: obj.steps() without return types