Skip to content

Commit

Permalink
Increase test coverage of control-flow operations
Browse files Browse the repository at this point in the history
  • Loading branch information
jakelishman committed Jan 29, 2024
1 parent 30e03b0 commit 94af67a
Showing 1 changed file with 127 additions and 22 deletions.
149 changes: 127 additions & 22 deletions test/python/circuit/test_circuit_load_from_qpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import numpy as np

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, pulse
from qiskit.circuit import CASE_DEFAULT
from qiskit.circuit import CASE_DEFAULT, IfElseOp, WhileLoopOp, SwitchCaseOp
from qiskit.circuit.classical import expr, types
from qiskit.circuit.classicalregister import Clbit
from qiskit.circuit.quantumregister import Qubit
Expand Down Expand Up @@ -77,6 +77,26 @@ def assertDeprecatedBitProperties(self, original, roundtripped):
original_clbits, roundtripped_clbits = zip(*owned_clbits)
self.assertEqual(original_clbits, roundtripped_clbits)

def assertMinimalVarEqual(self, left, right):
"""Replacement for asserting `QuantumCircuit` equality for use in `Var` tests, for use while
the `DAGCircuit` does not yet allow full equality checks. This should be removed and the
tests changed to directly call `assertEqual` once possible.
This filters out instructions that have `QuantumCircuit` parameters in the data comparison
(such as control-flow ops), which need to be handled separately."""
self.assertEqual(list(left.iter_input_vars()), list(right.iter_input_vars()))
self.assertEqual(list(left.iter_declared_vars()), list(right.iter_declared_vars()))
self.assertEqual(list(left.iter_captured_vars()), list(right.iter_captured_vars()))

def filter_ops(data):
return [
ins
for ins in data
if not any(isinstance(x, QuantumCircuit) for x in ins.operation.params)
]

self.assertEqual(filter_ops(left.data), filter_ops(right.data))

def test_qpy_full_path(self):
"""Test full path qpy serialization for basic circuit."""
qr_a = QuantumRegister(4, "a")
Expand Down Expand Up @@ -1727,10 +1747,6 @@ def test_clifford(self):

def test_load_empty_vars(self):
"""Test loading empty circuits with variables."""

# Note: this test doesn't use `QuantumCircuit.__eq__` because it's not yet defined for
# variables.

