Skip to content

Commit c9e5ce2

Browse files
authored
🚸♻️ improved verify functions (#117)
- `verify` function refactored from C++ binding function to Python function in analogy to `verify_compilation_flow` - allow to pass keyword arguments to `verify` functions instead of configuration object
1 parent 6a09dfc commit c9e5ce2

File tree

6 files changed

+81
-30
lines changed

6 files changed

+81
-30
lines changed

Diff for: mqt/qcec/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
EquivalenceCheckingManager,
1111
EquivalenceCriterion,
1212
StateType,
13-
verify,
1413
)
14+
from mqt.qcec.verify import verify
1515
from mqt.qcec.verify_compilation_flow import verify_compilation
1616

1717
__all__ = [

Diff for: mqt/qcec/bindings.cpp

-16
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,6 @@ static std::unique_ptr<EquivalenceCheckingManager> createManagerFromOptions(
149149
return createManagerFromConfiguration(circ1, circ2, configuration);
150150
}
151151

152-
static ec::EquivalenceCheckingManager::Results
153-
verify(const py::object& circ1, const py::object& circ2,
154-
const Configuration& configuration = Configuration()) {
155-
const auto ecm = createManagerFromConfiguration(circ1, circ2, configuration);
156-
ecm->run();
157-
return ecm->getResults();
158-
}
159-
160152
PYBIND11_MODULE(pyqcec, m) {
161153
m.doc() = "Python interface for the MQT QCEC quantum circuit equivalence "
162154
"checking tool";
@@ -731,14 +723,6 @@ PYBIND11_MODULE(pyqcec, m) {
731723
"exponentially, this is only recommended for a small number of "
732724
"qubits and defaults to :code:`False`.");
733725

734-
m.def("verify", &verify, "circ1"_a, "circ2"_a,
735-
"config"_a = ec::Configuration{},
736-
"Convenience function for verifying the equivalence of two circuits. "
737-
"Wraps creating an instance of :class:`EquivalenceCheckingManager "
738-
"<.EquivalenceCheckingManager>`, calling "
739-
":meth:`EquivalenceCheckingManager.run` and calling "
740-
":meth:`EquivalenceCheckingManager.get_result`.");
741-
742726
#ifdef VERSION_INFO
743727
m.attr("__version__") = MACRO_STRINGIFY(VERSION_INFO);
744728
#else

Diff for: mqt/qcec/verify.py

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from __future__ import annotations
2+
3+
from typing import Any
4+
5+
from mqt.qcec import Configuration, EquivalenceCheckingManager
6+
from qiskit import QuantumCircuit
7+
8+
9+
def verify(
10+
circ1: QuantumCircuit | str, circ2: QuantumCircuit | str, configuration: Configuration | None = None, **kwargs: Any
11+
) -> EquivalenceCheckingManager.Results:
12+
"""
13+
Verify that ``circ1`` and ``circ2`` are equivalent.
14+
Wraps creating an instance of :class:`EquivalenceCheckingManager <.EquivalenceCheckingManager>`,
15+
calling :meth:`EquivalenceCheckingManager.run`,
16+
and calling :meth:`EquivalenceCheckingManager.get_result`.
17+
If ``configuration`` is not ``None``, it is used to configure the ``EquivalenceCheckingManager``.
18+
"""
19+
20+
if kwargs:
21+
# create the equivalence checker from keyword arguments
22+
ecm = EquivalenceCheckingManager(circ1, circ2, **kwargs)
23+
else:
24+
if configuration is None:
25+
configuration = Configuration()
26+
27+
# create the equivalence checker from configuration
28+
ecm = EquivalenceCheckingManager(circ1, circ2, configuration)
29+
30+
# execute the check
31+
ecm.run()
32+
33+
# obtain the result
34+
return ecm.get_results()

Diff for: mqt/qcec/verify_compilation_flow.py

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
import sys
4-
from typing import TYPE_CHECKING
4+
from typing import TYPE_CHECKING, Any
55

66
if TYPE_CHECKING or sys.version_info < (3, 9, 0):
77
import importlib_resources as resources
@@ -19,17 +19,22 @@ def verify_compilation(
1919
optimization_level: int = 1,
2020
ancilla_mode: AncillaMode = AncillaMode.NO_ANCILLA,
2121
configuration: Configuration | None = None,
22+
**kwargs: Any,
2223
) -> EquivalenceCheckingManager.Results:
2324
"""
2425
Verify that the ``compiled_circuit`` (compiled with a certain ``optimization_level`` amd ``ancilla_mode``) is equivalent to the ``original_circuit``.
2526
If ``configuration`` is not ``None``, it is used to configure the ``EquivalenceCheckingManager``.
2627
"""
2728

28-
if configuration is None:
29-
configuration = Configuration()
29+
if kwargs:
30+
# create the equivalence checker from keyword arguments
31+
ecm = EquivalenceCheckingManager(original_circuit, compiled_circuit, **kwargs)
32+
else:
33+
if configuration is None:
34+
configuration = Configuration()
3035

31-
# create the equivalence checker
32-
ecm = EquivalenceCheckingManager(original_circuit, compiled_circuit, configuration)
36+
# create the equivalence checker from configuration
37+
ecm = EquivalenceCheckingManager(original_circuit, compiled_circuit, configuration)
3338

3439
# use the gate_cost scheme for the verification
3540
ecm.set_application_scheme("gate_cost")

Diff for: test/python/test_verify.py

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from __future__ import annotations
2+
3+
import pytest
4+
from mqt import qcec
5+
from qiskit import QuantumCircuit
6+
7+
8+
@pytest.fixture
9+
def original_circuit() -> QuantumCircuit:
10+
qc = QuantumCircuit(3)
11+
qc.h(0)
12+
qc.cx(0, 1)
13+
qc.cx(0, 2)
14+
qc.measure_all()
15+
return qc
16+
17+
18+
@pytest.fixture
19+
def alternative_circuit() -> QuantumCircuit:
20+
qc = QuantumCircuit(3, 3)
21+
qc.h(0)
22+
qc.cx(0, 1)
23+
qc.swap(0, 1)
24+
qc.cx(1, 2)
25+
qc.measure(0, 1)
26+
qc.measure(1, 0)
27+
qc.measure(2, 2)
28+
return qc
29+
30+
31+
def test_verify(original_circuit: QuantumCircuit, alternative_circuit: QuantumCircuit) -> None:
32+
"""
33+
Test the verification of two equivalent circuits.
34+
"""
35+
result = qcec.verify(original_circuit, alternative_circuit)
36+
assert result.equivalence == qcec.EquivalenceCriterion.equivalent

Diff for: test/python/test_verify_compilation.py

-8
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,6 @@
66
from qiskit.providers.fake_provider import FakeAthens
77

88

9-
def get_circuits(qc: QuantumCircuit, optimization_level: int = 1) -> tuple[QuantumCircuit, QuantumCircuit]:
10-
"""
11-
Compile the circuit ``qc`` to the 5-qubit IBMQ Athens architecture.
12-
"""
13-
qc_comp = transpile(qc, backend=FakeAthens(), optimization_level=optimization_level)
14-
return qc, qc_comp
15-
16-
179
@pytest.fixture
1810
def original_circuit() -> QuantumCircuit:
1911
qc = QuantumCircuit(3)

0 commit comments

Comments
 (0)