Qiskit / qiskit

Qiskit is an open-source SDK for working with quantum computers at the level of extended quantum circuits, operators, and primitives.
https://www.ibm.com/quantum/qiskit
Apache License 2.0
4.98k stars 2.32k forks source link

`QuantumCircuit.qasm` cannot handle nested gates with different parameters #9805

Closed Durd3nT closed 1 year ago

Durd3nT commented 1 year ago

Environment

What is happening?

Having a QuantumCircuit containing nested gates, such as PauliEvolutionGate, with different parameters leads to issues when translating these circuits to qasm strings. Qasm defines a new gate for these nested gates with parameters but discards gates that contain the same nested gates with different parameters.

The resulting qasm string looks like this, containing a duplicate definition of the gates exp_it_ZZ_ and exp_it_XI_:

OPENQASM 2.0;
include "qelib1.inc";
gate exp_it_ZZ_ q0,q1 { rzz(20.0) q0,q1; }
gate exp_it_XI_ q0,q1 { rx(20.0) q1; }
gate exp_it_ZZ_ q0,q1 { rzz(2.0) q0,q1; }
gate exp_it_XI_ q0,q1 { rx(2.0) q1; }
gate gate_PauliEvolution(param0) q0,q1 { exp_it_XI_ q0,q1; exp_it_ZZ_ q0,q1; }
gate gate_PauliEvolution_140709141623472(param0) q0,q1 { exp_it_XI_ q0,q1; exp_it_ZZ_ q0,q1; }
qreg q[2];
h q[0];
h q[1];
gate_PauliEvolution(1.0) q[0],q[1];
gate_PauliEvolution_140709141623472(1.0) q[0],q[1];

The result is that only one definition of these gates is used, ignoring the fact that they are used multiple times with different parameterizations.

This is a problem when sending such circuits to the cloud and results in the wrong circuits bein evaluated.

How can we reproduce the issue?

from qiskit.quantum_info import SparsePauliOp
from qiskit import QuantumCircuit
from qiskit.circuit.library import PauliEvolutionGate

circ = QuantumCircuit(1)
evo_gate1 = PauliEvolutionGate(
    SparsePauliOp(["X", "Z"], [1, 1]),
    1
)

evo_gate2 = PauliEvolutionGate(
    SparsePauliOp(["X", "Z"], [10, 10]),
    1
)

circ.append(evo_gate1, circ.qubits)
circ.append(evo_gate2, circ.qubits)

qasm_str = circ.qasm()
rebuilt = QuantumCircuit.from_qasm_str(qasm_str)

Output: qiskit.qasm.exceptions.QasmError: "Duplicate declaration for gate 'exp_it_Z_' at line 5, file .\nPrevious occurrence at line 3, file "

What should happen?

Qasm should define distinct gates for each nested gate with a distinct parameterization.

Any suggestions?

No response

Durd3nT commented 1 year ago

The code below shows that this issue is more deeply rooted and does not only apply to the PauliEvolutionGate, but to nested gates generally.

from qiskit import QuantumCircuit
from qiskit.circuit import Gate

class Internal(Gate):
    def __init__(self, x):
        super().__init__("internal", 1, [x], label=f"I'm {x:.2f}")

    def _define(self):
        definition = QuantumCircuit(1)
        definition.rx(self.params[0], 0)
        self._definition = definition

class MyGate(Gate):
    def __init__(self, x):
        super().__init__("breakstuff", 1, [x], label=f"I'm {x:.2f}")

    def _define(self):
        definition = QuantumCircuit(1)
        definition.append(Internal(self.params[0]), [0])
        self._definition = definition

gate1, gate2 = MyGate(1), MyGate(2)
circ = QuantumCircuit(1)
circ.append(gate1, [0])
circ.append(gate2, [0])

qasm_str = circ.qasm()
print(qasm_str)
rebuilt = QuantumCircuit.from_qasm_str(qasm_str)

Output Qasm string from print statement:

OPENQASM 2.0;
include "qelib1.inc";
gate internal(param0) q0 { rx(2.0) q0; }
gate internal(param0) q0 { rx(1.0) q0; }
gate breakstuff(param0) q0 { internal(1.0) q0; }
gate breakstuff_4533491840(param0) q0 { internal(2.0) q0; }
qreg q[1];
breakstuff(1.0) q[0];
breakstuff_4533491840(2.0) q[0];

Error raised in the last line of code (same as above): qiskit.qasm.exceptions.QasmError: "Duplicate declaration for gate 'internal' at line 4, file .\nPrevious occurrence at line 3, file "

1ucian0 commented 1 year ago

It seems like the exporter does not check the namespace of declared gates.