How it works

To convert Python code into a quantum circuit, qlasskit implements a series of transformations:

  1. It begins with the Python AST (Abstract Syntax Tree), converting it into a more streamlined form using the ast2ast module.

  2. Next, the streamlined AST is translated into boolean expressions as an intermediate step by the ast2logic module. During this phase, boolean expressions are refined and optimized in preparation for the final transformation.

  3. Finally, the compiler module takes these optimized boolean expressions and compiles them into a quantum circuit.

Unlike other libraries that translate individual operations into quantum circuits before combining them, qlasskit constructs a single boolean expression for each output qubit of the entire function. This unique approach facilitates advanced optimization leveraging boolean algebraic properties.

Transformations

For instance, let assume we have the following function:

from qlasskit import qlassf, Qint
from qiskit import QuantumCircuit


@qlassf
def f_comp(b: bool, n: Qint[2]) -> Qint[2]:
    for i in range(3):
        n += 1 if b else 2
    return n

If we decompose the algorithm in 3 separate additions and we compile them separately, we obtain the following circuit:

@qlassf
def f1(b: bool, n: Qint[2]) -> Qint[2]:
    return n + (1 if b else 2)


qc = QuantumCircuit(f_comp.num_qubits * 2 - 1)

for i in range(3):
    qc.barrier(label=f"it_{i}")
    qc.append(f1.gate(), [0] + list(range(1 + i * 2, 5 + i * 2)))

print("Operations:", qc.decompose().count_ops())
qc.decompose().draw("mpl")
Operations: OrderedDict([('cx', 12), ('barrier', 3), ('x', 3), ('ccx', 3)])
_images/c8554185f0e7b6ce7bbb3518bc1a3fcfee1df87647abed38732acb382dabebb0.png

While if we compile the whole function to a quantum circuit using qlasskit, we obtain the following quantum circuit:

qc = QuantumCircuit(f_comp.num_qubits)
qc.append(f_comp.gate(), f_comp.qubits)

print("Operations:", qc.decompose().count_ops())
qc.decompose().draw("mpl")
Operations: OrderedDict([('cx', 4), ('x', 1), ('ccx', 1)])
_images/f04d51830be29018b0043e84d8b0b6760021972297e13a62948c8faf2876b5d4.png

As we can see from the circuit drawings, qlasskit approach needs half the number of qubits and half the number of gates.

AST Traslator

Given a python function, the qlasskit.ast2logic module walks its syntax tree translating all the statements / expressions to boolean expressions.

For instance, the following function:

@qlassf
def f(n: Qint[4]) -> bool:
    return n == 3

Is translated to this boolean expression:

print(f.expressions)
[(_ret, n.0 & n.1 & ~n.2 & ~n.3)]

Compiler

The boolean expressions are then being fed to the `qlasskit.compiler`` which compiles boolean expressions to invertible circuits, introducing auxiliary qubits. In this step, the compiler will automatically uncompute auxiliary qubits in order to reduce the number of qubits needed and the circuit footprint.

For the compilation, two backends are supported:

  • InternalCompiler

  • Tweedledum.xag_synth

Result

The result of the compiler is a quantum circuit represented with qlasskit QCircuit. This circuit can now be exported to one of the supported framework as a gate or as a standalone circuit.

The previous example function f, is translated to the following quantum circuit: the result is available at qubit q6.

f.export().draw("mpl")
_images/981b936f883d850a86045c786629348dc03c30f84ac897452ba323c175347acc.png