Skip to content

Commit

Permalink
Merge branch 'master' into clifford_simulator
Browse files Browse the repository at this point in the history
  • Loading branch information
renatomello committed Dec 9, 2023
2 parents d693eef + ad0cb94 commit 5aac466
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 1 deletion.
6 changes: 6 additions & 0 deletions doc/source/api-reference/qibo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1584,6 +1584,12 @@ Expressibility of parameterized quantum circuits
.. autofunction:: qibo.quantum_info.expressibility


Frame Potential
"""""""""""""""

.. autofunction:: qibo.quantum_info.frame_potential


Random Ensembles
^^^^^^^^^^^^^^^^

Expand Down
78 changes: 77 additions & 1 deletion src/qibo/quantum_info/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,7 @@ def expressibility(
Defaults to ``None``.
Returns:
float: Entangling capability.
float: Expressibility of parametrized circuit.
"""

if isinstance(power_t, int) is False:
Expand Down Expand Up @@ -1146,6 +1146,82 @@ def expressibility(
return fid


def frame_potential(
circuit,
power_t: int,
samples: int = None,
backend=None,
):
"""Returns the frame potential of a parametrized circuit under uniform sampling of the parameters.
For :math:`n` qubits and moment :math:`t`, the frame potential
:math:`\\mathcal{F}_{\\mathcal{U}}^{(t)}` if given by [1]
.. math::
\\mathcal{F}_{\\mathcal{U}}^{(t)} = \\int_{U,V \\in \\mathcal{U}} \\,
\\text{d}U \\, \\text{d}V \\, \\text{abs}\\bigl[\\text{tr}(U^{\\dagger} \\, V)\\bigr]^{2t} \\, ,
where :math:`\\mathcal{U}` is the group of unitaries defined by the parametrized circuit.
The frame potential is approximated by the average
.. math::
\\mathcal{F}_{\\mathcal{U}}^{(t)} \\approx \\frac{1}{N} \\,
\\sum_{k=1}^{N} \\, \\text{abs}\\bigl[ \\text{tr}(U_{k}^{\\dagger} \\, V_{k})\\bigr]^{2t} \\, ,
where :math:`N` is the number of ``samples``.
Args:
circuit (:class:`qibo.models.circuit.Circuit`): Parametrized circuit.
power_t (int): power that defines the :math:`t`-design.
samples (int): number of samples to estimate the integral.
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: Frame potential of the parametrized circuit.
References:
1. M. Liu *et al.*, *Estimating the randomness of quantum circuit ensembles up to 50 qubits*.
`arXiv:2205.09900 [quant-ph] <https://arxiv.org/abs/2205.09900>`_.
"""
if not isinstance(power_t, int):
raise_error(
TypeError, f"power_t must be type int, but it is type {type(power_t)}."
)

if not isinstance(samples, int):
raise_error(
TypeError, f"samples must be type int, but it is type {type(samples)}."
)

if backend is None: # pragma: no cover
backend = GlobalBackend()

nqubits = circuit.nqubits
dim = 2**nqubits

potential = 0
for _ in range(samples):
unitary_1 = circuit.copy()
params_1 = np.random.uniform(-np.pi, np.pi, circuit.trainable_gates.nparams)
unitary_1.set_parameters(params_1)
unitary_1 = unitary_1.unitary(backend) / np.sqrt(dim)

for _ in range(samples):
unitary_2 = circuit.copy()
params_2 = np.random.uniform(-np.pi, np.pi, circuit.trainable_gates.nparams)
unitary_2.set_parameters(params_2)
unitary_2 = unitary_2.unitary(backend) / np.sqrt(dim)

potential += np.abs(
np.trace(np.transpose(np.conj(unitary_1)) @ unitary_2)
) ** (2 * power_t)

return potential / samples**2


def _check_hermitian_or_not_gpu(matrix, backend=None):
"""Checks if a given matrix is Hermitian and whether
the backend is neither :class:`qibojit.backends.CupyBackend`
Expand Down
28 changes: 28 additions & 0 deletions tests/test_quantum_info_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
entropy,
expressibility,
fidelity,
frame_potential,
gate_error,
hilbert_schmidt_distance,
impurity,
Expand Down Expand Up @@ -649,3 +650,30 @@ def test_expressibility(backend):
expr_3 = expressibility(c3, t, samples, backend=backend)

backend.assert_allclose(expr_1 < expr_2 < expr_3, True)


@pytest.mark.parametrize("samples", [int(1e2)])
@pytest.mark.parametrize("power_t", [2])
@pytest.mark.parametrize("nqubits", [2, 3, 4])
def test_frame_potential(backend, nqubits, power_t, samples):
depth = int(np.ceil(nqubits * power_t))

circuit = Circuit(nqubits)
circuit.add(gates.U3(q, 0.0, 0.0, 0.0) for q in range(nqubits))
for _ in range(depth):
circuit.add(gates.CNOT(q, q + 1) for q in range(nqubits - 1))
circuit.add(gates.U3(q, 0.0, 0.0, 0.0) for q in range(nqubits))

with pytest.raises(TypeError):
frame_potential(circuit, power_t="2", backend=backend)
with pytest.raises(TypeError):
frame_potential(circuit, 2, samples="1000", backend=backend)

dim = 2**nqubits
potential_haar = 2 / dim**4

potential = frame_potential(
circuit, power_t=power_t, samples=samples, backend=backend
)

backend.assert_allclose(potential, potential_haar, rtol=1e-2, atol=1e-2)

0 comments on commit 5aac466

Please sign in to comment.