code: def _create_rnn_variant(
      cls, init_model, pred_model, n, opset_version
  ):
      assert (
          init_model is not None
      ), "cannot convert RNNs without access to the full model"
      assert (
          pred_model is not None
      ), "cannot convert RNNs without access to the full model"
  
      attrs = dict(n.attrs)  # make a copy, which is safe to mutate
      hidden_size = attrs.pop("hidden_size")
      direction = force_unicode(attrs.pop("direction", "forward"))
  
      if n.op_type == "RNN":
          activation = force_unicode(
              attrs.pop("activations", ("tanh",))[0].lower()
          )
      elif n.op_type == "GRU":
          linear_before_reset = attrs.pop("linear_before_reset", 0)
  
      assert not attrs, "unsupported RNN attributes: " + str(
          attrs.keys()
      )
      assert direction in [
          "forward",
          "bidirectional",
      ], "unsupported backwards RNN/GRU/LSTM"
  
      if n.op_type in ["RNN", "GRU"]:
          input_blob, W, R, B, sequence_lens, initial_h = n.inputs
      elif n.op_type == "LSTM":
          (
              input_blob,
              W,
              R,
              B,
              sequence_lens,
              initial_h,
              initial_c,
          ) = n.inputs
  
      if sequence_lens == "":
          sequence_lens = None
  
      for x in itertools.chain(
          init_model.graph.input,
          init_model.graph.value_info,
          pred_model.graph.input,
          pred_model.graph.value_info,
      ):
          if x.name == W:
              input_size = x.type.tensor_type.shape.dim[2].dim_value
              break
      else:
          raise RuntimeError(
              "best-effort shape inference for RNN/GRU/LSTM failed"
          )
  
      pred_mh = ModelHelper()
      init_net = core.Net("init-net")
  
      init_net.Reshape(W, [W, cls.dummy_name()], shape=[1, -1, 0])
      init_net.Squeeze(W, W, dims=[0])
      init_net.Reshape(R, [R, cls.dummy_name()], shape=[1, -1, 0])
      init_net.Squeeze(R, R, dims=[0])
      init_net.Reshape(B, [B, cls.dummy_name()], shape=[1, -1])
      init_net.Squeeze(B, B, dims=[0])
  
      if n.op_type == "RNN":
  
          def reform(*args):
              pass
  
          def make_cell(*args, **kwargs):
              return rnn_cell.BasicRNN(
                  *args, activation=activation, **kwargs
              )
  
          def make_rnn(direction_offset):
              return cls._make_rnn_direction(
                  input_blob,
                  B,
                  W,
                  R,
                  [(initial_h, "/initial_h")],
                  sequence_lens,
                  pred_mh,
                  init_net,
                  input_size,
                  hidden_size,
                  1,
                  direction_offset,
                  "/i2h_b",
                  "/gates_t_b",
                  "/i2h_w",
                  "/gates_t_w",
                  reform,
                  make_cell,
                  lambda x: x,
              )
  
      elif n.op_type == "GRU":
  
          def reform(Bi, Br, W_, R_, name, hidden_size, init_net):
              # caffe2 has a different order from onnx. We need to rearrange
              #  z r h  -> r z h
              reforms = (
                  (W_, "i2h_w", True, [(0, -1)]),
                  (R_, "gate_t_w", False, [(0, -1)]),
                  (Bi, "i2h_b", True, []),
                  (Br, "gate_t_b", False, []),
              )
              cls._rnn_reform_weights(
                  reforms,
                  name,
                  hidden_size,
                  init_net,
                  ["update", "reset", "output"],
                  [1, 0, 2],
              )
  
          def make_cell(*args, **kwargs):
              return gru_cell.GRU(
                  *args,
                  linear_before_reset=linear_before_reset,
                  **kwargs,
              )
  
          def make_rnn(direction_offset):
              return cls._make_rnn_direction(
                  input_blob,
                  B,
                  W,
                  R,
                  [(initial_h, "/initial_h")],
                  sequence_lens,
                  pred_mh,
                  init_net,
                  input_size,
                  hidden_size,
                  3,
                  direction_offset,
                  "_bias_i2h",
                  "_bias_gates",
                  "/i2h_w_pre",
                  "/gates_t_w_pre",
                  reform,
                  make_cell,
                  lambda x: x,
              )
  
      elif n.op_type == "LSTM":
  
          def reform(Bi, Br, W_, R_, name, hidden_size, init_net):
              # caffe2 has a different order from onnx. We need to rearrange
              #   i o f c -> i f o c
              reforms = (
                  (W_, "i2h_w", True, [(0, -1)]),
                  (R_, "gates_t_w", True, [(0, -1)]),
                  (Bi, "i2h_b", True, []),
                  (Br, "gates_t_b", True, []),
              )
              cls._rnn_reform_weights(
                  reforms,
                  name,
                  hidden_size,
                  init_net,
                  ["input", "output", "forget", "cell"],
                  [0, 2, 1, 3],
              )
  
          def make_cell(*args, **kwargs):
              return rnn_cell.LSTM(*args, **kwargs)
  
          def make_rnn(direction_offset):
              return cls._make_rnn_direction(
                  input_blob,
                  B,
                  W,
                  R,
                  [
                      (initial_h, "/initial_h"),
                      (initial_c, "/initial_c"),
                  ],
                  sequence_lens,
                  pred_mh,
                  init_net,
                  input_size,
                  hidden_size,
                  4,
                  direction_offset,
                  "/i2h_b",
                  "/gates_t_b",
                  "/i2h_w",
                  "/gates_t_w",
                  reform,
                  make_cell,
                  lambda x: [x[0], x[1], x[3]],
              )
  
      if direction == "forward":
          outputs = make_rnn(0)
  
          # in the forward case, storage is shared between the
          # last outputs. We need to decouple them so that the
          # VariableLengthSequencePadding only mutates
          # n.outputs[0]
          for i in range(1, len(outputs)):
              pred_mh.net.Copy(outputs[i], n.outputs[i])
  
          if sequence_lens is not None:
              pred_mh.net.VariableLengthSequencePadding(
                  [outputs[0], sequence_lens], [outputs[0]]
              )
          pred_mh.net.ExpandDims([outputs[0]], [n.outputs[0]], dims=[1])
      elif direction == "bidirectional":
          outputs_f = make_rnn(0)
          outputs_b = make_rnn(1)
  
          concatted_output, _ = pred_mh.net.Concat(
              [outputs_f[0], outputs_b[0]],
              [cls.dummy_name(), cls.dummy_name()],
              axis=2,
          )
          if sequence_lens is not None:
              pred_mh.net.VariableLengthSequencePadding(
                  [concatted_output, sequence_lens], [concatted_output]
              )
          reshaped_output, _ = pred_mh.net.Reshape(
              concatted_output,
              [cls.dummy_name(), cls.dummy_name()],
              shape=[0, 0, -1, 2],
          )
          pred_mh.net.Transpose(
              reshaped_output, n.outputs[0], axes=[0, 2, 1, 3]
          )
          for i in range(1, len(n.outputs)):
              pred_mh.net.Concat(
                  [outputs_f[i], outputs_b[i]],
                  [n.outputs[i], cls.dummy_name()],
                  axis=0,
              )
  
      # We want to decide whether to put all of our weight-reshaping
      # operators in the init net or the predict net. We can put
      # them in the init net iff the inputs to those operators are
      # already available, either as graph initializers, or as the
      # output of other operators in the init net. The latter case
      # occurs, for example, when exporting from pytorch to onnx.
      # In most production use, we expect has_initializers to be
      # true.
      initializers = {i.name for i in init_model.graph.initializer}
      outputs = {
          output
          for node in init_model.graph.node
          for output in node.output
      }
      has_initializers = all(
          x in initializers or x in outputs for x in (W, R, B)
      )
  
      pred_ops = []
      init_ops = []
      (init_ops if has_initializers else pred_ops).extend(
          init_net.Proto().op
      )
      pred_ops.extend(pred_mh.Proto().op)
  
      return Caffe2Ops(
          pred_ops, init_ops, list(pred_mh.Proto().external_input)
      )
  
imports: import collections
  import sys
  import zipfile
  import itertools
  import onnx.backend
  from caffe2.python import core, workspace, rnn_cell, gru_cell
  from caffe2.python.model_helper import ModelHelper
  from caffe2.proto import caffe2_pb2
  import caffe2.python.utils
  import numpy
  import onnx
  from onnx import TensorProto
  import onnx.numpy_helper
  import onnx.defs
  import onnx.shape_inference
  import onnx.utils
  from onnx.backend.base import Backend, Device, DeviceType, namedtupledict
  from caffe2.python.onnx.workspace import Workspace
  from caffe2.python.onnx.backend_rep import Caffe2Rep
  import caffe2.python._import_c_extension
  import warnings
label_desc: Use image node_id 6 for calling the Caffe2Backend obj's underlying member method code with example usage: obj._create_rnn_variant(cls, init_model, pred_model, n, opset_version) and returns: Caffe2Ops, rnn_cell, cls, gru_cell, cls, rnn_cell, cls