a = expr.Var.new("a", types.Bool())
b = expr.Var.new("b", types.Uint(8))
all_vars = {
Expand All @@ -1745,36 +1761,125 @@ def test_load_empty_vars(self):
dump(inputs, fptr)
fptr.seek(0)
new_inputs = load(fptr)[0]
self.assertEqual(list(inputs.iter_input_vars()), list(new_inputs.iter_input_vars()))
self.assertEqual(list(inputs.iter_declared_vars()), list(new_inputs.iter_declared_vars()))
self.assertEqual(list(inputs.iter_captured_vars()), list(new_inputs.iter_captured_vars()))
self.assertMinimalVarEqual(inputs, new_inputs)
self.assertDeprecatedBitProperties(inputs, new_inputs)

# Reversed order just to check there's no sorting shenanigans.
captures = QuantumCircuit(captures=list(all_vars)[::-1])
with io.BytesIO() as fptr:
dump(captures, fptr)
fptr.seek(0)
new_captures = load(fptr)[0]
self.assertEqual(list(captures.iter_input_vars()), list(new_captures.iter_input_vars()))
self.assertEqual(
list(captures.iter_declared_vars()), list(new_captures.iter_declared_vars())
)
self.assertEqual(
list(captures.iter_captured_vars()), list(new_captures.iter_captured_vars())
)
self.assertMinimalVarEqual(captures, new_captures)
self.assertDeprecatedBitProperties(captures, new_captures)

declares = QuantumCircuit(declarations=all_vars)
with io.BytesIO() as fptr:
dump(declares, fptr)
fptr.seek(0)
new_declares = load(fptr)[0]
self.assertEqual(list(declares.iter_input_vars()), list(new_declares.iter_input_vars()))
self.assertEqual(
list(declares.iter_declared_vars()), list(new_declares.iter_declared_vars())
)
self.assertEqual(
list(declares.iter_captured_vars()), list(new_declares.iter_captured_vars())
)
self.assertMinimalVarEqual(declares, new_declares)
self.assertDeprecatedBitProperties(declares, new_declares)

def test_load_empty_vars_if(self):
"""Test loading circuit with vars in if/else closures."""
a = expr.Var.new("a", types.Bool())
b = expr.Var.new("θψφ", types.Bool())
c = expr.Var.new("c", types.Uint(8))
d = expr.Var.new("🐍🐍🐍", types.Uint(8))

qc = QuantumCircuit(inputs=[a])
qc.add_var(b, expr.logic_not(a))
qc.add_var(c, expr.lift(0, c.type))
with qc.if_test(b) as else_:
qc.store(c, expr.lift(3, c.type))
with else_:
qc.add_var(d, expr.lift(7, d.type))

with io.BytesIO() as fptr:
dump(qc, fptr)
fptr.seek(0)
new_qc = load(fptr)[0]
self.assertMinimalVarEqual(qc, new_qc)
self.assertDeprecatedBitProperties(qc, new_qc)

old_if_else = qc.data[-1].operation
new_if_else = new_qc.data[-1].operation
# Sanity check for test.
self.assertIsInstance(old_if_else, IfElseOp)
self.assertIsInstance(new_if_else, IfElseOp)
self.assertEqual(len(old_if_else.blocks), len(new_if_else.blocks))

for old, new in zip(old_if_else.blocks, new_if_else.blocks):
self.assertMinimalVarEqual(old, new)
self.assertDeprecatedBitProperties(old, new)

def test_load_empty_vars_while(self):
"""Test loading circuit with vars in while closures."""
a = expr.Var.new("a", types.Bool())
b = expr.Var.new("θψφ", types.Bool())
c = expr.Var.new("🐍🐍🐍", types.Uint(8))

qc = QuantumCircuit(inputs=[a])
qc.add_var(b, expr.logic_not(a))
with qc.while_loop(b):
qc.add_var(c, expr.lift(7, c.type))

with io.BytesIO() as fptr:
dump(qc, fptr)
fptr.seek(0)
new_qc = load(fptr)[0]
self.assertMinimalVarEqual(qc, new_qc)
self.assertDeprecatedBitProperties(qc, new_qc)

old_while = qc.data[-1].operation
new_while = new_qc.data[-1].operation
# Sanity check for test.
self.assertIsInstance(old_while, WhileLoopOp)
self.assertIsInstance(new_while, WhileLoopOp)
self.assertEqual(len(old_while.blocks), len(new_while.blocks))

for old, new in zip(old_while.blocks, new_while.blocks):
self.assertMinimalVarEqual(old, new)
self.assertDeprecatedBitProperties(old, new)

def test_load_empty_vars_switch(self):
"""Test loading circuit with vars in switch closures."""
a = expr.Var.new("🐍🐍🐍", types.Uint(8))

qc = QuantumCircuit(1, 1, inputs=[a])
qc.measure(0, 0)
b_outer = qc.add_var("b", False)
with qc.switch(a) as case:
with case(0):
qc.store(b_outer, True)
with case(1):
qc.store(qc.clbits[0], False)
with case(2):
# Explicit shadowing.
qc.add_var("b", True)
with case(3):
qc.store(a, expr.lift(1, a.type))
with case(case.DEFAULT):
pass

with io.BytesIO() as fptr:
dump(qc, fptr)
fptr.seek(0)
new_qc = load(fptr)[0]
self.assertMinimalVarEqual(qc, new_qc)
self.assertDeprecatedBitProperties(qc, new_qc)

old_switch = qc.data[-1].operation
new_switch = new_qc.data[-1].operation
# Sanity check for test.
self.assertIsInstance(old_switch, SwitchCaseOp)
self.assertIsInstance(new_switch, SwitchCaseOp)
self.assertEqual(len(old_switch.blocks), len(new_switch.blocks))

for old, new in zip(old_switch.blocks, new_switch.blocks):
self.assertMinimalVarEqual(old, new)
self.assertDeprecatedBitProperties(old, new)


class TestSymengineLoadFromQPY(QiskitTestCase):
Expand Down

0 comments on commit 94af67a

Please sign in to comment.