|
| 1 | +# This code is part of Qiskit. |
| 2 | +# |
| 3 | +# (C) Copyright IBM 2024. |
| 4 | +# |
| 5 | +# This code is licensed under the Apache License, Version 2.0. You may |
| 6 | +# obtain a copy of this license in the LICENSE.txt file in the root directory |
| 7 | +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. |
| 8 | +# |
| 9 | +# Any modifications or derivative works of this code must retain this |
| 10 | +# copyright notice, and modified files need to carry a notice indicating |
| 11 | +# that they have been altered from the originals. |
| 12 | + |
| 13 | +""" |
| 14 | +Additional utilities for Operators. |
| 15 | +""" |
| 16 | + |
| 17 | +from __future__ import annotations |
| 18 | + |
| 19 | +from qiskit.quantum_info import Operator |
| 20 | +from qiskit.quantum_info.operators.predicates import matrix_equal |
| 21 | + |
| 22 | + |
| 23 | +def _equal_with_ancillas( |
| 24 | + op1: Operator, |
| 25 | + op2: Operator, |
| 26 | + ancilla_qubits: list[int], |
| 27 | + ignore_phase: bool = False, |
| 28 | + rtol: float | None = None, |
| 29 | + atol: float | None = None, |
| 30 | +) -> bool: |
| 31 | + r"""Test if two Operators are equal on the subspace where ancilla qubits |
| 32 | + are :math:`|0\rangle`. |
| 33 | +
|
| 34 | + Args: |
| 35 | + op1 (Operator): an operator object. |
| 36 | + op2 (Operator): an operator object. |
| 37 | + ancilla_qubits (list[int]): a list of clean ancilla qubits. |
| 38 | + ignore_phase (bool): ignore complex-phase difference between matrices. |
| 39 | + rtol (float): relative tolerance value for comparison. |
| 40 | + atol (float): absolute tolerance value for comparison. |
| 41 | +
|
| 42 | + Returns: |
| 43 | + bool: True iff operators are equal up to clean ancilla qubits. |
| 44 | + """ |
| 45 | + if op1.dim != op2.dim: |
| 46 | + return False |
| 47 | + |
| 48 | + if atol is None: |
| 49 | + atol = op1.atol |
| 50 | + if rtol is None: |
| 51 | + rtol = op1.rtol |
| 52 | + |
| 53 | + num_qubits = op1._op_shape._num_qargs_l |
| 54 | + num_non_ancillas = num_qubits - len(ancilla_qubits) |
| 55 | + |
| 56 | + # Find a permutation that moves all ancilla qubits to the back |
| 57 | + pattern = [] |
| 58 | + ancillas = [] |
| 59 | + for q in range(num_qubits): |
| 60 | + if q not in ancilla_qubits: |
| 61 | + pattern.append(q) |
| 62 | + else: |
| 63 | + ancillas.append(q) |
| 64 | + pattern = pattern + ancillas |
| 65 | + |
| 66 | + # Apply this permutation to both operators |
| 67 | + permuted1 = op1.apply_permutation(pattern) |
| 68 | + permuted2 = op2.apply_permutation(pattern) |
| 69 | + |
| 70 | + # Restrict to the subspace where ancillas are 0 |
| 71 | + restricted1 = permuted1.data[: 2**num_non_ancillas, : 2**num_qubits] |
| 72 | + restricted2 = permuted2.data[: 2**num_non_ancillas, : 2**num_qubits] |
| 73 | + |
| 74 | + return matrix_equal( |
| 75 | + restricted1, restricted2.data, ignore_phase=ignore_phase, rtol=rtol, atol=atol |
| 76 | + ) |
0 commit comments