code: def gates_in_isa(isa):
      
      gates = []
      for q in isa.qubits:
          if q.dead:
              # TODO: dead qubits may in the future lead to some implicit re-indexing
              continue
          if q.type in ["Xhalves"]:
              gates.extend([
                  Gate("I", [], [unpack_qubit(q.id)]),
                  Gate("RX", [np.pi / 2], [unpack_qubit(q.id)]),
                  Gate("RX", [-np.pi / 2], [unpack_qubit(q.id)]),
                  Gate("RX", [np.pi], [unpack_qubit(q.id)]),
                  Gate("RX", [-np.pi], [unpack_qubit(q.id)]),
                  Gate("RZ", [THETA], [unpack_qubit(q.id)]),
              ])
          else:  # pragma no coverage
              raise ValueError("Unknown qubit type: {}".format(q.type))
  
      for e in isa.edges:
          if e.dead:
              continue
          targets = [unpack_qubit(t) for t in e.targets]
          if e.type in ["CZ", "ISWAP"]:
              gates.append(Gate(e.type, [], targets))
              gates.append(Gate(e.type, [], targets[::-1]))
          elif e.type in ["CPHASE"]:
              gates.append(Gate(e.type, [THETA], targets))
              gates.append(Gate(e.type, [THETA], targets[::-1]))
          else:  # pragma no coverage
              raise ValueError("Unknown edge type: {}".format(e.type))
      return gates
docs: Generate the full gateset associated with an ISA.
  
      :param ISA isa: The instruction set architecture for a QPU.
      :return: A sequence of Gate objects encapsulating all gates compatible with the ISA.
      :rtype: Sequence[Gate]