Skip to content

Commit

Permalink
Merge branch 'master' into clifford_simulator_numba
Browse files Browse the repository at this point in the history
  • Loading branch information
renatomello committed Feb 27, 2024
2 parents a81d273 + 19368f2 commit 4591bc7
Showing 4 changed files with 92 additions and 28 deletions.
6 changes: 6 additions & 0 deletions doc/source/api-reference/qibo.rst
Original file line number Diff line number Diff line change
@@ -2269,6 +2269,12 @@ Hellinger fidelity
.. autofunction:: qibo.quantum_info.hellinger_fidelity


Hellinger shot error
""""""""""""""""""""

.. autofunction:: qibo.quantum_info.hellinger_fidelity


Haar integral
"""""""""""""

24 changes: 1 addition & 23 deletions src/qibo/noise_model.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import numpy as np

from qibo import gates, models
from qibo.quantum_info import hellinger_fidelity
from qibo.quantum_info.utils import hellinger_fidelity, hellinger_shot_error


def noisy_circuit(circuit, params):
@@ -200,28 +200,6 @@ def freq_to_prob(freq):
return prob


def hellinger_shot_error(p, q, nshots):
"""Hellinger fidelity error caused by using two probability distributions estimated using a finite number of shots.
It is calculated propagating the probability error of each state of the system. The complete formula is:
:math:`(1 - H^{2}(p, q))/\\sqrt{nshots} * \\sum_{i=1}^{n}(\\sqrt{p_i(1-q_i)}+\\sqrt{q_i(1-p_i)})`
where the sum is made all over the possible states and :math:`H(p, q)` is the Hellinger distance.
Args:
p (numpy.ndarray): (discrete) probability distribution :math:`p`.
q (numpy.ndarray): (discrete) probability distribution :math:`q`.
nshots (int): the number of shots we used to run the circuit to obtain :math:`p` and :math:`q`.
Returns:
(float): The Hellinger fidelity error.
"""
hellinger_fid = hellinger_fidelity(p, q)
hellinger_fid_e = np.sqrt(hellinger_fid / nshots) * np.sum(
np.sqrt(q * (1 - p)) + np.sqrt(p * (1 - q))
)
return hellinger_fid_e


def loss(parameters, *args):
"""The loss function used to be maximized in the fit method of the :class:`qibo.noise_model.CompositeNoiseModel`.
It is the hellinger fidelity calculated between the probability distribution of the noise model and the experimental target distribution using the :func:`qibo.quantum_info.hellinger_fidelity`.
58 changes: 54 additions & 4 deletions src/qibo/quantum_info/utils.py
Original file line number Diff line number Diff line change
@@ -258,14 +258,13 @@ def hellinger_fidelity(prob_dist_p, prob_dist_q, validate: bool = False, backend
.. math::
(1 - H^{2}(p, q))^{2} \\, ,
where :math:`H(p, q)` is the Hellinger distance
(:func:`qibo.quantum_info.utils.hellinger_distance`).
where :math:`H(p, q)` is the :func:`qibo.quantum_info.utils.hellinger_distance`.
Args:
prob_dist_p (ndarray or list): discrete probability distribution :math:`p`.
prob_dist_q (ndarray or list): discrete probability distribution :math:`q`.
validate (bool, optional): if True, checks if :math:`p` and :math:`q` are proper
probability distributions. Default: False.
validate (bool, optional): if ``True``, checks if :math:`p` and :math:`q` are proper
probability distributions. Defaults to ``False``.
backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be
used in the execution. If ``None``, it uses
:class:`qibo.backends.GlobalBackend`. Defaults to ``None``.
@@ -274,11 +273,62 @@ def hellinger_fidelity(prob_dist_p, prob_dist_q, validate: bool = False, backend
(float): Hellinger fidelity.
"""
backend = _check_backend(backend)

distance = hellinger_distance(prob_dist_p, prob_dist_q, validate, backend=backend)

return (1 - distance**2) ** 2


