mirror of https://github.com/Qiskit/qiskit.git
Fix OpenQASM 3 built-ins with classical conditions (#8731)
The `.condition` attribute for `Instruction` instances corresponding to OpenQASM 3 built-ins (such as `reset` and `measure`) would previously be ignored. This now correctly nests them inside a a branching statement in the exporter, so the condition is preserved. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
This commit is contained in:
parent
780099972c
commit
bcec9a314c
|
@ -779,45 +779,49 @@ class QASM3Builder:
|
|||
"""Builds a list of call statements"""
|
||||
ret = []
|
||||
for instruction in instructions:
|
||||
if isinstance(instruction.operation, ForLoopOp):
|
||||
ret.append(self.build_for_loop(instruction))
|
||||
continue
|
||||
if isinstance(instruction.operation, WhileLoopOp):
|
||||
ret.append(self.build_while_loop(instruction))
|
||||
continue
|
||||
if isinstance(instruction.operation, IfElseOp):
|
||||
ret.append(self.build_if_statement(instruction))
|
||||
continue
|
||||
# Build the node, ignoring any condition.
|
||||
if isinstance(instruction.operation, Gate):
|
||||
if instruction.operation.condition:
|
||||
eqcondition = self.build_eqcondition(instruction.operation.condition)
|
||||
operation_without_condition = instruction.operation.copy()
|
||||
operation_without_condition.condition = None
|
||||
true_body = self.build_program_block(
|
||||
[instruction.replace(operation=operation_without_condition)]
|
||||
)
|
||||
ret.append(ast.BranchingStatement(eqcondition, true_body))
|
||||
else:
|
||||
ret.append(self.build_gate_call(instruction))
|
||||
nodes = [self.build_gate_call(instruction)]
|
||||
elif isinstance(instruction.operation, Barrier):
|
||||
operands = [
|
||||
self.build_single_bit_reference(operand) for operand in instruction.qubits
|
||||
]
|
||||
ret.append(ast.QuantumBarrier(operands))
|
||||
nodes = [ast.QuantumBarrier(operands)]
|
||||
elif isinstance(instruction.operation, Measure):
|
||||
measurement = ast.QuantumMeasurement(
|
||||
[self.build_single_bit_reference(operand) for operand in instruction.qubits]
|
||||
)
|
||||
qubit = self.build_single_bit_reference(instruction.clbits[0])
|
||||
ret.append(ast.QuantumMeasurementAssignment(qubit, measurement))
|
||||
nodes = [ast.QuantumMeasurementAssignment(qubit, measurement)]
|
||||
elif isinstance(instruction.operation, Reset):
|
||||
for operand in instruction.qubits:
|
||||
ret.append(ast.QuantumReset(self.build_single_bit_reference(operand)))
|
||||
nodes = [
|
||||
ast.QuantumReset(self.build_single_bit_reference(operand))
|
||||
for operand in instruction.qubits
|
||||
]
|
||||
elif isinstance(instruction.operation, Delay):
|
||||
ret.append(self.build_delay(instruction))
|
||||
elif isinstance(instruction.operation, ForLoopOp):
|
||||
ret.append(self.build_for_loop(instruction))
|
||||
elif isinstance(instruction.operation, WhileLoopOp):
|
||||
ret.append(self.build_while_loop(instruction))
|
||||
elif isinstance(instruction.operation, IfElseOp):
|
||||
ret.append(self.build_if_statement(instruction))
|
||||
nodes = [self.build_delay(instruction)]
|
||||
elif isinstance(instruction.operation, BreakLoopOp):
|
||||
ret.append(ast.BreakStatement())
|
||||
nodes = [ast.BreakStatement()]
|
||||
elif isinstance(instruction.operation, ContinueLoopOp):
|
||||
ret.append(ast.ContinueStatement())
|
||||
nodes = [ast.ContinueStatement()]
|
||||
else:
|
||||
ret.append(self.build_subroutine_call(instruction))
|
||||
nodes = [self.build_subroutine_call(instruction)]
|
||||
|
||||
if instruction.operation.condition is None:
|
||||
ret.extend(nodes)
|
||||
else:
|
||||
eqcondition = self.build_eqcondition(instruction.operation.condition)
|
||||
body = ast.ProgramBlock(nodes)
|
||||
ret.append(ast.BranchingStatement(eqcondition, body))
|
||||
return ret
|
||||
|
||||
def build_if_statement(self, instruction: CircuitInstruction) -> ast.BranchingStatement:
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
fixes:
|
||||
- |
|
||||
The OpenQASM 3 exporter (:mod:`qiskit.qasm3`) will now correctly handle
|
||||
OpenQASM built-ins (such as ``reset`` and ``measure``) that have a classical
|
||||
condition applied by :meth:`~.InstructionSet.c_if`. Previously the condition
|
||||
would have been ignored.
|
|
@ -22,7 +22,7 @@ import unittest
|
|||
from ddt import ddt, data
|
||||
|
||||
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit, transpile
|
||||
from qiskit.circuit import Parameter, Qubit, Clbit, Instruction, Gate
|
||||
from qiskit.circuit import Parameter, Qubit, Clbit, Instruction, Gate, Delay, Barrier
|
||||
from qiskit.test import QiskitTestCase
|
||||
from qiskit.qasm3 import Exporter, dumps, dump, QASM3ExporterError
|
||||
from qiskit.qasm3.exporter import QASM3Builder
|
||||
|
@ -1696,6 +1696,60 @@ class TestCircuitQASM3ExporterTemporaryCasesWithBadParameterisation(QiskitTestCa
|
|||
)
|
||||
self.assertEqual(Exporter(includes=[]).dumps(circuit), expected_qasm)
|
||||
|
||||
def test_unusual_conditions(self):
|
||||
"""Test that special QASM constructs such as ``measure`` are correctly handled when the
|
||||
Terra instructions have old-style conditions."""
|
||||
qc = QuantumCircuit(3, 2)
|
||||
qc.h(0)
|
||||
qc.measure(0, 0)
|
||||
qc.measure(1, 1).c_if(0, True)
|
||||
qc.reset([0, 1]).c_if(0, True)
|
||||
with qc.while_loop((qc.clbits[0], True)):
|
||||
qc.break_loop().c_if(0, True)
|
||||
qc.continue_loop().c_if(0, True)
|
||||
# Terra forbids delay and barrier from being conditioned through `c_if`, but in theory they
|
||||
# should work fine in a dynamic-circuits sense (although what a conditional barrier _means_
|
||||
# is a whole other kettle of fish).
|
||||
delay = Delay(16, "dt")
|
||||
delay.condition = (qc.clbits[0], True)
|
||||
qc.append(delay, [0], [])
|
||||
barrier = Barrier(2)
|
||||
barrier.condition = (qc.clbits[0], True)
|
||||
qc.append(barrier, [0, 1], [])
|
||||
|
||||
expected = """
|
||||
OPENQASM 3;
|
||||
include "stdgates.inc";
|
||||
bit[2] c;
|
||||
qubit[3] _all_qubits;
|
||||
let q = _all_qubits[0:2];
|
||||
h q[0];
|
||||
c[0] = measure q[0];
|
||||
if (c[0] == 1) {
|
||||
c[1] = measure q[1];
|
||||
}
|
||||
if (c[0] == 1) {
|
||||
reset q[0];
|
||||
}
|
||||
if (c[0] == 1) {
|
||||
reset q[1];
|
||||
}
|
||||
while (c[0] == 1) {
|
||||
if (c[0] == 1) {
|
||||
break;
|
||||
}
|
||||
if (c[0] == 1) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (c[0] == 1) {
|
||||
delay[16dt] q[0];
|
||||
}
|
||||
if (c[0] == 1) {
|
||||
barrier q[0], q[1];
|
||||
}"""
|
||||
self.assertEqual(dumps(qc).strip(), expected.strip())
|
||||
|
||||
|
||||
@ddt
|
||||
class TestQASM3ExporterFailurePaths(QiskitTestCase):
|
||||
|
|
Loading…
Reference in New Issue