code: def _unroll_plate(factors, var_to_ordinal, sum_vars, plate, step):
      # size of the plate
      size = next(
          iter(
              f.inputs[plate].size for f in factors if plate in f.inputs
          )
      )
      # history of the plate
      history = 1 if step else 0
  
      # replicated variables
      plate_vars = set()
      for var, ordinal in var_to_ordinal.items():
          if plate in ordinal:
              plate_vars.add(var)
  
      # make sure that all vars in the plate are being unrolled
      assert plate_vars.issubset(sum_vars)
  
      # unroll variables
      for var in plate_vars:
          sum_vars -= frozenset({var})
          if var in step.keys():
              new_var = frozenset(
                  {
                      "{}_{}".format(var.split("_")[0], i)
                      for i in range(size)
                  }
              )
          elif var in step.values():
              new_var = frozenset(
                  {
                      "{}_{}".format(var.split("_")[0], i + history)
                      for i in range(size)
                  }
              )
          else:
              new_var = frozenset(
                  {
                      "{}_{}".format(var, i + history)
                      for i in range(size)
                  }
              )
          sum_vars |= new_var
          ordinal = var_to_ordinal.pop(var)
          new_ordinal = ordinal.difference({plate})
          var_to_ordinal.update({v: new_ordinal for v in new_var})
  
      # unroll factors
      unrolled_factors = []
      for factor in factors:
          if plate in factor.inputs:
              f_vars = plate_vars.intersection(factor.inputs)
              prev_to_var = {
                  key: key.split("_")[0] for key in step.keys()
              }
              curr_to_var = {
                  value: value.split("_")[0] for value in step.values()
              }
              nonmarkov_vars = (
                  f_vars - set(step.keys()) - set(step.values())
              )
              unrolled_factors.extend(
                  [
                      factor(
                          **{plate: i},
                          **{
                              var: "{}_{}".format(var, i + history)
                              for var in nonmarkov_vars
                          },
                          **{
                              curr: "{}_{}".format(var, i + history)
                              for curr, var in curr_to_var.items()
                          },
                          **{
                              prev: "{}_{}".format(var, i)
                              for prev, var in prev_to_var.items()
                          },
                      )
                      for i in range(size)
                  ]
              )
          else:
              unrolled_factors.append(factor)
  
      return unrolled_factors, var_to_ordinal, sum_vars
  
imports: import re
  from collections import OrderedDict, defaultdict
  from functools import reduce
  from math import gcd
  import funsor
  import funsor.ops
  from funsor.cnf import Contraction
  from funsor.domains import Bint, Reals
  from funsor.interpreter import gensym
  from funsor.ops import PRODUCT_TO_POWER, UNITS, AssociativeOp
  from funsor.terms import Cat, Funsor, FunsorMeta, Number, Slice, Stack, Subs, Variable, eager, substitute, to_funsor
  from funsor.util import quote
label_desc: Use image node_id 2 for calling a global function with example usage: _unroll_plate(factors, var_to_ordinal, sum_vars, plate, step) and returns: unrolled_factors,  var_to_ordinal,  sum_vars