def hellinger_shot_error(
prob_dist_p, prob_dist_q, nshots: int, validate: bool = False, backend=None
):
"""Calculates the Hellinger fidelity error between two discrete probability distributions estimated from finite statistics.
It is calculated propagating the probability error of each state of the system.
The complete formula is:
.. math::
\\frac{1 - H^{2}(p, q)}{\\sqrt{nshots}} \\, \\sum_{k} \\,
\\left(\\sqrt{p_{k} \\, (1 - q_{k})} + \\sqrt{q_{k} \\, (1 - p_{k})}\\right)
where :math:`H(p, q)` is the :func:`qibo.quantum_info.utils.hellinger_distance`,
and :math:`1 - H^{2}(p, q)` is the square root of the
:func:`qibo.quantum_info.utils.hellinger_fidelity`.
Args:
prob_dist_p (ndarray or list): discrete probability distribution :math:`p`.
prob_dist_q (ndarray or list): discrete probability distribution :math:`q`.
nshots (int): number of shots we used to run the circuit to obtain :math:`p` and :math:`q`.
validate (bool, optional): if ``True``, checks if :math:`p` and :math:`q` are proper
probability distributions. Defaults to ``False``.
backend (:class:`qibo.backends.abstract.Backend`, optional): backend to be
used in the execution. If ``None``, it uses
:class:`qibo.backends.GlobalBackend`. Defaults to ``None``.
Returns:
(float): Hellinger fidelity error.
"""
backend = _check_backend(backend)

if isinstance(prob_dist_p, list):
prob_dist_p = backend.cast(prob_dist_p, dtype=np.float64)

if isinstance(prob_dist_q, list):
prob_dist_q = backend.cast(prob_dist_q, dtype=np.float64)

hellinger_error = hellinger_fidelity(
prob_dist_p, prob_dist_q, validate=validate, backend=backend
)
hellinger_error = np.sqrt(hellinger_error / nshots) * np.sum(
np.sqrt(prob_dist_q * (1 - prob_dist_p))
+ np.sqrt(prob_dist_p * (1 - prob_dist_q))
)

return hellinger_error


def haar_integral(
nqubits: int,
power_t: int,
32 changes: 31 additions & 1 deletion tests/test_quantum_info_utils.py
Original file line number Diff line number Diff line change
@@ -4,16 +4,18 @@
import numpy as np
import pytest

from qibo import Circuit, matrices
from qibo import Circuit, gates, matrices
from qibo.config import PRECISION_TOL
from qibo.quantum_info.metrics import fidelity
from qibo.quantum_info.random_ensembles import random_clifford
from qibo.quantum_info.utils import (
haar_integral,
hadamard_transform,
hamming_distance,
hamming_weight,
hellinger_distance,
hellinger_fidelity,
hellinger_shot_error,
pqc_integral,
)

@@ -175,6 +177,34 @@ def test_hellinger(backend, validate, kind):
assert fidelity == (1 - target**2) ** 2


@pytest.mark.parametrize("kind", [None, list])
@pytest.mark.parametrize("validate", [False, True])
def test_hellinger_shot_error(backend, validate, kind):
nqubits, nshots = 5, 1000

circuit = random_clifford(nqubits, seed=1, backend=backend)
circuit.add(gates.M(qubit) for qubit in range(nqubits))

circuit_2 = random_clifford(nqubits, seed=2, backend=backend)
circuit_2.add(gates.M(qubit) for qubit in range(nqubits))

prob_dist_p = backend.execute_circuit(circuit, nshots=nshots).probabilities()
prob_dist_q = backend.execute_circuit(circuit_2, nshots=nshots).probabilities()

if kind is not None:
prob_dist_p = kind(prob_dist_p)
prob_dist_q = kind(prob_dist_q)

hellinger_error = hellinger_shot_error(
prob_dist_p, prob_dist_q, nshots, validate=validate, backend=backend
)
hellinger_fid = hellinger_fidelity(
prob_dist_p, prob_dist_q, validate=validate, backend=backend
)

assert 2 * hellinger_error < hellinger_fid


def test_haar_integral_errors(backend):
with pytest.raises(TypeError):
nqubits, power_t, samples = 0.5, 2, 10

0 comments on commit 4591bc7

Please sign in to comment.