Conversion of Qiskit circuits into Qaptiva ones and vice versa¶

This notebook will give you informations on the differents methods that you can use to convert a Qiskit circuit into Qaptiva one and vice versa, by using the functions:

  • qiskit_to_qaptiva
  • qaptiva_to_qiskit

Or by using the classes:

  • QiskitToQaptiva
  • QaptivaToQiskit

From Qaptiva to Qiskit¶

qat.qiskit introduce to you 2 methods to transform a Qaptiva circuit into a Qiskit one.

For both of these methods, the known translatable gates are H, X, Y, Z, SWAP, I, S, S.dag(), T, T.dag(), RX, RY, RZ, H.ctrl(), CNOT, Y.ctrl(), CSIGN, RZ.ctrl(), CCNOT, SWAP.ctrl(), U, RXX, RZZ, R, MS.

Function qaptiva_to_qiskit¶

This function only takes the Qaptiva circuit as an argument:

Warning: this function do not support custom gate, and intermediate measurement.

In [1]:
from qat.qiskit.converters import qaptiva_to_qiskit
from qat.lang.AQASM import Program
from qat.lang.AQASM.gates import H, CNOT

nbqubits = 2

prog = Program()

qreg = prog.qalloc(nbqubits)
creg = prog.calloc(nbqubits)

# Create a circuit
prog.apply(H, qreg[0])
prog.apply(CNOT, qreg[0], qreg[1])

qlm_circuit = prog.to_circ()

# Conversion
qiskit_circuit = qaptiva_to_qiskit(qlm_circuit)
print(qiskit_circuit)
      ┌───┐     
q0_0: ┤ H ├──■──
      └───┘┌─┴─┐
q0_1: ─────┤ X ├
           └───┘

Class QaptivaToQiskit¶

This class converts a Qaptiva circuit into a Qiskit one. Additionally, this class can also translate custom gate if passed in argument. The constructor of this class takes one optionnal argument: gates_dict which defines the translation of these custom gates.

After creating the class, 2 methods are available for the translation of the circuit:

  • to_circ: to transform the Qaptiva circuit into a qiskit one.
  • job_to_circ: to transform the Qaptiva job into a Qiskit circuit one.

So before using this class, let's create an AbstactGate.

In [2]:
import numpy as np
from qat.lang import AbstractGate
from qiskit.circuit import Gate as QiskitGate

def phase_matrix(theta):
    return np.diag([1, np.exp(1j * theta)])

# Creation of an Abstact gate in Qaptiva
PhaseGateQaptiva = AbstractGate("Phase", [float], matrix_generator=phase_matrix, arity=1)

# Creation of the same custom gate in qiskit
class PhaseGateQiskit(QiskitGate):
    def __init__(self, theta):
        # Initialize the custom gate with name "Phase" of arity 1
        super().__init__("Phase", 1, [theta])
        self._theta = theta

    def to_matrix(self):
        return phase_matrix(self._theta)

Now, let's translate the circuit.

In [3]:
from qat.lang import qrout, H, CNOT
from qat.qiskit.converters import QaptivaToQiskit

@qrout
def circ():
    H(0)
    CNOT(0, 1)
    PhaseGateQaptiva(1.23)(0)

circuit = circ().to_circ()

translator = QaptivaToQiskit({"Phase": PhaseGateQiskit})
print(translator.to_circ(circuit))
      ┌───┐     ┌─────────────┐
q1_0: ┤ H ├──■──┤ Phase(1.23) ├
      └───┘┌─┴─┐└─────────────┘
q1_1: ─────┤ X ├───────────────
           └───┘               

From Qiskit to Qaptiva¶

For this conversion, qat.qiskit introduces also 2 methods.

1. Function qiskit_to_qaptiva¶

This function can take 3 arguments where 1 is optional:

  • qiskit_circuit: The Qiskit circuit to convert.
  • gates_dict (dict): A dictionary mapping specific qiskit gates.
  • sep_measure (optional, bool): if set to True, measures won’t be included in the resulting circuit. This method will then return a tuple composed of the translated circuit with no measument, and the list of measured qubits.

Note: This function can be given custom cate in gates_dict and translate them into the Qaptiva circuit.

Let's fist use the function without the argument sep_measure.

In [4]:
from qat.qiskit.converters import qiskit_to_qaptiva
from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister
from qat.core.util import get_syntax

nbqubits = 2
qreg = QuantumRegister(nbqubits)
creg = ClassicalRegister(nbqubits)

qiskit_circuit = QuantumCircuit(qreg, creg)

qiskit_circuit.h(qreg[0])
qiskit_circuit.cx(qreg[0], qreg[1])
# Add of a custom gate
qiskit_circuit.append(PhaseGateQiskit(1.23), [qreg[0]])
qiskit_circuit.measure(qreg, creg)

# get result with included measures
qlm_circuit = qiskit_to_qaptiva(qiskit_circuit, {"Phase": PhaseGateQaptiva})
qlm_circuit.display()
No description has been provided for this image

As we can see, with sep_measureset to False, the measure done on the circuit are translated to the Qaptiva circuit.

If we take the same circuit and put sep_measure to True:

In [5]:
# Convert circuit with separated measurements
qlm_circuit, to_measure = qiskit_to_qaptiva(
    qiskit_circuit,
    {"Phase": PhaseGateQaptiva},
    sep_measures=True
)

qlm_circuit.display()
print(f"the Qiskit circuit measured the qubits in this list: {to_measure}.")
No description has been provided for this image
the Qiskit circuit measured the qubits in this list: [0, 1].

2. Use of the class QiskitToQaptiva¶

Like the precedent class, QaptivaToQiskit, this class takes one optionnal argument: gates_dict which defines the translation of these custom gates.

However, the function of this class changes:

  • to_circ: now, this function can takes as an argument sep_measure (bool) to remove the measure on the circuit and return a tuple of the circuit and the list of measured qubits.
  • to_job: will take a qiskit circuit, the observable and a list of qubits to return a Qaptiva job.
In [6]:
from qat.qiskit.converters import QiskitToQaptiva
from qat.core import Observable, Term

qiskit_circuit = QuantumCircuit(2)

qiskit_circuit.h(0)
qiskit_circuit.cx(0, 1)
qiskit_circuit.append(PhaseGateQiskit(0.5), [0])

converter = QiskitToQaptiva({"Phase": PhaseGateQaptiva})

# Translation of the circuit into the Qaptiva one
qaptiva_circuit = converter.to_circ(qiskit_circuit)
qaptiva_circuit.display()

# Add of observable to make the to_job
obs = Observable(2, pauli_terms=[Term(1., "ZZ", [0, 1])])
qaptiva_job = converter.to_job(qiskit_circuit, observable=obs)
No description has been provided for this image