code: def merge_with_pauli_noise(prog_list: Iterable, probabilities: List, qubits: List):
      
      p = Program()
      p.defgate("pauli_noise", np.eye(2 ** len(qubits)))
      p.define_noisy_gate("pauli_noise", qubits, pauli_kraus_map(probabilities))
      for elem in prog_list:
          p.inst(Program(elem))
          if isinstance(elem, Measurement):
              continue  # do not apply noise after measurement
          p.inst(("pauli_noise", *qubits))
      return p
docs: Insert pauli noise channels between each item in the list of programs.
      This noise channel is implemented as a single noisy identity gate acting on the provided qubits.
      This method does not rely on merge_programs and so avoids the inclusion of redundant Kraus Pragmas
      that would occur if merge_programs was called directly on programs with distinct noisy gate definitions.
  
      :param prog_list: an iterable such as a program or a list of programs.
          If a program is provided, a single noise gate will be applied after each gate in the program.
          If a list of programs is provided, the noise gate will be applied after each program.
      :param probabilities: The 4^num_qubits list of probabilities specifying the desired pauli channel.
          There should be either 4 or 16 probabilities specified in the order
          I, X, Y, Z or II, IX, IY, IZ, XI, XX, XY, etc respectively.
      :param qubits: a list of the qubits that the noisy gate should act on.
      :return: A single program with noisy gates inserted between each element of the program list.
      :rtype: Program