BQSKit / bqskit

Berkeley Quantum Synthesis Toolkit
Other
106 stars 31 forks source link

Proposal to include echoed-resonance (ECR) gate into BQSkit. #216

Closed jagandecapri closed 1 month ago

jagandecapri commented 5 months ago

Hi,

I would like to propose the inclusion of echoed-resonance (ECR) gate into BQSKit. Reason being, I believe it will be useful when compiling to native gate sets of devices that have ECR gate as entangling gate, i.e: OQC Lucy. I've put together some code following the little-endian convention for qubit ordering similar to the implementation at here as following. I'm not super sure on the QASM string representation and I think the conversion to Qiskit fails due to the invalid QASM string.

"""This module implements the ECRGate."""
from __future__ import annotations
import numpy as np

from bqskit.ir.gates.constantgate import ConstantGate
from bqskit.ir.gates.qubitgate import QubitGate
from bqskit.qis.unitary.unitarymatrix import UnitaryMatrix

class ECRGate(ConstantGate, QubitGate):
    """
    The echoed cross-resonance gate.

    The ECR gate is given by the following unitary:

    .. math::

        \\frac{1}{\sqrt{2}}\begin{pmatrix} 
        0 & 1 & 0 & i \\\\
        1 & 0 & -i & 0 \\\\
        0 & i & 0 & 1 \\\\ 
        -i & 0 & 1 & 0 
        \\end{pmatrix}
    """

    _num_qudits = 2
    _qasm_name = 'opaque ecr'
    _utry = UnitaryMatrix(
        1/np.sqrt(2)*np.array([
            [0, 1, 0, 1j],
            [1, 0, -1j, 0],
            [0, 1j, 0, 1],
            [-1j, 0, 1, 0],
        ]),
    )

Code for testing as below. Assuming opaque ecr is the QASM 2.0 representation for ECR gate. Conversion to Qiskit circuit fails though.

from bqskit.ir import CircuitLocation
from bqskit.qis import RealVector

ecr = ECRGate()
print(ecr.get_unitary())

real_vector = []
circuit_location = CircuitLocation([3, 5])
print(ecr.get_qasm(real_vector, circuit_location))

from bqskit.ir.circuit import Circuit
from bqskit.ext import bqskit_to_qiskit

# Create a circuit with 2 qubits
circuit = Circuit(2)

# Add a ECR gate between the first and second qubits
circuit.append_gate(ECRGate(), [0, 1])

print(circuit.to('qasm'))

# Conversion to Qiskit fails because of invalid QASM 2.0 string?
qc = bqskit_to_qiskit(circuit)

QASM Output

OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
opaque ecr q[0], q[1];

Looking forward to your response! 🙂

edyounis commented 5 months ago

This is an awesome and welcome addition to BQSKit! If you put this up as a PR, we can get this merged in.

I am curious about the qasm issue. Looking at the qasm 2.0 grammar, it seems that this is valid qasm. What happens if you build a circuit in Qiskit with an ECR and print out its qasm?

jagandecapri commented 5 months ago

Thank you for looking into my suggestion. 🙂 I built a circuit using ecr gate in qiskit as below:

from qiskit import circuit

qc = circuit.QuantumCircuit(2)
qc.ecr(0, 1)

print(qc.qasm())

Output:

OPENQASM 2.0;
include "qelib1.inc";
gate rzx(param0) q0,q1 { h q1; cx q0,q1; rz(param0) q1; cx q0,q1; h q1; }
gate ecr q0,q1 { rzx(pi/4) q0,q1; x q0; rzx(-pi/4) q0,q1; }
qreg q[2];
ecr q[0],q[1];

It looks like Qiskit defines the decomposition of the ecr gate in the QASM file. I'm not sure whether this is the right approach when compiling for devices that natively support ecr gate.

edyounis commented 5 months ago

Yeah, this concept is always tricky with qasm. We took the approach in bqskit to try and recognize every common qasm gate and even some uncommon ones without needed definitions. This being said, I would assume Qiskit/IBM to be a good authority on both the qasm and ECR gate. This gives us a few options: we can mimic their functionality, go with a simple ecr q[0], q[1], or an opaque ecr gate. As long as the qasm that BQSKit prints, it can read the same, I am happy.

If you read that qasm file back in, does Qiskit recognize it as a single, simple ecr gate?

When you put up your PR, you may want to add an appropriate line in the qasm visitor to decode the ecr gate from a qasm file.

jagandecapri commented 5 months ago

I would like to refer to a way that we did to handle the ecr gate in our MQTBench toolkit at here. We post-process the QASM file to replace the decomposed ecr gate definition with opaque ecr q0, q1;. The ecr gates in the QASM file could be then read into Qiskit circuit as following (referred from here).

from qiskit import qasm2
from qiskit.circuit.library import ECRGate

qasm_str = """
OPENQASM 2.0;
include "qelib1.inc";
opaque ecr q0, q1;
qreg q[2];
ecr q[0], q[1];
"""

ecr = qasm2.CustomInstruction('ecr', 0, 2, ECRGate, builtin=False)
custom_instructions = qasm2.LEGACY_CUSTOM_INSTRUCTIONS + (ecr,)

qc = qasm2.loads(
    qasm_str,
    include_path=qasm2.LEGACY_INCLUDE_PATH,
    custom_instructions = custom_instructions,
    custom_classical=qasm2.LEGACY_CUSTOM_CLASSICAL,
    strict=False,
)

for gate in qc.data:
    print(gate[0].name)
    print(gate[0].to_matrix())

On the same line, in bqskit, maybe the ecr gate can be defined as opaque ecr q0 q1; and in the function that converts bqskit circuit to qiskit and vice-versa, there could be some logic to handle the ecr gate properly in the conversion process. Looking forward to your thoughts on this.

edyounis commented 5 months ago

I would personally prefer to mimic Qiskit's functionality. This can be accomplished with an override on the get_qasm_gate_def function. We did something similar in the iswap gate.

This would then not require any change to the qasm decoder, as the gate definitions are picked up by our circuitgate object.

edyounis commented 1 month ago

Completed as of #234