Source code for circuit

# Import necessary packages
import quimb as quimb
import numpy as np
from qiskit import QuantumCircuit, execute, Aer
from qiskit.circuit import library as lb

from gates import cphase_swap_qiskit, cphase_swap_quimb, GATES, cphase_and_swap_cirq
from manual import apply_two_qubit_gate_full, max_bond_dimension, apply_two_qubit_gate, apply_one_qubit_gate

# +
#--- QFT - MANUAL ---
[docs]def qft_circuit_swap_full(state, N, verbosity=False): """ Computes the QFT of a MPS *state* with *N* qubits. Parameters ---------- state: list of ndarray list of tensors of the MPS N: int number of qubits in *state* verbosity: bool, optional Default to False. If True prints applied gates Returns ------- state: list of ndarray MPS where we have applied the QFT """ H = np.array(quimb.hadamard()) for pos in range(N): if verbosity: print("H(0)") state = apply_one_qubit_gate(H, 0, state) for i in range(N-pos-1): if verbosity: print(f"CZS(({i},{i+1}), {1/2**(i+1)})") state = apply_two_qubit_gate_full(cphase_swap_quimb(1/2**(i+1)), i, state) #to (i, i+1) return state
[docs]def qft_circuit_swap_approx(state, N, verbosity=False, chi=2): """ Computes the QFT of a MPS *state* with *N* qubits and a bond dimension *chi*. Parameters ---------- state: list of ndarray list of tensors of the MPS N: int number of qubits in *state* verbosity: bool, optional Default to False. If True prints applied gates chi: int, optional Bond dimension Returns ------- state: list of ndarray MPS where we have applied the QFT """ H = np.array(quimb.hadamard()) for pos in range(N): if verbosity: print("H(0)") state = apply_one_qubit_gate(H, 0, state) for i in range(N-pos-1): if verbosity: print(f"CZS(({i},{i+1}), {1/2**(i+1)})") state = apply_two_qubit_gate(cphase_swap_quimb(1/2**(i+1)), i, state, chi) #to (i, i+1) return state
# - #---QFT - QISKIT---
[docs]def qft_circuit_qiskit(circuit, n): """ Apply the QFT to a qiskit quantum circuit *circuit* in a recursive way Parameters ---------- circuit : quantum circuit quantum circuit where we want to apply the QFT n : int number of qubits in *circuit* Returns ------- None: None Acts in place """ if n == 0: return circuit elif n==1: circuit.h(0) return circuit circuit.h(0) for i in range(n-1): cphase_swap_qiskit(circuit, i, i+1, np.pi*1/2**(i+1)) return qft_circuit_qiskit(circuit, n-1)
# ---QFT - CIRQ---
[docs]def qft_circuit_swap_cirq(qubits, circuit=[]): """ Build a circuit implementing the QFT algorithm on the given *qubits*. The order of *qubits* is preserved by SWAP operations. Implemented using only local operations, i.e. gates acting on neighbouring qubits. Adapted from: https://github.com/quantumlib/Cirq/blob/master/examples/quantum_fourier_transform.py and extended to n generic qubits through recursion. Parameters ---------- qubits: cirq.LineQubit qubits circuit: list list of directives to create the quantum circuit Returns ------- circuit: cirq.Circuit quantum circuit where we have applied the QFT """ n = len(qubits) assert n > 0, "Number of qubits must be > 0" if (n == 1): circuit.append(cirq.H(qubits[0])) return cirq.Circuit(circuit, strategy=cirq.InsertStrategy.EARLIEST) else: circuit.append(cirq.H(qubits[0])) circuit.extend(cphase_and_swap_cirq(qubits[i], qubits[i+1], 1/2**(i+1)) for i in range(n-1)) return qft_circuit_swap_cirq(qubits[:n-1], circuit)
# ---QFT - QUIMB---
[docs]def qft_circuit_quimb(mps, n, chi=2): """ Apply the QFT to the matrix product state *mps* with a bond dimension of *chi* in a recursive way Parameters ---------- mps : quimb.tensor.tensor_1d.MatrixProductState Matrix product state to which we want to apply the QFT n : int number of qubits in *mps* chi : int, optional Bond dimension of the MPS. Default to 2 Returns ------- mps: quimb.tensor.tensor_1d.MatrixProductState Matrix product state to which we have applied the QFT """ H = quimb.hadamard() if n == 0: return mps elif n==1: mps.gate_(H, 0, tags='H', contract=True) return mps mps.gate_(H, 0, tags='H', contract=True) for i in range(n-1): mps.gate_(cphase_swap_quimb(1/2**(i+1)), (i, i+1), tags='Cphase_swap', max_bond=chi, contract='swap+split') return qft_circuit_quimb(mps, n-1)
#---MPS-QISKIT INTERFACE---
[docs]class circ_data(): """ Class to contain the metadata of a qiskit quantum circuit Attributes ---------- data: list data of a quantum circuit gates: list of string names of the gate applied in the correct order gates_params: list of lists gate parameters. if no parameter is present it returns an empty list [] indeces: list of tuples of ints indeces to which the gates are applyed n_qub: int number of qubits in the circuit """ def __init__(self, circ): """ Initialize the class by registering the gates applied in the circuit in @self.gates, the gate parameters in @self.gate_params (if no parameter is present it returns an empty list []), the indeces to which the gates are applyed @self.indeces and the number of qubits in the circuit @self.n_qub """ self.data = [ info for info in circ.data] # Gates applied in order self.gates = [ qub[0].name for qub in self.data ] self.gates_params = [ qub[0].params for qub in self.data ] # Indices of application of the gates self.indeces = [ self._to_index( qub[1]) for qub in self.data ] # Number of qubits in the circuit self.n_qub = circ.num_qubits def _to_index(self, x): """ Unpack indeces in @x """ return [ y.index for y in x ]
[docs]def MPS_circ(qc, gates = None, init_state = None, chi=None, verbosity=False): """ Function to transform a qiskit circuit *qc* to a quimb MPS circuit, using the gates *gates* Parameters ---------- qc : QuantumCircuit Qiskit quantum circuit to apply to the MPS gates : dictionary or None, optional dictionary containing the gate used in the circuit in the format qiskit_gate_name: quimb_gate If None a standard set is used init_state : String, None or quimb.tensor.tensor_1d.MatrixProductState, optional If String containing the state in computational basis, i.e. '000' or '0011' If None starts from empty state '0'x(num_qubits) If MPS in quimb format the initial state is the MPS chi : int, optional maximum bond dimension. If None it is automatically chosen. verbosity : bool, optional if True prints when the state is initialized. Default to false. Returns ------- MPS : quimb.tensor.tensor_1d.MatrixProductState quimb MPS where we have applied the quantum circuit *qc* starting from *init_state* """ data = circ_data(qc) if gates == None: gate_list = GATES() gates = gate_list.gate_dict if init_state == None: init_state = '0' * data.n_qub if type(init_state) == quimb.tensor.tensor_1d.MatrixProductState: if verbosity: print('--- State initialized ---') MPS = init_state else: MPS = quimb.tensor.MPS_computational_state(init_state, tags = 'psi0') for gate_name, qub_idx, params in zip( data.gates, data.indeces, data.gates_params): qubits = tuple(qub_idx) identity = np.eye(2**len(qubits)) if len(qubits)==1: if len(params)==0: MPS.gate_( gates[ gate_name ], qubits, tags=gate_name, max_bond=chi, contract=True) else: MPS.gate_( gates[ gate_name ](*params), qubits, tags=gate_name, max_bond=chi, contract=True) elif len(qubits)==2: if len(params)==0: MPS.gate_( gates[ gate_name ], qubits, tags=gate_name, max_bond=chi, contract='swap+split') else: # Parametric gates MPS.gate_( gates[ gate_name ](*params), qubits, tags=gate_name, max_bond=chi, contract='swap+split') return MPS
[docs]def GHZ_qiskit(circ): """ Generates a GHZ state in the quantum circuit *circ* composed by n qubits. Acts in place. Parameters ---------- circ: Quantum Circuit The quantum circuit in the state '00...0' where to build the GHZ Returns ------- None: None Acts in place. """ n = circ.num_qubits circ.h(0) for i in range(1, n): circ.cx( 0, i)