code: def test_chain_multi(self):
      g = hops_graph()
  
      g2a = g.chain(
          [
              n({g._node: "e"}),
              e_forward({}, hops=2),
          ]
      )
      assert g2a._nodes.shape == (5, 2)  # e, l, g, b, a
      assert g2a._edges.shape == (4, 3)
  
      g2b = g.chain(
          [
              n({g._node: "e"}),
              e_forward({}, hops=1),
              e_forward({}, hops=1),
          ]
      )
  
      assert (
          g2b._nodes.sort_values(by=["node"])
          .reset_index(drop=True)
          .equals(
              g2a._nodes.sort_values(by=["node"]).reset_index(drop=True)
          )
      )
      assert g2b._edges.equals(g2b._edges)
  
imports: from functools import lru_cache
  from typing import Dict, List
  import logging
  import pandas
  from common import NoAuthTestCase
  from graphistry.compute.ast import n, e_forward, e_reverse, e_undirected, is_in
  from graphistry.tests.test_compute import CGFull
  from graphistry.tests.test_compute_hops import hops_graph
  from graphistry.util import setup_logger
label_desc: Use image node_id 9 for calling the TestComputeChainMixin obj's underlying member method code with example usage: obj.test_chain_multi() without return types