code: def test_sequential_sum_product_adjoint(
      impl, sum_op, prod_op, batch_inputs, state_domain, num_steps
  ):
      # test mostly copied from test_sum_product.py
      inputs = OrderedDict(batch_inputs)
      inputs.update(prev=state_domain, curr=state_domain)
      inputs["time"] = Bint[num_steps]
      if state_domain.dtype == "real":
          trans = random_gaussian(inputs)
      else:
          trans = random_tensor(inputs)
      time = Variable("time", Bint[num_steps])
  
      with AdjointTape() as actual_tape:
          actual = impl(sum_op, prod_op, trans, time, {"prev": "curr"})
          actual = actual.reduce(sum_op)
  
      # Check against contract.
      operands = tuple(
          trans(
              time=t, prev="t_{}".format(t), curr="t_{}".format(t + 1)
          )
          for t in range(num_steps)
      )
      reduce_vars = frozenset(
          "t_{}".format(t) for t in range(1, num_steps)
      )
      with AdjointTape() as expected_tape:
          with reflect:
              lazy_expected = sum_product(
                  sum_op, prod_op, operands, reduce_vars
              )
          expected = apply_optimizer(lazy_expected)
          expected = expected.reduce(sum_op)
  
      # check forward pass (sanity check)
      assert_close(
          actual,
          expected.align(tuple(actual.inputs.keys())),
          rtol=5e-3 * num_steps,
      )
  
      # perform backward passes only after the sanity check
      expected_bwds = expected_tape.adjoint(
          sum_op, prod_op, expected, operands
      )
      actual_bwd = actual_tape.adjoint(
          sum_op, prod_op, actual, (trans,)
      )[trans]
  
      # check backward pass
      for t, operand in enumerate(operands):
          actual_bwd_t = actual_bwd(
              time=t, prev="t_{}".format(t), curr="t_{}".format(t + 1)
          )
          expected_bwd = expected_bwds[operand]
          assert (
              actual_bwd_t - expected_bwd
          ).abs().data.max() < 5e-3 * num_steps
  
file_path: funsor/test/test_adjoint.py
imports: from collections import OrderedDict
  import pytest
  import funsor
  import funsor.ops
  from funsor.adjoint import AdjointTape
  from funsor.domains import Bint
  from funsor.einsum import BACKEND_ADJOINT_OPS, einsum, naive_einsum, naive_plated_einsum
  from funsor.optimizer import apply_optimizer
  from funsor.sum_product import MarkovProduct, naive_sequential_sum_product, sequential_sum_product, sum_product
  from funsor.terms import Variable, reflect
  from funsor.testing import assert_close, make_einsum_example, make_plated_hmm_einsum, random_gaussian, random_tensor, xfail_param
  from funsor.util import get_backend
label_desc: Use image node_id 4 for calling a global function with example usage: test_sequential_sum_product_adjoint(impl, sum_op, prod_op, batch_inputs, state_domain, num_steps) without return types