code: def main(args):
      """
      Compute marginal probabilities p(x[t], x[t-1] | Y) for an HMM:
  
      x[0] -> ... -> x[t-1] -> x[t] -> ... -> x[T]
       |              |         |             |
       v              v         v             v
      y[0]           y[t-1]    y[t]           y[T]
  
      Z = p(y[0:T])
      alpha[t] = p(y[0:t], x[t])
      beta[t] = p(y[t+1:T] | x[t])
      trans[t] = p(y[t], x[t] | x[t-1])
  
      p(x[t], x[t-1] | Y) = alpha[t-1] * beta[t] * trans[t] / Z
  
      d Z / d trans[t] = alpha[t-1] * beta[t]
  
      **References:**
  
      [1] Jason Eisner (2016)
          "Inside-Outside and Forward-Backward Algorithms Are Just Backprop
          (Tutorial Paper)"
          https://www.cs.jhu.edu/~jason/papers/eisner.spnlp16.pdf
      [2] Zhifei Li and Jason Eisner (2009)
          "First- and Second-Order Expectation Semirings
          with Applications to Minimum-Risk Training on Translation Forests"
          http://www.cs.jhu.edu/~zfli/pubs/semiring_translation_zhifei_emnlp09.pdf
      """
  
      # transition and emission probabilities
      init = random_tensor(
          OrderedDict([("x_curr", Bint[args.hidden_dim])])
      )
      factors = [init]
      for t in range(args.time_steps - 1):
          factors.append(
              random_tensor(
                  OrderedDict(
                      x_prev=Bint[args.hidden_dim],
                      x_curr=Bint[args.hidden_dim],
                  )
              )
          )
  
      # Compute marginal probabilities using the forward-backward algorithm
      marginals = forward_backward_algorithm(
          factors, {"x_prev": "x_curr"}
      )
      # Compute marginal probabilities using backpropagation
      with AdjointTape() as tape:
          Z = forward_algorithm(factors, {"x_prev": "x_curr"})
      result = tape.adjoint(ops.logaddexp, ops.add, Z, factors)
      adjoint_terms = list(result.values())
  
      t = 0
      for expected, adj, trans in zip(
          marginals, adjoint_terms, factors
      ):
          # adjoint returns dZ / dtrans = alpha[t-1] * beta[t]
          # marginal = alpha[t-1] * beta[t] * trans / Z
          actual = adj + trans - Z
          assert_close(
              expected, actual.align(tuple(expected.inputs)), rtol=1e-4
          )
          print("")
          print(f"Marginal term: p(x[{t}], x[{t-1}] | Y)")
          print("Forward-backward algorithm:\n", expected.data)
          print("Differentiating forward algorithm:\n", actual.data)
          t += 1
  
file_path: funsor/examples/forward_backward.py
imports: import argparse
  from collections import OrderedDict
  from typing import Dict, List, Tuple
  import funsor.ops
  from funsor import Funsor, Tensor
  from funsor.adjoint import AdjointTape
  from funsor.domains import Bint
  from funsor.testing import assert_close, random_tensor
label_desc: Use image node_id 3 for calling a global function with example usage: main(args) without return types