mirror of https://github.com/Qiskit/qiskit.git
194 lines
7.1 KiB
Python
194 lines
7.1 KiB
Python
# This code is part of Qiskit.
|
|
#
|
|
# (C) Copyright IBM 2017, 2018.
|
|
#
|
|
# This code is licensed under the Apache License, Version 2.0. You may
|
|
# obtain a copy of this license in the LICENSE.txt file in the root directory
|
|
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
|
|
#
|
|
# Any modifications or derivative works of this code must retain this
|
|
# copyright notice, and modified files need to carry a notice indicating
|
|
# that they have been altered from the originals.
|
|
|
|
# pylint: disable=missing-class-docstring
|
|
|
|
"""Test the passmanager logic"""
|
|
|
|
import copy
|
|
|
|
import numpy as np
|
|
|
|
from qiskit import QuantumRegister, QuantumCircuit
|
|
from qiskit.circuit.library import U2Gate
|
|
from qiskit.converters import circuit_to_dag
|
|
from qiskit.passmanager.flow_controllers import (
|
|
FlowControllerLinear,
|
|
ConditionalController,
|
|
DoWhileController,
|
|
)
|
|
from qiskit.transpiler import PassManager, PropertySet, TransformationPass
|
|
from qiskit.transpiler.passes import Optimize1qGates, BasisTranslator, ResourceEstimation
|
|
from qiskit.circuit.library.standard_gates.equivalence_library import (
|
|
StandardEquivalenceLibrary as std_eqlib,
|
|
)
|
|
from test import QiskitTestCase # pylint: disable=wrong-import-order
|
|
|
|
|
|
class TestPassManager(QiskitTestCase):
|
|
"""Test Pass manager logic."""
|
|
|
|
def test_callback(self):
|
|
"""Test the callback parameter."""
|
|
qr = QuantumRegister(1, "qr")
|
|
circuit = QuantumCircuit(qr, name="MyCircuit")
|
|
circuit.h(qr[0])
|
|
circuit.h(qr[0])
|
|
circuit.h(qr[0])
|
|
expected_start = QuantumCircuit(qr)
|
|
expected_start.append(U2Gate(0, np.pi), [qr[0]])
|
|
expected_start.append(U2Gate(0, np.pi), [qr[0]])
|
|
expected_start.append(U2Gate(0, np.pi), [qr[0]])
|
|
expected_start_dag = circuit_to_dag(expected_start)
|
|
|
|
expected_end = QuantumCircuit(qr)
|
|
expected_end.append(U2Gate(0, np.pi), [qr[0]])
|
|
expected_end_dag = circuit_to_dag(expected_end)
|
|
|
|
calls = []
|
|
|
|
def callback(**kwargs):
|
|
out_dict = kwargs
|
|
out_dict["dag"] = copy.deepcopy(kwargs["dag"])
|
|
calls.append(out_dict)
|
|
|
|
passmanager = PassManager()
|
|
passmanager.append(BasisTranslator(std_eqlib, ["u2"]))
|
|
passmanager.append(Optimize1qGates())
|
|
passmanager.run(circuit, callback=callback)
|
|
self.assertEqual(len(calls), 2)
|
|
self.assertEqual(len(calls[0]), 5)
|
|
self.assertEqual(calls[0]["count"], 0)
|
|
self.assertEqual(calls[0]["pass_"].name(), "BasisTranslator")
|
|
self.assertEqual(expected_start_dag, calls[0]["dag"])
|
|
self.assertIsInstance(calls[0]["time"], float)
|
|
self.assertEqual(calls[0]["property_set"], PropertySet())
|
|
self.assertEqual("MyCircuit", calls[0]["dag"].name)
|
|
self.assertEqual(len(calls[1]), 5)
|
|
self.assertEqual(calls[1]["count"], 1)
|
|
self.assertEqual(calls[1]["pass_"].name(), "Optimize1qGates")
|
|
self.assertEqual(expected_end_dag, calls[1]["dag"])
|
|
self.assertIsInstance(calls[0]["time"], float)
|
|
self.assertEqual(calls[0]["property_set"], PropertySet())
|
|
self.assertEqual("MyCircuit", calls[1]["dag"].name)
|
|
|
|
def test_callback_with_pass_requires(self):
|
|
"""Test the callback with a pass with pass requirements."""
|
|
qr = QuantumRegister(3, "qr")
|
|
circuit = QuantumCircuit(qr, name="MyCircuit")
|
|
circuit.z(qr[0])
|
|
circuit.cx(qr[0], qr[2])
|
|
circuit.z(qr[0])
|
|
expected_start = QuantumCircuit(qr)
|
|
expected_start.z(qr[0])
|
|
expected_start.cx(qr[0], qr[2])
|
|
expected_start.z(qr[0])
|
|
expected_start_dag = circuit_to_dag(expected_start)
|
|
|
|
expected_end = QuantumCircuit(qr)
|
|
expected_end.cx(qr[0], qr[2])
|
|
|
|
calls = []
|
|
|
|
def callback(**kwargs):
|
|
out_dict = kwargs
|
|
out_dict["dag"] = copy.deepcopy(kwargs["dag"])
|
|
calls.append(out_dict)
|
|
|
|
passmanager = PassManager()
|
|
passmanager.append(ResourceEstimation())
|
|
passmanager.run(circuit, callback=callback)
|
|
|
|
self.assertEqual(len(calls), 7)
|
|
|
|
required_passes = [
|
|
"Depth",
|
|
"Width",
|
|
"Size",
|
|
"CountOps",
|
|
"NumTensorFactors",
|
|
"NumQubits",
|
|
"ResourceEstimation",
|
|
]
|
|
|
|
for call_entry in range(7):
|
|
self.assertEqual(len(calls[call_entry]), 5)
|
|
self.assertEqual(calls[call_entry]["count"], call_entry)
|
|
self.assertEqual(calls[call_entry]["pass_"].name(), required_passes[call_entry])
|
|
self.assertEqual(expected_start_dag, calls[call_entry]["dag"])
|
|
self.assertIsInstance(calls[call_entry]["time"], float)
|
|
self.assertIsInstance(calls[call_entry]["property_set"], PropertySet)
|
|
self.assertEqual("MyCircuit", calls[call_entry]["dag"].name)
|
|
|
|
def test_to_flow_controller(self):
|
|
"""Test that conversion to a `FlowController` works, and the result can be added to a
|
|
circuit and conditioned, with the condition only being called once."""
|
|
|
|
class DummyPass(TransformationPass):
|
|
def __init__(self, x):
|
|
super().__init__()
|
|
self.x = x
|
|
|
|
def run(self, dag):
|
|
return dag
|
|
|
|
def repeat(count):
|
|
def condition(_):
|
|
nonlocal count
|
|
if not count:
|
|
return False
|
|
count -= 1
|
|
return True
|
|
|
|
return condition
|
|
|
|
def make_inner(prefix):
|
|
inner = PassManager()
|
|
inner.append(DummyPass(f"{prefix} 1"))
|
|
inner.append(ConditionalController(DummyPass(f"{prefix} 2"), condition=lambda _: False))
|
|
inner.append(ConditionalController(DummyPass(f"{prefix} 3"), condition=lambda _: True))
|
|
inner.append(DoWhileController(DummyPass(f"{prefix} 4"), do_while=repeat(1)))
|
|
return inner.to_flow_controller()
|
|
|
|
self.assertIsInstance(make_inner("test"), FlowControllerLinear)
|
|
|
|
outer = PassManager()
|
|
outer.append(make_inner("first"))
|
|
outer.append(ConditionalController(make_inner("second"), condition=lambda _: False))
|
|
# The intent of this `condition=repeat(1)` is to ensure that the outer condition is only
|
|
# checked once and not flattened into the inner controllers; an inner pass invalidating the
|
|
# condition should not affect subsequent passes once the initial condition was met.
|
|
outer.append(ConditionalController(make_inner("third"), condition=repeat(1)))
|
|
|
|
calls = []
|
|
|
|
def callback(pass_, **_):
|
|
self.assertIsInstance(pass_, DummyPass)
|
|
calls.append(pass_.x)
|
|
|
|
outer.run(QuantumCircuit(), callback=callback)
|
|
|
|
expected = [
|
|
"first 1",
|
|
"first 3",
|
|
# it's a do-while loop, not a while, which is why the `repeat(1)` gives two calls
|
|
"first 4",
|
|
"first 4",
|
|
# If the outer pass-manager condition is called more than once, then only the first of
|
|
# the `third` passes will appear.
|
|
"third 1",
|
|
"third 3",
|
|
"third 4",
|
|
"third 4",
|
|
]
|
|
self.assertEqual(calls, expected)
|