[1]:
from diskit import *
from qiskit import assemble, Aer, QuantumCircuit
from qiskit.visualization import plot_bloch_multivector
import numpy as np
import warnings

warnings.filterwarnings("ignore")

Distributed Quantum Fourier Transform

Functions to construct the circuit to do Quantum Fourier Transform

[2]:
def qft_rotations(circuit, n):
    """Add controlled phase gates as per qft on the first n qubits in circuit"""
    if n == 0:
        return circuit
    n -= 1
    circuit.h(n)
    for qubit in range(n):
        circuit.cp(np.pi/2**(n-qubit), qubit, n)
    qft_rotations(circuit, n) # recursion call

def swap_registers(circuit, n):
    """Swap Registers as per QFT scheme"""
    for qubit in range(n//2):
        circuit.swap(qubit, n-qubit-1)
    return circuit


def qft(circuit, n, transpile_swap = False):
    """QFT on the first n qubits in circuit"""
    qft_rotations(circuit, n)
    swap_registers(circuit, n)
    return circuit

Create a Distributed Circuit for performing Quantum Fourier Transform

For 2 processor with 4 qubits

Define a Topology of 4 qubits with 2 processors each having 2 qubits

[3]:
circuit_topo = Topology()
circuit_topo.create_qmap(2, [2, 2],"sys_qft")
circuit_topo.qmap, circuit_topo.emap
[3]:
({'sys_qft0': [Qubit(QuantumRegister(2, 'sys_qft0'), 0),
   Qubit(QuantumRegister(2, 'sys_qft0'), 1)],
  'sys_qft1': [Qubit(QuantumRegister(2, 'sys_qft1'), 0),
   Qubit(QuantumRegister(2, 'sys_qft1'), 1)]},
 {'sys_qft0': Qubit(QuantumRegister(1, 'com_sys_qft0'), 0),
  'sys_qft1': Qubit(QuantumRegister(1, 'com_sys_qft1'), 0)})
[4]:
remapper = CircuitRemapper(circuit_topo)

Get the registers and total number of qubits

[5]:
qregs = circuit_topo.get_regs()
n_q = circuit_topo.num_qubits()

Define the monolithic Circuit for performing Fourier Transform of \(|0\rangle\)

[6]:
circ_1 = QuantumCircuit(*qregs)
qft_circ = qft(circ_1, n_q)
qft_circ.draw(output='mpl')
[6]:
../_images/examples_QFT_12_0.png

We shall need to decompose the swap gates to CNOT before translating to Distributed Circuit.

[7]:
qft_circ.decompose('swap').draw(output='mpl')
[7]:
../_images/examples_QFT_14_0.png
[8]:
dist_circ_1 = remapper.remap_circuit(qft_circ.decompose('swap'))
dist_circ_1.draw('mpl')
[8]:
../_images/examples_QFT_15_0.png

For 4 processors with total 16 qubits

Define a Topology of 10 qubits with 4 processors.

[19]:
circuit_topo = Topology()
circuit_topo.create_qmap(4, [2, 3, 4, 1],"sys_qft")
circuit_topo.qmap, circuit_topo.emap
[19]:
({'sys_qft0': [Qubit(QuantumRegister(2, 'sys_qft0'), 0),
   Qubit(QuantumRegister(2, 'sys_qft0'), 1)],
  'sys_qft1': [Qubit(QuantumRegister(3, 'sys_qft1'), 0),
   Qubit(QuantumRegister(3, 'sys_qft1'), 1),
   Qubit(QuantumRegister(3, 'sys_qft1'), 2)],
  'sys_qft2': [Qubit(QuantumRegister(4, 'sys_qft2'), 0),
   Qubit(QuantumRegister(4, 'sys_qft2'), 1),
   Qubit(QuantumRegister(4, 'sys_qft2'), 2),
   Qubit(QuantumRegister(4, 'sys_qft2'), 3)],
  'sys_qft3': [Qubit(QuantumRegister(1, 'sys_qft3'), 0)]},
 {'sys_qft0': Qubit(QuantumRegister(1, 'com_sys_qft0'), 0),
  'sys_qft1': Qubit(QuantumRegister(1, 'com_sys_qft1'), 0),
  'sys_qft2': Qubit(QuantumRegister(1, 'com_sys_qft2'), 0),
  'sys_qft3': Qubit(QuantumRegister(1, 'com_sys_qft3'), 0)})
[20]:
remapper = CircuitRemapper(circuit_topo)

Get the registers and total number of qubits

[21]:
qregs = circuit_topo.get_regs()
n_q = circuit_topo.num_qubits()

Define the monolithic Circuit for performing Fourier Transform of \(|0\rangle\)

[22]:
circ_2 = QuantumCircuit(*qregs)
qft_circ = qft(circ_2, n_q)
# qft_circ.draw(output='mpl')
[23]:
dist_circ_2 = remapper.remap_circuit(circ_2.decompose('swap'))

Let us check the validity of the working of the distributed version for the 16 Qubit case

First we check the output for monolithic circuit

[24]:
from qiskit.quantum_info import partial_trace
sim = Aer.get_backend('aer_simulator')
[28]:
circ2_copy = circ_2.copy()
circ2_copy.save_statevector()
qobj = assemble(circ2_copy)
state = sim.run(qobj).result().get_statevector()

state = partial_trace(state, [10,11,12,13]) ## Trace out the communication qubits (12 - 15)

plot_bloch_multivector(state)
[28]:
../_images/examples_QFT_28_0.png

Now We check output of the distributed circuit

[29]:
dqc2_copy = dist_circ_2.copy()
dqc2_copy.save_statevector()
qobj = assemble(dqc2_copy)
state = sim.run(qobj).result().get_statevector()

state = partial_trace(state, [10,11,12,13]) ## Trace out the communication qubits (12 - 15)

plot_bloch_multivector(state)
[29]:
../_images/examples_QFT_30_0.png