Skip to content

Commit

Permalink
Merge branch 'master' into quantinuum
Browse files Browse the repository at this point in the history
  • Loading branch information
renatomello committed Oct 16, 2023
2 parents a8df429 + 29fbea1 commit e40a549
Show file tree
Hide file tree
Showing 12 changed files with 140 additions and 62 deletions.
2 changes: 1 addition & 1 deletion doc/source/api-reference/qibo.rst
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ Parametric ZX interaction (RZX)
:member-order: bysource

Parametric XX-YY interaction (RXXYY)
""""""""""""""""""""""""""""""""""
""""""""""""""""""""""""""""""""""""

.. autoclass:: qibo.gates.RXXYY
:members:
Expand Down
24 changes: 20 additions & 4 deletions src/qibo/backends/abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,13 +326,29 @@ def entanglement_entropy(self, rho): # pragma: no cover
raise_error(NotImplementedError)

@abc.abstractmethod
def calculate_norm(self, state): # pragma: no cover
"""Calculate norm of a state vector."""
def calculate_norm(self, state, order=2): # pragma: no cover
"""Calculate norm of a state vector. Default is :math:`2`-norm.
For specifications on possible values of the parameter ``order``
for the ``tensorflow`` backend, please refer to
`tensorflow.norm <https://www.tensorflow.org/api_docs/python/tf/norm>`_.
For all other backends, please refer to
`numpy.linalg.norm <https://numpy.org/doc/stable/reference/generated/numpy.linalg.norm.html>`_.
"""
raise_error(NotImplementedError)

@abc.abstractmethod
def calculate_norm_density_matrix(self, state): # pragma: no cover
"""Calculate norm (trace) of a density matrix."""
def calculate_norm_density_matrix(self, state, order="nuc"): # pragma: no cover
"""Calculate norm of a density matrix. Default is the ``nuclear`` norm.
If ``order="nuc"``, it returns the nuclear norm of ``state``,
assuming ``state`` is Hermitian (also known as trace norm).
For specifications on the other possible values of the
parameter ``order`` for the ``tensorflow`` backend, please refer to
`tensorflow.norm <https://www.tensorflow.org/api_docs/python/tf/norm>`_.
For all other backends, please refer to
`numpy.linalg.norm <https://numpy.org/doc/stable/reference/generated/numpy.linalg.norm.html>`_.
"""
raise_error(NotImplementedError)

@abc.abstractmethod
Expand Down
8 changes: 4 additions & 4 deletions src/qibo/backends/numpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -673,13 +673,13 @@ def entanglement_entropy(self, rho):
entropy = self.np.sum(masked_eigvals * spectrum) / self.np.log(2.0)
return entropy, spectrum

def calculate_norm(self, state):
def calculate_norm(self, state, order=2):
state = self.cast(state)
return self.np.sqrt(self.np.sum(self.np.abs(state) ** 2))
return self.np.linalg.norm(state, ord=order)

def calculate_norm_density_matrix(self, state):
def calculate_norm_density_matrix(self, state, order="nuc"):
state = self.cast(state)
return self.np.trace(state)
return self.np.linalg.norm(state, ord=order)

def calculate_overlap(self, state1, state2):
state1 = self.cast(state1)
Expand Down
10 changes: 10 additions & 0 deletions src/qibo/backends/tensorflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,16 @@ def entanglement_entropy(self, rho):
entropy = self.np.sum(masked_eigvals * spectrum) / self.np.log(2.0)
return entropy, spectrum

def calculate_norm(self, state, order=2):
state = self.cast(state)
return self.tf.norm(state, ord=order)

def calculate_norm_density_matrix(self, state, order="nuc"):
state = self.cast(state)
if order == "nuc":
return self.np.trace(state)
return self.tf.norm(state, ord=order)

def calculate_eigenvalues(self, matrix, k=6):
return self.tf.linalg.eigvalsh(matrix)

Expand Down
24 changes: 17 additions & 7 deletions src/qibo/quantum_info/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,11 @@ def trace_distance(state, target, check_hermitian: bool = False, backend=None):
difference = state - target
if check_hermitian is True:
hermitian = bool(
backend.calculate_norm(np.transpose(np.conj(difference)) - difference)
float(
backend.calculate_norm_density_matrix(
np.transpose(np.conj(difference)) - difference, order=2
)
)
<= PRECISION_TOL
)
if (
Expand Down Expand Up @@ -724,14 +728,18 @@ def process_fidelity(channel, target=None, check_unitary: bool = False, backend=
dim = int(np.sqrt(channel.shape[0]))

if check_unitary is True:
norm_channel = backend.calculate_norm(
np.dot(np.conj(np.transpose(channel)), channel) - np.eye(dim**2)
norm_channel = float(
backend.calculate_norm_density_matrix(
np.dot(np.conj(np.transpose(channel)), channel) - np.eye(dim**2)
)
)
if target is None and norm_channel > PRECISION_TOL:
raise_error(TypeError, "Channel is not unitary and Target is None.")
if target is not None:
norm_target = backend.calculate_norm(
np.dot(np.conj(np.transpose(target)), target) - np.eye(dim**2)
norm_target = float(
backend.calculate_norm(
np.dot(np.conj(np.transpose(target)), target) - np.eye(dim**2)
)
)
if (norm_channel > PRECISION_TOL) and (norm_target > PRECISION_TOL):
raise_error(TypeError, "Neither channel is unitary.")
Expand Down Expand Up @@ -1131,10 +1139,12 @@ def _check_hermitian_or_not_gpu(matrix, backend=None):
if backend is None: # pragma: no cover
backend = GlobalBackend()

hermitian = bool(
backend.calculate_norm(np.transpose(np.conj(matrix)) - matrix) < PRECISION_TOL
norm = backend.calculate_norm_density_matrix(
np.transpose(np.conj(matrix)) - matrix, order=2
)

hermitian = bool(float(norm) <= PRECISION_TOL)

if hermitian is False and backend.__class__.__name__ in [
"CupyBackend",
"CuQuantumBackend",
Expand Down
8 changes: 5 additions & 3 deletions src/qibo/quantum_info/superoperator_transformations.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,8 +453,10 @@ def choi_to_kraus(
backend = GlobalBackend()

if validate_cp:
norm = backend.calculate_norm(
choi_super_op - np.transpose(np.conj(choi_super_op))
norm = float(
backend.calculate_norm_density_matrix(
choi_super_op - np.transpose(np.conj(choi_super_op)), order=2
)
)
if norm > PRECISION_TOL:
non_cp = True
Expand Down Expand Up @@ -2065,7 +2067,7 @@ def function(x0, operators):
for prob, oper in zip(x0, operators):
operator += prob * oper

return float(backend.calculate_norm(target - operator))
return float(backend.calculate_norm_density_matrix(target - operator, order=2))

# initial parameters as flat distribution
x0 = [1.0 / (len(kraus_ops) + 1)] * len(kraus_ops)
Expand Down
8 changes: 3 additions & 5 deletions src/qibo/quantum_info/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,11 +245,9 @@ def hellinger_distance(prob_dist_p, prob_dist_q, validate: bool = False, backend
if np.abs(np.sum(prob_dist_q) - 1.0) > PRECISION_TOL:
raise_error(ValueError, "Second probability array must sum to 1.")

distance = backend.calculate_norm(
np.sqrt(prob_dist_p) - np.sqrt(prob_dist_q)
) / np.sqrt(2)

distance = float(distance)
distance = float(
backend.calculate_norm(np.sqrt(prob_dist_p) - np.sqrt(prob_dist_q)) / np.sqrt(2)
)

return distance

Expand Down
8 changes: 5 additions & 3 deletions tests/test_callbacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
# Absolute testing tolerance for the cases of zero entanglement entropy
from qibo.config import PRECISION_TOL
from qibo.models import AdiabaticEvolution, Circuit
from qibo.quantum_info.random_ensembles import random_density_matrix, random_statevector


def test_abstract_callback_properties():
Expand Down Expand Up @@ -324,17 +325,18 @@ def test_state_callback(backend, density_matrix, copy):
backend.assert_allclose(statec[1], target_state1)


@pytest.mark.parametrize("seed", list(range(1, 5 + 1)))
@pytest.mark.parametrize("density_matrix", [False, True])
def test_norm(backend, density_matrix):
def test_norm(backend, density_matrix, seed):
norm = callbacks.Norm()
if density_matrix:
norm.nqubits = 1
state = np.random.random((2, 2)) + 1j * np.random.random((2, 2))
state = random_density_matrix(2**norm.nqubits, seed=seed, backend=backend)
target_norm = np.trace(state)
final_norm = norm.apply_density_matrix(backend, state)
else:
norm.nqubits = 2
state = np.random.random(4) + 1j * np.random.random(4)
state = random_statevector(2**norm.nqubits, seed=seed, backend=backend)
target_norm = np.sqrt((np.abs(state) ** 2).sum())
final_norm = norm.apply(backend, state)

Expand Down
32 changes: 25 additions & 7 deletions tests/test_gates_channels.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,19 +94,29 @@ def test_kraus_channel(backend, pauli_order):
channel = gates.KrausChannel(0, [a_1, a_2])

backend.assert_allclose(
backend.calculate_norm(channel.to_liouville(backend=backend) - test_superop)
float(
backend.calculate_norm_density_matrix(
channel.to_liouville(backend=backend) - test_superop, order=2
)
)
< PRECISION_TOL,
True,
)
backend.assert_allclose(
backend.calculate_norm(channel.to_choi(backend=backend) - test_choi)
float(
backend.calculate_norm_density_matrix(
channel.to_choi(backend=backend) - test_choi, order=2
)
)
< PRECISION_TOL,
True,
)
backend.assert_allclose(
backend.calculate_norm(
channel.to_pauli_liouville(pauli_order=pauli_order, backend=backend)
- test_pauli
float(
backend.calculate_norm(
channel.to_pauli_liouville(pauli_order=pauli_order, backend=backend)
- test_pauli
)
)
< PRECISION_TOL,
True,
Expand Down Expand Up @@ -212,7 +222,11 @@ def test_pauli_noise_channel(backend, pauli_order):
liouville = gates.PauliNoiseChannel(0, list(zip(basis, pnp))).to_pauli_liouville(
normalize=True, pauli_order=pauli_order, backend=backend
)
norm = backend.calculate_norm(backend.to_numpy(liouville) - test_representation)
norm = float(
backend.calculate_norm_density_matrix(
backend.to_numpy(liouville) - test_representation, order=2
)
)

assert norm < PRECISION_TOL

Expand Down Expand Up @@ -341,7 +355,11 @@ def test_thermal_relaxation_channel(backend, t_1, t_2, time, excpop):
target_state = backend.cast(target_state, dtype=target_state.dtype)

backend.assert_allclose(
backend.calculate_norm(final_state - target_state) < PRECISION_TOL, True
float(
backend.calculate_norm_density_matrix(final_state - target_state, order=2)
)
< PRECISION_TOL,
True,
)


Expand Down
2 changes: 1 addition & 1 deletion tests/test_models_circuit_features.py
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ def test_repeated_execute_probs_and_freqs(backend, nqubits):
test_probabilities = backend.cast(test_probabilities, dtype=float)
print(result.probabilities())
backend.assert_allclose(
backend.calculate_norm(result.probabilities() - test_probabilities)
float(backend.calculate_norm(result.probabilities() - test_probabilities))
< PRECISION_TOL,
True,
)
Expand Down
11 changes: 3 additions & 8 deletions tests/test_quantum_info_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,7 @@ def test_entropy(backend, base, check_hermitian):
test = 0.8613531161467861

backend.assert_allclose(
backend.calculate_norm(
entropy(state, base, check_hermitian=check_hermitian, backend=backend)
- test
)
< PRECISION_TOL,
True,
entropy(state, base, check_hermitian=check_hermitian, backend=backend), test
)


Expand Down Expand Up @@ -520,8 +515,8 @@ def test_process_fidelity_and_infidelity(backend):
channel = backend.cast(channel, dtype=channel.dtype)
test = process_fidelity(channel, check_unitary=True, backend=backend)
with pytest.raises(TypeError):
channel = np.random.rand(d**2, d**2)
target = np.random.rand(d**2, d**2)
channel = 10 * np.random.rand(d**2, d**2)
target = 10 * np.random.rand(d**2, d**2)
channel = backend.cast(channel, dtype=channel.dtype)
target = backend.cast(target, dtype=target.dtype)
test = process_fidelity(channel, target, check_unitary=True, backend=backend)
Expand Down
Loading

0 comments on commit e40a549

Please sign in to comment.