code: def pair_features(
      mol: RDKitMol,
      bond_features_map: dict,
      bond_adj_list: List,
      bt_len: int = 6,
      graph_distance: bool = True,
      max_pair_distance: Optional[int] = None,
  ) -> Tuple[np.ndarray, np.ndarray]:
      """Helper method used to compute atom pair feature vectors.
  
      Many different featurization methods compute atom pair features
      such as WeaveFeaturizer. Note that atom pair features could be
      for pairs of atoms which aren't necessarily bonded to one
      another.
  
      Parameters
      ----------
      mol: RDKit Mol
          Molecule to compute features on.
      bond_features_map: dict
          Dictionary that maps pairs of atom ids (say `(2, 3)` for a bond between
          atoms 2 and 3) to the features for the bond between them.
      bond_adj_list: list of lists
          `bond_adj_list[i]` is a list of the atom indices that atom `i` shares a
          bond with . This list is symmetrical so if `j in bond_adj_list[i]` then `i
          in bond_adj_list[j]`.
      bt_len: int, optional (default 6)
          The number of different bond types to consider.
      graph_distance: bool, optional (default True)
          If true, use graph distance between molecules. Else use euclidean
          distance. The specified `mol` must have a conformer. Atomic
          positions will be retrieved by calling `mol.getConformer(0)`.
      max_pair_distance: Optional[int], (default None)
          This value can be a positive integer or None. This
          parameter determines the maximum graph distance at which pair
          features are computed. For example, if `max_pair_distance==2`,
          then pair features are computed only for atoms at most graph
          distance 2 apart. If `max_pair_distance` is `None`, all pairs are
          considered (effectively infinite `max_pair_distance`)
  
      Note
      ----
      This method requires RDKit to be installed.
  
      Returns
      -------
      features: np.ndarray
          Of shape `(N_edges, bt_len + max_distance + 1)`. This is the array
          of pairwise features for all atom pairs, where N_edges is the
          number of edges within max_pair_distance of one another in this
          molecules.
      pair_edges: np.ndarray
          Of shape `(2, num_pairs)` where `num_pairs` is the total number of
          pairs within `max_pair_distance` of one another.
      """
      if graph_distance:
          max_distance = 7
      else:
          max_distance = 1
      N = mol.GetNumAtoms()
      pair_edges = max_pair_distance_pairs(mol, max_pair_distance)
      num_pairs = pair_edges.shape[1]
      N_edges = pair_edges.shape[1]
      features = np.zeros((N_edges, bt_len + max_distance + 1))
      # Get mapping
      mapping = {}
      for n in range(N_edges):
          a1, a2 = pair_edges[:, n]
          mapping[(int(a1), int(a2))] = n
      num_atoms = mol.GetNumAtoms()
      rings = mol.GetRingInfo().AtomRings()
      for a1 in range(num_atoms):
          for a2 in bond_adj_list[a1]:
              # first `bt_len` features are bond features(if applicable)
              if (int(a1), int(a2)) not in mapping:
                  raise ValueError(
                      "Malformed molecule with bonds not in specified graph distance."
                  )
              else:
                  n = mapping[(int(a1), int(a2))]
              features[n, :bt_len] = np.asarray(
                  bond_features_map[tuple(sorted((a1, a2)))],
                  dtype=float,
              )
          for ring in rings:
              if a1 in ring:
                  for a2 in ring:
                      if (int(a1), int(a2)) not in mapping:
                          # For ring pairs outside max pairs distance continue
                          continue
                      else:
                          n = mapping[(int(a1), int(a2))]
                      # `bt_len`-th feature is if the pair of atoms are in the same ring
                      if a2 == a1:
                          features[n, bt_len] = 0
                      else:
                          features[n, bt_len] = 1
          # graph distance between two atoms
          if graph_distance:
              # distance is a matrix of 1-hot encoded distances for all atoms
              distance = find_distance(
                  a1,
                  num_atoms,
                  bond_adj_list,
                  max_distance=max_distance,
              )
              for a2 in range(num_atoms):
                  if (int(a1), int(a2)) not in mapping:
                      # For ring pairs outside max pairs distance continue
                      continue
                  else:
                      n = mapping[(int(a1), int(a2))]
                      features[n, bt_len + 1 :] = distance[a2]
      # Euclidean distance between atoms
      if not graph_distance:
          coords = np.zeros((N, 3))
          for atom in range(N):
              pos = mol.GetConformer(0).GetAtomPosition(atom)
              coords[atom, :] = [pos.x, pos.y, pos.z]
          features[:, :, -1] = np.sqrt(
              np.sum(
                  np.square(
                      np.stack([coords] * N, axis=1)
                      - np.stack([coords] * N, axis=0)
                  ),
                  axis=2,
              )
          )
  
      return features, pair_edges
  
imports: import numpy
  import deepchem
  from deepchem.feat.base_classes import MolecularFeaturizer
  from deepchem.feat.mol_graphs import ConvMol, WeaveMol
  from deepchem.data import DiskDataset
  import logging
  from typing import Optional, List, Tuple, Union, Iterable
  from deepchem.utils.typing import RDKitMol, RDKitAtom
  from deepchem.utils.molecule_feature_utils import one_hot_encode
label_desc: Use image node_id 12 for calling a global function with example usage: pair_features(mol, bond_features_map, bond_adj_list, bt_len, graph_distance, max_pair_distance) and returns: features,  pair_edges