diff --git a/qiskit_addon_aqc_tensor/ansatz_generation/from_connectivity.py b/qiskit_addon_aqc_tensor/ansatz_generation/from_connectivity.py index 9b7391d..51c91e7 100644 --- a/qiskit_addon_aqc_tensor/ansatz_generation/from_connectivity.py +++ b/qiskit_addon_aqc_tensor/ansatz_generation/from_connectivity.py @@ -15,6 +15,7 @@ from __future__ import annotations +import logging from typing import Sequence import numpy as np @@ -25,9 +26,15 @@ QuantumCircuit, ) from qiskit.circuit.library import UnitaryGate +from qiskit.compiler import transpile +from qiskit.exceptions import QiskitError from qiskit.quantum_info import Operator from qiskit.synthesis import OneQubitEulerDecomposer, TwoQubitWeylDecomposition +logger = logging.getLogger(__name__) +# We'd like the user to actually see warnings from this module by default +logger.setLevel(logging.WARNING) + class AnsatzBlock(Gate): """Ansatz block. @@ -300,7 +307,21 @@ def perform_separation(q0: int, q1: int): partner[q1] = None couple_qc = couples[q0, q1] mat = Operator(couple_qc).data - d = TwoQubitWeylDecomposition(mat) + try: + d = TwoQubitWeylDecomposition(mat) + except QiskitError as exc: + # Try again with the transpiled circuit. See + # https://github.com/Qiskit/qiskit-addon-aqc-tensor/pull/100 + logger.warning( + "M2 diagonalization has failed with the following non-fatal exception:", + exc_info=exc, + ) + logger.warning( + "Trying M2 diagonalization again with transpiled 2-qubit circuit.", + ) + couple_qc = transpile(couple_qc, basis_gates=["cx", "rx", "ry", "rz"]) + mat = Operator(couple_qc).data + d = TwoQubitWeylDecomposition(mat) singles[q0] = [UnitaryGate(d.K1r)] singles[q1] = [UnitaryGate(d.K1l)] fp01 = free_params[q0, q1] diff --git a/releasenotes/notes/TwoQubitWeylDecomposition-fix-52d4b0e63a12f346.yaml b/releasenotes/notes/TwoQubitWeylDecomposition-fix-52d4b0e63a12f346.yaml new file mode 100644 index 0000000..4d1f092 --- /dev/null +++ b/releasenotes/notes/TwoQubitWeylDecomposition-fix-52d4b0e63a12f346.yaml @@ -0,0 +1,12 @@ +--- +fixes: + - | + This adds a workaround for an issue sometimes encountered when calling :class:`~qiskit.synthesis.TwoQubitWeylDecomposition` inside + :func:`.generate_ansatz_from_circuit`. Sometimes an error is thrown despite the input matrix + being unitary to numerical precision, and returning :meth:`~qiskit.quantum_info.Operator.is_unitary()`` as ``True``. This is + an ongoing issue in qiskit, see `https://github.com/Qiskit/qiskit/issues/4159` for more details. + + With this workaround, any time the error is encountered, it will retry by transpiling the two-qubit circuit (from which the + matrix is derived) prior to passing the matrix to :class:`~qiskit.synthesis.TwoQubitWeylDecomposition`. It should + be noted that this is does not fix the root issue, and is not guaranteed to always work, but has + been observed to work for some instances. diff --git a/test/ansatz_generation/test_from_connectivity.py b/test/ansatz_generation/test_from_connectivity.py index 9e3a4f1..e90df6f 100644 --- a/test/ansatz_generation/test_from_connectivity.py +++ b/test/ansatz_generation/test_from_connectivity.py @@ -91,3 +91,14 @@ def test_idle_qubit(self): qc.x(0) _, initial_parameters = generate_ansatz_from_circuit(qc) assert len(initial_parameters) == 3 + + def test_large_circuit_no_two_qubit_weyl_decomposition_error(self): + qc = QuantumCircuit(50) + + for _ in range(10): + for i in range(0, 50, 2): + qc.cx(i, i + 1) + for i in range(1, 49, 2): + qc.cx(i, i + 1) + + _, _ = generate_ansatz_from_circuit(qc, qubits_initially_zero=True)