code: def test_builder_to_backend_smoke_bidirlstm(
      self,
      compute_unit,
      backend,
      seq_len,
      batch_size,
      input_size,
      hidden_size,
      has_bias,
      output_sequence,
      symbolic,
      dtype,
  ):
      def _pytorch_hidden_to_coreml(x):
          x = x.detach().numpy()
          # Split of Direction axis
          f, b = np.split(x, 2, axis=0)
          # Concat on Hidden Size axis
          x = np.concatenate([f, b], axis=2)
          x = np.squeeze(x, axis=0)
          return x
  
      direction = "bidirectional"
      torch.manual_seed(20)
      rnn = torch.nn.LSTM(
          input_size, hidden_size, 1, bidirectional=True, bias=has_bias
      )
      state_dict = rnn.state_dict()
  
      ih_wt = state_dict["weight_ih_l0"].detach().numpy()
      hh_wt = state_dict["weight_hh_l0"].detach().numpy()
      ih_wt_r = state_dict["weight_ih_l0_reverse"].detach().numpy()
      hh_wt_r = state_dict["weight_hh_l0_reverse"].detach().numpy()
  
      def ifzo_to_ifoz(x):
          i, f, z, o = np.split(x, 4)
          return np.concatenate([i, f, o, z], axis=0).astype(dtype)
  
      wx = ifzo_to_ifoz(ih_wt)
      wh = ifzo_to_ifoz(hh_wt)
      r_wx = ifzo_to_ifoz(ih_wt_r)
      r_wh = ifzo_to_ifoz(hh_wt_r)
  
      b, r_b = None, None
      if has_bias:
          ih_b = state_dict["bias_ih_l0"].detach().numpy()
          hh_b = state_dict["bias_hh_l0"].detach().numpy()
          r_ih_b = state_dict["bias_ih_l0_reverse"].detach().numpy()
          r_hh_b = state_dict["bias_hh_l0_reverse"].detach().numpy()
          # Convert forward bias into [4*H]
          b = ih_b + hh_b
          b = ifzo_to_ifoz(b)
          # Convert reverse bias into [*H]
          r_b = r_ih_b + r_hh_b
          r_b = ifzo_to_ifoz(r_b)
  
      t = torch.randn(seq_len, batch_size, input_size)
      h0 = torch.randn(2, batch_size, hidden_size)
      c0 = torch.randn(2, batch_size, hidden_size)
  
      output, (hn, cn) = rnn(t, (h0, c0))
      if not output_sequence:
          output_f = output[-1].unsqueeze(0)[:, :, :hidden_size]
          output_r = output[0].unsqueeze(0)[:, :, hidden_size:]
          output = torch.cat([output_f, output_r], dim=2)
  
      output = output.detach().numpy().astype(dtype)
      hn = _pytorch_hidden_to_coreml(hn).astype(dtype)
      cn = _pytorch_hidden_to_coreml(cn).astype(dtype)
  
      if symbolic:
          batch_size = get_new_symbol()
          seq_len = get_new_symbol()
  
      input_shape = [seq_len, batch_size, input_size]
      h_shape = [batch_size, 2 * hidden_size]
      c_shape = [batch_size, 2 * hidden_size]
  
      builtin_dtype = numpy_type_to_builtin_type(dtype)
      expected_output_types = [
          (
              seq_len if output_sequence else 1,
              batch_size,
              2 * hidden_size,
              builtin_dtype,
          ),
          (batch_size, 2 * hidden_size, builtin_dtype),
          (batch_size, 2 * hidden_size, builtin_dtype),
      ]
      expected_outputs = [output, hn, cn]
  
      t = t.detach().numpy()
      h = _pytorch_hidden_to_coreml(h0)
      c = _pytorch_hidden_to_coreml(c0)
  
      input_placeholders = {
          "x": mb.placeholder(shape=input_shape, dtype=builtin_dtype),
          "initial_h": mb.placeholder(
              shape=h_shape, dtype=builtin_dtype
          ),
          "initial_c": mb.placeholder(
              shape=c_shape, dtype=builtin_dtype
          ),
      }
      input_values = {
          "x": t.astype(dtype),
          "initial_h": h.astype(dtype),
          "initial_c": c.astype(dtype),
      }
  
      def build(x, initial_h, initial_c):
          arguments = {
              "x": x,
              "initial_h": initial_h,
              "initial_c": initial_c,
              "weight_ih": wx,
              "weight_hh": wh,
              "weight_ih_back": r_wx,
              "weight_hh_back": r_wh,
              "direction": direction,
              "output_sequence": output_sequence,
          }
          # If bias is provided, add in arguments
          if b is not None:
              arguments["bias"] = b
              arguments["bias_back"] = r_b
          return mb.lstm(**arguments)
  
      run_compare_builder(
          build,
          input_placeholders,
          input_values,
          expected_output_types,
          expected_outputs,
          inputs=construct_inputs_from_placeholders(
              input_placeholders, upper_bound=64
          )
          if symbolic and backend.backend == "mlprogram"
          else None,
          compute_unit=compute_unit,
          backend=backend,
      )
  
imports: import itertools
  import numpy
  import pytest
  import coremltools
  from coremltools._deps import _HAS_TORCH, MSG_TORCH_NOT_FOUND
  from coremltools.converters.mil.mil import Builder
  from coremltools.converters.mil.mil import get_new_symbol
  from coremltools.converters.mil.mil.ops.tests.iOS14 import backends
  from coremltools.converters.mil.mil.ops.tests.testing_utils import construct_inputs_from_placeholders, run_compare_builder
  from coremltools.converters.mil.mil.types.type_mapping import numpy_type_to_builtin_type
  from coremltools.converters.mil.testing_reqs import compute_units
  from coremltools.converters.mil.testing_utils import ssa_fn
label_desc: Use image node_id 3 for calling the TestLSTM obj's underlying member method code with example usage: obj.test_builder_to_backend_smoke_bidirlstm(compute_unit, backend, seq_len, batch_size, input_size, hidden_size, has_bias, output_sequence, symbolic, dtype) and returns: x, np, mb