docs: def state_by_node2state_by_state(tpm):
      """Convert a state-by-node TPM to a state-by-state TPM.
  
      .. important::
          A nondeterministic state-by-node TPM can have more than one
          representation as a state-by-state TPM. However, the mapping can be
          made to be one-to-one if we assume the TPMs to be conditionally
          independent. Therefore, **this function returns the corresponding
          conditionally independent state-by-state TPM.**
  
      .. note::
          The indices of the rows of the state-by-node TPM are assumed to follow
          the little-endian convention, while the indices of the columns follow
          the big-endian convention. The indices of the rows and columns of the
          resulting state-by-state TPM both follow the big-endian convention. See
          the documentation on PyPhi :ref:`tpm-conventions` for more info.
  
      Args:
          tpm (list[list] or np.ndarray): A state-by-node TPM with row indices
              following the little-endian convention and column indices following
              the big-endian convention.
  
      Returns:
          np.ndarray: A state-by-state TPM, with both row and column indices
          following the big-endian convention.
  
      >>> tpm = np.array([[1, 1, 0],
      ...                 [0, 0, 1],
      ...                 [0, 1, 1],
      ...                 [1, 0, 0],
      ...                 [0, 0, 1],
      ...                 [1, 0, 0],
      ...                 [1, 1, 1],
      ...                 [1, 0, 1]])
      >>> state_by_node2state_by_state(tpm)
      array([[0., 0., 0., 1., 0., 0., 0., 0.],
             [0., 0., 0., 0., 1., 0., 0., 0.],
             [0., 0., 0., 0., 0., 0., 1., 0.],
             [0., 1., 0., 0., 0., 0., 0., 0.],
             [0., 0., 0., 0., 1., 0., 0., 0.],
             [0., 1., 0., 0., 0., 0., 0., 0.],
             [0., 0., 0., 0., 0., 0., 0., 1.],
             [0., 0., 0., 0., 0., 1., 0., 0.]])
      """
      # Cast to np.array.
      tpm = np.array(tpm)
      # Convert to multidimensional form.
      tpm = to_multidimensional(tpm)
      # Get the number of nodes from the last dimension of the TPM.
      N = tpm.shape[-1]
      # Get the number of states.
      S = 2**N
      # Initialize the state-by-state TPM.
      sbs_tpm = np.zeros((S, S))
      if not np.any(np.logical_and(tpm < 1, tpm > 0)):
          # TPM is deterministic.
          for previous_state_index in range(S):
              # Use the little-endian convention to get the row and column
              # indices.
              previous_state = le_index2state(previous_state_index, N)
              current_state_index = state2le_index(tpm[previous_state])
              sbs_tpm[previous_state_index, current_state_index] = 1
      else:
          # TPM is nondeterministic.
          for previous_state_index in range(S):
              # Use the little-endian convention to get the row and column
              # indices.
              previous_state = le_index2state(previous_state_index, N)
              marginal_tpm = tpm[previous_state]
              for current_state_index in range(S):
                  current_state = np.array(
                      [i for i in le_index2state(current_state_index, N)])
                  sbs_tpm[previous_state_index, current_state_index] = (
                      np.prod(marginal_tpm[current_state == 1]) *
                      np.prod(1 - marginal_tpm[current_state == 0]))
      return sbs_tpm