diff --git a/src/qibo/models/dbi/utils.py b/src/qibo/models/dbi/utils.py index 1380b0ce88..87a4b92ef1 100644 --- a/src/qibo/models/dbi/utils.py +++ b/src/qibo/models/dbi/utils.py @@ -66,13 +66,13 @@ def str_to_symbolic(name: str): return tensor_op -def cs_angle_sgn(dbi_object, d): +def cs_angle_sgn(dbi_object, d, backend=None): """Calculates the sign of Cauchy-Schwarz Angle :math:`\\langle W(Z), W({\\rm canonical}) \\rangle_{\\rm HS}`.""" - backend = dbi_object.backend - d = dbi_object.backend.cast(d) + backend = _check_backend(backend) + d = backend.cast(d) norm = backend.np.trace( backend.np.dot( - backend.np.conjugate( + backend.np.conj( dbi_object.commutator(dbi_object.diagonal_h_matrix, dbi_object.h.matrix) ).T, dbi_object.commutator(d, dbi_object.h.matrix), @@ -81,13 +81,13 @@ def cs_angle_sgn(dbi_object, d): return backend.np.real(backend.np.sign(norm)) -def decompose_into_pauli_basis(h_matrix: np.array, pauli_operators: list): +def decompose_into_pauli_basis(h_matrix: np.array, pauli_operators: list, backend=None): """finds the decomposition of hamiltonian `h_matrix` into Pauli-Z operators""" nqubits = int(np.log2(h_matrix.shape[0])) - + backend = _check_backend(backend) decomposition = [] for Z_i in pauli_operators: - expect = np.trace(h_matrix @ Z_i) / 2**nqubits + expect = backend.np.trace(h_matrix @ Z_i) / 2**nqubits decomposition.append(expect) return decomposition @@ -185,7 +185,7 @@ def params_to_diagonal_operator( # TODO: write proper tests for normalize=True if normalize: # pragma: no cover d = d / np.linalg.norm(d) - return d + return backend.cast(d) def off_diagonal_norm_polynomial_expansion_coef(dbi_object, d, n): diff --git a/src/qibo/models/dbi/utils_dbr_strategies.py b/src/qibo/models/dbi/utils_dbr_strategies.py index 5aae761fde..5c088dea63 100644 --- a/src/qibo/models/dbi/utils_dbr_strategies.py +++ b/src/qibo/models/dbi/utils_dbr_strategies.py @@ -1,3 +1,5 @@ +from copy import copy + import hyperopt from qibo.backends import _check_backend @@ -47,6 +49,7 @@ def select_best_dbr_generator( dbi, Z_ops, compare_canonical=True ) """ + backend = dbi_object.backend if scheduling is None: scheduling = dbi_object.scheduling @@ -63,9 +66,9 @@ def select_best_dbr_generator( for i, d in enumerate(d_list): # prescribed step durations - dbi_eval = deepcopy(dbi_object) - d = dbi_eval.backend.cast(d) - flip_list[i] = cs_angle_sgn(dbi_eval, d) + dbi_eval = copy(dbi_object) + d = backend.cast(d) + flip_list[i] = cs_angle_sgn(dbi_eval, d, backend=backend) if flip_list[i] != 0: if step is None: step_best = dbi_eval.choose_step( @@ -78,7 +81,7 @@ def select_best_dbr_generator( norms_off_diagonal_restriction[i] = dbi_eval.off_diagonal_norm # canonical if compare_canonical is True: - dbi_eval = deepcopy(dbi_object) + dbi_eval = copy(dbi_object) dbi_eval.mode = DoubleBracketGeneratorType.canonical if step is None: step_best = dbi_eval.choose_step(scheduling=scheduling, **kwargs) @@ -91,7 +94,7 @@ def select_best_dbr_generator( idx_max_loss = np.argmin(norms_off_diagonal_restriction) flip = flip_list[idx_max_loss] step_optimal = optimal_steps[idx_max_loss] - dbi_eval = deepcopy(dbi_object) + dbi_eval = copy(dbi_object) if idx_max_loss == len(d_list) and compare_canonical is True: # canonical dbi_eval(step=step_optimal, mode=DoubleBracketGeneratorType.canonical) @@ -126,13 +129,17 @@ def gradient_numerical( nqubits = dbi_object.nqubits grad = np.zeros(len(d_params)) d = params_to_diagonal_operator( - d_params, nqubits, parameterization=parameterization, **kwargs + d_params, nqubits, parameterization=parameterization, **kwargs, backend=backend ) for i in range(len(d_params)): - params_new = backend.cast(d_params, copy=True) - params_new[i] += delta + params_new = backend.to_numpy(d_params).copy() + params_new[i] = params_new[i] + delta d_new = params_to_diagonal_operator( - params_new, nqubits, parameterization=parameterization, **kwargs + params_new, + nqubits, + parameterization=parameterization, + **kwargs, + backend=backend, ) # find the increment of a very small step grad[i] = (dbi_object.loss(s, d_new) - dbi_object.loss(s, d)) / delta @@ -230,6 +237,7 @@ def gradient_descent( parameterization=parameterization, pauli_operator_dict=pauli_operator_dict, normalize=normalize, + backend=backend, ) loss_hist = [dbi_object.loss(0.0, d=d)] d_params_hist = [d_params] diff --git a/tests/test_models_dbi.py b/tests/test_models_dbi.py index c20465e5a9..83f231f8f6 100644 --- a/tests/test_models_dbi.py +++ b/tests/test_models_dbi.py @@ -3,7 +3,7 @@ import numpy as np import pytest -from qibo import hamiltonians, set_backend +from qibo import hamiltonians from qibo.hamiltonians import Hamiltonian from qibo.models.dbi.double_bracket import ( DoubleBracketCostFunction, @@ -242,7 +242,7 @@ def test_gradient_descent(backend, order): pauli_operators = list(pauli_operator_dict.values()) # let initial d be approximation of $\Delta(H) d_coef_pauli = decompose_into_pauli_basis( - dbi.diagonal_h_matrix, pauli_operators=pauli_operators + dbi.diagonal_h_matrix, pauli_operators=pauli_operators, backend=backend ) d_pauli = sum([d_coef_pauli[i] * pauli_operators[i] for i in range(nqubits)]) loss_hist_pauli, d_params_hist_pauli, s_hist_pauli = gradient_descent( @@ -252,6 +252,7 @@ def test_gradient_descent(backend, order): ParameterizationTypes.pauli, pauli_operator_dict=pauli_operator_dict, pauli_parameterization_order=order, + backend=backend, ) assert loss_hist_pauli[-1] < initial_off_diagonal_norm @@ -262,6 +263,10 @@ def test_gradient_descent(backend, order): _, _, ) = gradient_descent( - dbi, NSTEPS, d_coef_computational_partial, ParameterizationTypes.computational + dbi, + NSTEPS, + d_coef_computational_partial, + ParameterizationTypes.computational, + backend=backend, ) assert loss_hist_computational_partial[-1] < initial_off_diagonal_norm