Source code for qiskit_addon_cutting.wire_cutting_transforms
# This code is a Qiskit project.## (C) Copyright IBM 2023.## This code is licensed under the Apache License, Version 2.0. You may# obtain a copy of this license in the LICENSE.txt file in the root directory# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.## Any modifications or derivative works of this code must retain this# copyright notice, and modified files need to carry a notice indicating# that they have been altered from the originals."""Function to transform a :class:`.CutWire` instruction to a :class:`.Move` instruction."""from__future__importannotationsfromtypingimportCallablefromitertoolsimportgroupbyimportnumpyasnpfromqiskit.circuitimportQubit,QuantumCircuit,Operationfromqiskit.circuit.exceptionsimportCircuitErrorfromqiskit.quantum_infoimportPauliListfrom.instructionsimportMovefrom.qpd.instructionsimportTwoQubitQPDGate
[docs]defcut_wires(circuit:QuantumCircuit,/)->QuantumCircuit:r"""Transform all :class:`.CutWire` instructions in a circuit to :class:`.Move` instructions marked for cutting. The returned circuit will have one newly allocated qubit for every :class:`.CutWire` instruction. See Sec. 3 and Appendix A of `2302.03366v1 <https://arxiv.org/abs/2302.03366v1>`__ for more information about the two different representations of wire cuts: single-qubit (:class:`.CutWire`) vs. two-qubit (:class:`.Move`). Args: circuit: Original circuit with :class:`.CutWire` instructions Returns: circuit: New circuit with :class:`.CutWire` instructions replaced by :class:`.Move` instructions wrapped in :class:`TwoQubitQPDGate`\ s """return_transform_cut_wires(circuit,lambda:TwoQubitQPDGate.from_instruction(Move()))
def_transform_cuts_to_moves(circuit:QuantumCircuit,/)->QuantumCircuit:"""Transform all :class:`.CutWire` instructions in a circuit to :class:`.Move` instructions. Args: circuit: Original circuit with :class:`.CutWire` instructions Returns: circuit: New circuit with :class:`.CutWire` instructions replaced by :class`.Move` instructions """return_transform_cut_wires(circuit,Move)def_transform_cut_wires(circuit:QuantumCircuit,factory:Callable[[],Operation],/)->QuantumCircuit:new_circuit,mapping=_circuit_structure_mapping(circuit)forinstructionsincircuit.data:gate_index=[circuit.find_bit(qubit).indexforqubitininstructions.qubits]ifinstructionsincircuit.get_instructions("cut_wire"):# Replace cut_wire with move instructionnew_circuit.compose(other=factory(),qubits=[mapping[gate_index[0]],mapping[gate_index[0]]+1],inplace=True,)mapping[gate_index[0]]+=1else:new_circuit.compose(other=instructions.operation,qubits=[mapping[index]forindexingate_index],inplace=True,)returnnew_circuitdef_circuit_structure_mapping(circuit:QuantumCircuit,)->tuple[QuantumCircuit,list[int]]:new_circuit=QuantumCircuit()mapping=list(range(len(circuit.qubits)))cut_wire_index=[circuit.find_bit(instruction.qubits[0]).indexforinstructionincircuit.get_instructions("cut_wire")]cut_wire_freq={key:len(list(group))forkey,groupingroupby(cut_wire_index)}# Get intermediate mapping and add quantum bits to new_circuitforqubitincircuit.qubits:index=circuit.find_bit(qubit).indexifindexincut_wire_freq.keys():for_inrange(cut_wire_freq[index]):mapping[index+1:]=map(lambdaitem:item+1,iter(mapping[index+1:]))new_circuit.add_bits([Qubit()])new_circuit.add_bits([qubit])# Add quantum and classical registersforqregincircuit.qregs:new_circuit.add_register(qreg)new_circuit.add_bits(circuit.clbits)forcregincircuit.cregs:new_circuit.add_register(creg)returnnew_circuit,mapping
[docs]defexpand_observables(observables:PauliList,original_circuit:QuantumCircuit,final_circuit:QuantumCircuit,/,)->PauliList:r"""Expand observable(s) according to the qubit mapping between ``original_circuit`` and ``final_circuit``. The :class:`.Qubit`\ s on ``final_circuit`` must be a superset of those on ``original_circuit``. Given a :class:`.PauliList` of observables, this function returns new observables with identity operators placed on the qubits that did not exist in ``original_circuit``. This way, observables on ``original_circuit`` can be mapped to appropriate observables on ``final_circuit``. This function is designed to be used after calling ``final_circuit = transform_cuts_to_moves(original_circuit)`` (see :func:`.transform_cuts_to_moves`). This function requires ``observables.num_qubits == original_circuit.num_qubits`` and returns new observables such that ``retval.num_qubits == final_circuit.num_qubits``. Args: observables: Observables corresponding to ``original_circuit`` original_circuit: Original circuit final_circuit: Final circuit, whose qubits the original ``observables`` should be expanded to Returns: New :math:`N`-qubit observables which are compatible with the :math:`N`-qubit ``final_circuit`` Raises: ValueError: ``observables`` and ``original_circuit`` have different number of qubits. ValueError: Qubit from ``original_circuit`` cannot be found in ``final_circuit``. """ifobservables.num_qubits!=original_circuit.num_qubits:raiseValueError("The `observables` and `original_circuit` must have the same number "f"of qubits. ({observables.num_qubits} != {original_circuit.num_qubits})")mapping:list[int]=[]fori,qubitinenumerate(original_circuit.qubits):try:idx=final_circuit.find_bit(qubit)[0]exceptCircuitErrorasex:raiseValueError(f"The {i}-th qubit of the `original_circuit` cannot be found ""in the `final_circuit`.")fromexmapping.append(idx)dims=(len(observables),final_circuit.num_qubits)z=np.full(dims,False)x=np.full(dims,False)z[:,mapping]=observables.zx[:,mapping]=observables.xreturnPauliList.from_symplectic(z,x,observables.phase.copy())