[2]:
from diskit import *
from qiskit import assemble, Aer, QuantumCircuit
from qiskit.visualization import plot_bloch_multivector
import warnings

warnings.filterwarnings("ignore")

Distributed Circuit for a CNOT gates

Define a Topology

[3]:
circuit_topo = Topology()
circuit_topo.create_qmap(2, [2, 3],"sys_cnot")
circuit_topo.qmap, circuit_topo.emap
[3]:
({'sys_cnot0': [Qubit(QuantumRegister(2, 'sys_cnot0'), 0),
   Qubit(QuantumRegister(2, 'sys_cnot0'), 1)],
  'sys_cnot1': [Qubit(QuantumRegister(3, 'sys_cnot1'), 0),
   Qubit(QuantumRegister(3, 'sys_cnot1'), 1),
   Qubit(QuantumRegister(3, 'sys_cnot1'), 2)]},
 {'sys_cnot0': Qubit(QuantumRegister(1, 'com_sys_cnot0'), 0),
  'sys_cnot1': Qubit(QuantumRegister(1, 'com_sys_cnot1'), 0)})

Get the registers

[3]:
qregs = circuit_topo.get_regs()

Define the monolithic Circuit

[4]:
qc = QuantumCircuit(*qregs)
qc.h(1)
qc.cx(0,2)
qc.draw('mpl')
[4]:
../_images/examples_CNOT_7_0.png

Create the remapper object based on topology defined

[5]:
remapper = CircuitRemapper(circuit_topo)

Convert the monolithic circuit to a Distributed circuit

[6]:
dist_circ = remapper.remap_circuit(qc)
dist_circ.draw(output='mpl')
[6]:
../_images/examples_CNOT_11_0.png

If there are consecutive gates with same control but with target on the same processor, we can do the distribution only once. Let’s look at the following example.

[7]:
qc2 = QuantumCircuit(*qregs)
qc2.h(1)
qc2.cx(0,2)
qc2.cx(0,4)
qc2.draw('mpl')
[7]:
../_images/examples_CNOT_13_0.png
[8]:
dist_circ2 = remapper.remap_circuit(qc2)
dist_circ2.draw(output='mpl')
[8]:
../_images/examples_CNOT_14_0.png

Let us check the validity of the working of the distributed version

First we check the output for monolithic circuit

[9]:
from qiskit.quantum_info import partial_trace
[10]:
sim = Aer.get_backend('aer_simulator')
[11]:
qc2_copy = qc2.copy()
qc2_copy.save_statevector()
qobj = assemble(qc2_copy)
state = sim.run(qobj).result().get_statevector()

state = partial_trace(state, [5,6]) ## Trace out the communication qubits

plot_bloch_multivector(state)
[11]:
../_images/examples_CNOT_19_0.png

Now We check output of the distributed circuit

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

state = partial_trace(state, [5,6]) ## Trace out the communication qubits

plot_bloch_multivector(state)
[12]:
../_images/examples_CNOT_21_0.png

As can be seen, the output of both circuits are equivalent

The case of higher order gates

In the case of gates with more than 2 qubits, we shall need to decompose the circuit to 2 and 1 qubit gates for the distribution to work

[13]:
qregs = circuit_topo.get_regs()
qc = QuantumCircuit(*qregs)
qc.ccx(0, 2, 4)
qc.draw('mpl')
[13]:
../_images/examples_CNOT_24_0.png

Trying to distribute the above circuit shall result in an error.

[15]:
dist_circ = remapper.remap_circuit(qc)
dist_circ.draw(output='mpl')
---------------------------------------------------------------------------
CircuitError                              Traceback (most recent call last)
~\AppData\Local\Temp\ipykernel_23916\250667637.py in <module>
----> 1 dist_circ = remapper.remap_circuit(qc)
      2 dist_circ.draw(output='mpl')

E:\distributed-qiskit\distributed_qiskit\circuit_remapper.py in remap_circuit(self, circuit, decompose, decompose_list)
    221             if incompatible:
    222                 raise CircuitError(
--> 223                     "The circuit contains incompatible gates, Please try decompose=True keyword argument.")
    224
    225         layers = self._circuit_to_layers(circuit=circuit)

CircuitError: 'The circuit contains incompatible gates, Please try decompose=True keyword argument.'

To bypass one needs to put decompose=True which shall iteratively perform decomposition until circuit is ready for distribution.

[16]:
dist_circ = remapper.remap_circuit(qc, decompose=True)
dist_circ.draw(output='mpl')
[16]:
../_images/examples_CNOT_28_0.png