FusionPlugin
is a tool for reducing the number of gates in a circuit through gates fusion.
Though primarily conceived with the intent of being used in front of statevector-based simulator, ie. LinAlg
, some strategies included in this plugin are compatible with any QPU topology (such as adjacent strategy, which does not increase the gates' sizes).
Here we'll show an example where the eager strategy can heavily reduce the number of gates in a circuit.
Let us start by creating a random circuit, composed of layers of 1 and 2 qbits gates.
from qat.lang.AQASM import Program, RX, RY, RZ, CNOT
import random as rdm
# here we'll consider a 10-qubits circuit with 20 layers
nbqubits = 24
circuit_depth = 20
program = Program()
qubits = program.qalloc(nbqubits)
for i in range(circuit_depth):
for qubit in qubits:
# on each qubit, apply a randomly selected pauli gate
rdm.choice([RX, RY, RZ])(rdm.random())(qubit)
for j in range(0, nbqubits, 2):
# some degree of intrication
CNOT(qubits[j - i % 2], qubits[j - i % 2 + 1])
job = program.to_circ().to_job()
Executing the cell below allows us to see that this circuit contains 720 gates, including 240 CNOT, the remaining gates being rotations.
print(job.circuit.statistics())
We'll first only compile this job, in order to see what the processed circuit looks like.
from qat.plugins import FusionPlugin
# both statements are equivalent, "eager" is the default strategy
plugin = FusionPlugin(strategy="eager")
plugin = FusionPlugin()
from qat.core import Batch, HardwareSpecs
batch = Batch(jobs=[job])
new_job = plugin.compile(batch, HardwareSpecs()).jobs[0]
Executing the cell bellow, we should see that the number of gates in the circuit has been reduced to 130. Notice that all those gates are "custom gates", meaning general gates that here are defined by a matrix.
print(new_job.circuit.statistics())
Finally, we can execute our job with a linear algebra based simulator. By default, the FusionPlugin is already applied by the simulator, so we need to deactivate it in order to compare the simulation time. Do consider that, at only 24 qubits, FusionPlugin takes a non-negligible amount of time of the total measured time.
from time import perf_counter
from qat.qpus import get_default_qpu
# here we instantiate the default QPU and deactivates the default FusionPlugin
qpu = get_default_qpu(fusion=False)
time = perf_counter()
qpu.submit(job)
print(f"Time taken by LinAlg: {perf_counter() - time}s")
# here we instantiate the default QPU without any argument, which applies the FusionPlugin by default
qpu = get_default_qpu()
time = perf_counter()
qpu.submit(job)
print(f"Time taken by FusionPlugin+LinAlg {perf_counter() - time}s")