Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hyper-efficient unitary diamond distance #12343

Open
wants to merge 29 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8564212
Add to
owenagnel May 5, 2024
3e50bee
Add missing import to
owenagnel May 5, 2024
bc46973
Small bug fix
owenagnel May 5, 2024
81f4436
Added changelog and fixed linting and broken test
owenagnel May 5, 2024
809ed2d
Merge branch 'main' into main
owenagnel May 5, 2024
3149a68
Removed useless comment
owenagnel May 5, 2024
1d26dc9
Lint fix
owenagnel May 6, 2024
84fb0d7
Merge branch 'Qiskit:main' into main
owenagnel May 6, 2024
f7a4fda
Merge branch 'main' into main
owenagnel May 6, 2024
44748fe
Fixed error strings
owenagnel May 6, 2024
b1be5e7
Merge branch 'main' into main
owenagnel May 6, 2024
f0fa53d
Added tests for unitary diamond distance
owenagnel May 7, 2024
b525a43
Merge branch 'main' into main
owenagnel May 7, 2024
9dc8e6b
Merge branch 'main' into main
owenagnel May 7, 2024
77854a5
Refactored test_unitary_diamond_distance_random
owenagnel May 7, 2024
0e48c60
Linting fixes
owenagnel May 7, 2024
37ae773
Merge branch 'main' into main
owenagnel May 8, 2024
ca5d8b8
Merge branch 'main' into main
owenagnel May 8, 2024
94600c0
Fixed tests
owenagnel May 8, 2024
1d7c18e
Minor fixes
owenagnel May 8, 2024
f0844b2
Minor test fix
owenagnel May 9, 2024
4d6dddc
Changed parameter name
owenagnel May 9, 2024
f1291ab
Merge branch 'main' into main
owenagnel May 20, 2024
ab5b61a
Generalised diamond_distance to accept all BaseOperator types
owenagnel May 20, 2024
9176a6c
Linting fix
owenagnel May 20, 2024
2d32df4
Merge branch 'main' into main
owenagnel May 29, 2024
8bdd47c
Pauli optimisation and added tests
owenagnel May 30, 2024
56e972b
Fixed Pauli optimisation
owenagnel May 30, 2024
d26fc1d
Merge branch 'main' into main
owenagnel May 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 8 additions & 1 deletion qiskit/quantum_info/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
.. autofunction:: process_fidelity
.. autofunction:: gate_error
.. autofunction:: diamond_norm
.. autofunction:: unitary_diamond_distance
.. autofunction:: state_fidelity
.. autofunction:: purity
.. autofunction:: concurrence
Expand Down Expand Up @@ -128,7 +129,13 @@
)
from .operators.channel import PTM, Chi, Choi, Kraus, Stinespring, SuperOp
from .operators.dihedral import CNOTDihedral
from .operators.measures import average_gate_fidelity, diamond_norm, gate_error, process_fidelity
from .operators.measures import (
average_gate_fidelity,
diamond_norm,
unitary_diamond_distance,
gate_error,
process_fidelity,
)
from .random import (
random_clifford,
random_cnotdihedral,
Expand Down
8 changes: 7 additions & 1 deletion qiskit/quantum_info/operators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
from __future__ import annotations
from .channel import PTM, Chi, Choi, Kraus, Stinespring, SuperOp
from .dihedral import CNOTDihedral
from .measures import average_gate_fidelity, diamond_norm, gate_error, process_fidelity
from .measures import (
average_gate_fidelity,
diamond_norm,
unitary_diamond_distance,
gate_error,
process_fidelity,
)
from .operator import Operator
from .scalar_op import ScalarOp
from .symplectic import (
Expand Down
83 changes: 83 additions & 0 deletions qiskit/quantum_info/operators/measures.py
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,89 @@ def cvx_bmat(mat_r, mat_i):
return sol


def unitary_diamond_distance(channel1: Operator, channel2: Operator) -> float:
kevinsung marked this conversation as resolved.
Show resolved Hide resolved
owenagnel marked this conversation as resolved.
Show resolved Hide resolved
r"""Return the diamond distance between two unitary operators.

This function computes the completely-bounded trace-norm (often
referred to as the diamond-norm) of the difference of two unitary channels
(or unitary operators). This is often used as a distance metric in quantum
information This method is a more specialised implementaion of
owenagnel marked this conversation as resolved.
Show resolved Hide resolved
:meth:`~qiskit.quantum_info.diamond_norm`.

Args:
channel1 (Operator): a unitary operator.
channel2 (Operator): a unitary operator.

Returns:
float: The completely-bounded trace norm :math:`\|U - V\|_{\diamond}`.
owenagnel marked this conversation as resolved.
Show resolved Hide resolved

Raises:
QiskitError: if the input operators do not have the same dimensions or aren't unitary.

Additional Information:
The implementation uses an unproved result in Aharonov et al. (1998). Geometrically,
owenagnel marked this conversation as resolved.
Show resolved Hide resolved
owenagnel marked this conversation as resolved.
Show resolved Hide resolved
we compute the distance :math:`d` between the origin and the convex hull
of the eigenvalues which is then plugged into :math:`\sqrt{1 - d^2}`.

Reference:
D. Aharonov, A. Kitaev, and N. Nisan. “Quantum circuits with
mixed states” in Proceedings of the thirtieth annual ACM symposium
on Theory of computing, pp. 20-30, 1998.
"""
op1 = _input_formatter(channel1, Operator, "unitary_diamond_distance", "channel1")
op2 = _input_formatter(channel2, Operator, "unitary_diamond_distance", "channel2")

# Check operators are unitary and have same dimension
if not op1.is_unitary():
raise QiskitError(
owenagnel marked this conversation as resolved.
Show resolved Hide resolved
"Invalid operator supplied to channel1 of unitary_diamond_distance"
"operators must be unitary."
)
if not op2.is_unitary():
raise QiskitError(
"Invalid operator supplied to channel2 of unitary_diamond_distance"
"operators must be unitary."
)
if op1.dim != op2.dim:
raise QiskitError(
"Input quantum channel and target unitary must have the same "
"dimensions ({op1.dim} != {op2.dim})."
owenagnel marked this conversation as resolved.
Show resolved Hide resolved
)

# Compute the diamond norm
op1 = op1.data
op2 = op2.data
pre_diag = np.matmul(np.conj(op1).T, op2)
owenagnel marked this conversation as resolved.
Show resolved Hide resolved
eigenvals = np.linalg.eigvals(pre_diag)
d = _find_poly_distance(eigenvals)
return 2 * np.sqrt(1 - d**2)


def _find_poly_distance(eigenvals: np.ndarray) -> float:
"""Function to find the distance between origin and the convex hull of eigenvalues."""
phases = np.angle(eigenvals)
pos_max = phases.max()
neg_min = phases.min()
pos_min = np.where(phases > 0, phases, np.inf).min()
neg_max = np.where(phases <= 0, phases, -np.inf).max()

if neg_min > 0: # all eigenvalues have positive phase, so hull is above x axis
return np.cos((pos_max - pos_min) / 2)
owenagnel marked this conversation as resolved.
Show resolved Hide resolved

if pos_max <= 0: # all eigenvalues have negative phase, so hull is below x axis
return np.cos((np.abs(neg_min) - np.abs(neg_max)) / 2)

big_angle = pos_max - neg_min
small_angle = pos_min - neg_max
if big_angle >= np.pi:
if small_angle <= np.pi: # hull contains the origin
return 0
else:
return np.cos((2 * np.pi - small_angle) / 2) # hull is left of y axis
else:
return np.cos(big_angle / 2) # hull is right of y axis


def _cvxpy_check(name):
"""Check that a supported CVXPY version is installed"""
# Check if CVXPY package is installed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
features_quantum_info:
- |
Added :meth:`qiskit.quantum_info.unitary_diamond_distance` function for computing the
distance between two unitary channels. This is equivalent to using
:meth:`qiskit.quantum_info.unitary_diamond_distance` to calculate the distance of two
channels: ``diamond_norm(chan1 - chan2)``. In the case of unitary channels this
implementation is significantly more efficient. Refer to
`#12341 <https://github.com/Qiskit/qiskit/issues/12341>` for more details.
34 changes: 34 additions & 0 deletions test/python/quantum_info/operators/test_measures.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
from qiskit.quantum_info import average_gate_fidelity
from qiskit.quantum_info import gate_error
from qiskit.quantum_info import diamond_norm
from qiskit.quantum_info import unitary_diamond_distance
from qiskit.quantum_info.random import random_unitary
from qiskit.circuit.library import RZGate
from test import combine # pylint: disable=wrong-import-order
from test import QiskitTestCase # pylint: disable=wrong-import-order

Expand Down Expand Up @@ -184,6 +187,37 @@ def test_diamond_norm(self, num_qubits):
except cvxpy.SolverError:
self.skipTest("CVXPY solver failed.")

def test_unitary_diamond_distance(self):
"""Test the unitary_diamond_distance function for RZGates
with a specific set of angles."""
angles = np.linspace(0, 2 * np.pi, 10, endpoint=False)
for angle in angles:
op1 = Operator(RZGate(angle))
op2 = Operator.from_label("I")
d2 = np.cos(angle / 2) ** 2 # analytical formula for hull distance
target = np.sqrt(1 - d2) * 2
self.assertAlmostEqual(unitary_diamond_distance(op1, op2), target, places=7)

@combine(num_qubits=[1, 2, 3])
def test_unitary_diamond_distance_random(self, num_qubits):
owenagnel marked this conversation as resolved.
Show resolved Hide resolved
"""Tests the unitary_diamond_distance for random unitaries.
Compares results with semi-definite program."""
try:
import cvxpy
except ImportError:
# Skip test if CVXPY not installed
self.skipTest("CVXPY not installed.")
op1 = random_unitary(2**num_qubits)
op2 = random_unitary(2**num_qubits)
choi1 = Choi(op1)
choi2 = Choi(op2)
delta_choi = choi1 - choi2
try:
target = diamond_norm(delta_choi)
owenagnel marked this conversation as resolved.
Show resolved Hide resolved
self.assertAlmostEqual(unitary_diamond_distance(op1, op2), target, places=4)
kevinsung marked this conversation as resolved.
Show resolved Hide resolved
except cvxpy.SolverError:
self.skipTest("CVXPY solver failed.")
kevinsung marked this conversation as resolved.
Show resolved Hide resolved


if __name__ == "__main__":
unittest.main()