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

Expose DBI operators #1287

Merged
merged 33 commits into from
Apr 24, 2024
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
91a93c6
refactor: expose commutators
Edoardo-Pedicillo Mar 28, 2024
6a3b5b4
refactor: loss input of minimize in VQE
Edoardo-Pedicillo Mar 29, 2024
1b07e41
refactor: put default value of loss
Edoardo-Pedicillo Mar 29, 2024
745700c
fix: set loss default value to None
Edoardo-Pedicillo Mar 29, 2024
8d717ff
refactor: return the DBI operators in DBI call function
Edoardo-Pedicillo Apr 2, 2024
fb4e7af
refactor: change loss function variable name
Edoardo-Pedicillo Apr 2, 2024
652b83f
refactor: rename var_loss
Edoardo-Pedicillo Apr 3, 2024
52c4868
Committing suggestion for the name convention eval_operator -> eval_d…
marekgluza Apr 5, 2024
6860a48
sign conventions on top of the exposed dbr operator
marekgluza Apr 5, 2024
1ebe1c9
proposed a test of the exposed dbr unitary function
marekgluza Apr 5, 2024
2f55e19
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 5, 2024
3eb7029
improving the bound implementation
marekgluza Apr 5, 2024
3975be0
trivial conflict
marekgluza Apr 5, 2024
42c1c92
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 5, 2024
18517e8
Fixing the type which was already set for a future convention
marekgluza Apr 5, 2024
8cba7ad
fixing typo in test file
marekgluza Apr 5, 2024
0e281b4
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 5, 2024
6871133
now the test should run otherwise it entered into 0<0
marekgluza Apr 5, 2024
dc2de88
Merge branch 'dbi_fix_gc_sign_conventions' of github.com:qiboteam/qib…
marekgluza Apr 5, 2024
34405cb
passing the d argument in the test
marekgluza Apr 5, 2024
03ca52c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 5, 2024
d924465
Remove raise error in test_models_dbi.py
Sam-XiaoyueLi Apr 8, 2024
051240e
Remove "prints" comment in utils.py
Sam-XiaoyueLi Apr 8, 2024
09614be
correct sign in eval_dbr_unitaruy (double_bracket.py) - calculate_mat…
Sam-XiaoyueLi Apr 8, 2024
f2236cb
Update tests/test_models_dbi.py
marekgluza Apr 8, 2024
fba400b
Update tests/test_models_dbi.py
marekgluza Apr 8, 2024
76c7799
Merge pull request #1293 from qiboteam/dbi_fix_gc_sign_conventions
Sam-XiaoyueLi Apr 9, 2024
55b8fe2
Merge branch 'master' into dbi_fix
Edoardo-Pedicillo Apr 23, 2024
eb02d78
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 23, 2024
bf1c8fb
fix: remove not used variables
Edoardo-Pedicillo Apr 23, 2024
135d733
test: remove the case step = None in test_select_best_dbr_generator b…
Edoardo-Pedicillo Apr 23, 2024
c05fab3
refactor: use h2 expectation instead of the backend one
Edoardo-Pedicillo Apr 23, 2024
d9fb54c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Apr 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 31 additions & 10 deletions src/qibo/models/dbi/double_bracket.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,34 +56,55 @@ def __init__(
def __call__(
self, step: float, mode: DoubleBracketGeneratorType = None, d: np.array = None
):
r"""We use convention that $H' = U^\dagger H U$ where $U=e^{-sW}$ with $W=[D,H]$ (or depending on `mode` an approximation, see `eval_dbr_unitary`). If $s>0$ then for $D = \Delta(H)$ the GWW DBR will give a $\sigma$-decrease, see https://arxiv.org/abs/2206.11772."""

operator = self.eval_dbr_unitary(step, mode, d)
operator_dagger = self.backend.cast(
np.matrix(self.backend.to_numpy(operator)).getH()
)
self.h.matrix = operator_dagger @ self.h.matrix @ operator
return operator

def eval_dbr_unitary(
self, step: float, mode: DoubleBracketGeneratorType = None, d: np.array = None
):
"""In call we will are working in the convention that $H' = U^\\dagger H U$ where $U=e^{-sW}$ with $W=[D,H]$ or an approximation of that by a group commutator. That is handy because if we switch from the DBI in the Heisenberg picture for the Hamiltonian, we get that the transformation of the state is $|\\psi'\rangle = U |\\psi\rangle$ so that $\\langle H\rangle_{\\psi'} = \\langle H' \rangle_\\psi$ (i.e. when writing the unitary acting on the state dagger notation is avoided).

The group commutator must approximate $U=e^{-s[D,H]}$. This is achieved by setting $r = \\sqrt{s}$ so that
$$V = e^{-irH}e^{irD}e^{irH}e^{-irD}$$
because
$$e^{-irH}De^{irH} = D+ir[D,H]+O(r^2)$$
so
$$V\approx e^{irD +i^2 r^2[D,H] + O(r^2) -irD} \approx U\\ .$$
See the app in https://arxiv.org/abs/2206.11772 for a derivation.
"""
if mode is None:
mode = self.mode

if mode is DoubleBracketGeneratorType.canonical:
operator = self.backend.calculate_matrix_exp(
1.0j * step,
-1.0j * step,
self.commutator(self.diagonal_h_matrix, self.h.matrix),
)
elif mode is DoubleBracketGeneratorType.single_commutator:
if d is None:
d = self.diagonal_h_matrix
operator = self.backend.calculate_matrix_exp(
1.0j * step,
-1.0j * step,
self.commutator(d, self.h.matrix),
)
elif mode is DoubleBracketGeneratorType.group_commutator:
if d is None:
d = self.diagonal_h_matrix

sqrt_step = np.sqrt(step)
operator = (
self.h.exp(-step)
@ self.backend.calculate_matrix_exp(-step, d)
@ self.h.exp(step)
@ self.backend.calculate_matrix_exp(step, d)
self.h.exp(sqrt_step)
@ self.backend.calculate_matrix_exp(-sqrt_step, d)
@ self.h.exp(-sqrt_step)
@ self.backend.calculate_matrix_exp(sqrt_step, d)
)
operator_dagger = self.backend.cast(
np.matrix(self.backend.to_numpy(operator)).getH()
)
self.h.matrix = operator @ self.h.matrix @ operator_dagger
return operator

@staticmethod
def commutator(a, b):
Expand Down
12 changes: 8 additions & 4 deletions src/qibo/models/dbi/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,15 @@ def select_best_dbr_generator(
)
else:
step_best = step
dbi_eval(step=step_best)
optimal_steps.append(step_best)
norms_off_diagonal_restriction.append(dbi_eval.off_diagonal_norm)
dbi_object(step=step)
optimal_steps.append(step)
norms_off_diagonal_restriction.append(dbi_object.off_diagonal_norm)
dbi_object.h = deepcopy(h_before)
dbi_object.mode = generator_type
# find best d
idx_max_loss = np.argmin(norms_off_diagonal_restriction)
idx_max_loss = norms_off_diagonal_restriction.index(
min(norms_off_diagonal_restriction)
)
flip = flip_list[idx_max_loss]
step_optimal = optimal_steps[idx_max_loss]
dbi_eval = deepcopy(dbi_object)
Expand Down
7 changes: 7 additions & 0 deletions src/qibo/models/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,10 @@ def fourier_coefficients(
# Shift the filtered coefficients back
filtered_coeffs = np.fft.ifftshift(shifted_filtered_coeffs)
return filtered_coeffs


def vqe_loss(params, circuit, hamiltonian):
circuit.set_parameters(params)
result = hamiltonian.backend.execute_circuit(circuit)
final_state = result.state()
return hamiltonian.expectation(final_state)
25 changes: 10 additions & 15 deletions src/qibo/models/variational.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import numpy as np

from qibo.config import raise_error
from qibo.hamiltonians import Hamiltonian
from qibo.models.evolution import StateEvolution
from qibo.models.utils import vqe_loss


class VQE:
Expand Down Expand Up @@ -42,6 +42,7 @@ def minimize(
self,
initial_state,
method="Powell",
loss_func=None,
jac=None,
hess=None,
hessp=None,
Expand All @@ -61,6 +62,7 @@ def minimize(
method (str): the desired minimization method.
See :meth:`qibo.optimizers.optimize` for available optimization
methods.
loss (callable): loss function, the default one is :func:`qibo.models.utils.vqe_loss`.
jac (dict): Method for computing the gradient vector for scipy optimizers.
hess (dict): Method for computing the hessian matrix for scipy optimizers.
hessp (callable): Hessian of objective function times an arbitrary
Expand All @@ -80,25 +82,20 @@ def minimize(
the ``OptimizeResult``, for ``'cma'`` the ``CMAEvolutionStrategy.result``,
and for ``'sgd'`` the options used during the optimization.
"""

def _loss(params, circuit, hamiltonian):
circuit.set_parameters(params)
result = hamiltonian.backend.execute_circuit(circuit)
final_state = result.state()
return hamiltonian.expectation(final_state)

if loss_func is None:
loss_func = vqe_loss
if compile:
loss = self.hamiltonian.backend.compile(_loss)
loss = self.hamiltonian.backend.compile(loss_func)
else:
loss = _loss
loss = loss_func

if method == "cma":
# TODO: check if we can use this shortcut
# dtype = getattr(self.hamiltonian.backend.np, self.hamiltonian.backend._dtypes.get('DTYPE'))
dtype = self.hamiltonian.backend.np.float64
loss = lambda p, c, h: dtype(_loss(p, c, h))
loss = lambda p, c, h: dtype(loss_func(p, c, h))
elif method != "sgd":
loss = lambda p, c, h: self.hamiltonian.backend.to_numpy(_loss(p, c, h))
loss = lambda p, c, h: self.hamiltonian.backend.to_numpy(loss_func(p, c, h))

result, parameters, extra = self.optimizers.optimize(
loss,
Expand Down Expand Up @@ -636,8 +633,6 @@ def minimize(
The corresponding best parameters.
extra: variable with historical data for the energy and callbacks.
"""
import numpy as np

parameters = np.array([delta_t, 0])

def _loss(params, falqon, hamiltonian):
Expand All @@ -647,7 +642,7 @@ def _loss(params, falqon, hamiltonian):

energy = [np.inf]
callback_result = []
for it in range(1, max_layers + 1):
for _ in range(1, max_layers + 1):
beta = self.hamiltonian.backend.to_numpy(
_loss(parameters, self, self.evol_hamiltonian)
)
Expand Down
28 changes: 26 additions & 2 deletions tests/test_models_dbi.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
)
from qibo.quantum_info import random_hermitian

NSTEPS = 10
NSTEPS = 50
SEED = 10
"""Number of steps for evolution."""


Expand All @@ -30,7 +31,7 @@ def test_double_bracket_iteration_canonical(backend, nqubits):

@pytest.mark.parametrize("nqubits", [1, 2])
def test_double_bracket_iteration_group_commutator(backend, nqubits):
h0 = random_hermitian(2**nqubits, backend=backend)
h0 = random_hermitian(2**nqubits, backend=backend, seed=SEED)
d = backend.cast(np.diag(np.diag(backend.to_numpy(h0))))
dbi = DoubleBracketIteration(
Hamiltonian(nqubits, h0, backend=backend),
Expand All @@ -47,6 +48,29 @@ def test_double_bracket_iteration_group_commutator(backend, nqubits):
assert initial_off_diagonal_norm > dbi.off_diagonal_norm


@pytest.mark.parametrize("nqubits", [3])
def test_double_bracket_iteration_eval_dbr_unitary(backend, nqubits):
r"""The bound is $$||e^{-[D,H]}-GC||\le s^{3/2}(||[H,[D,H]||+||[D,[D,H]]||$$"""
h0 = random_hermitian(2**nqubits, backend=backend)
d = backend.cast(np.diag(np.diag(backend.to_numpy(h0))))
dbi = DoubleBracketIteration(
Hamiltonian(nqubits, h0, backend=backend),
mode=DoubleBracketGeneratorType.group_commutator,
)

for s in np.linspace(0.001, 0.01, NSTEPS):
u = dbi.eval_dbr_unitary(
s, d=d, mode=DoubleBracketGeneratorType.single_commutator
)
v = dbi.eval_dbr_unitary(
s, d=d, mode=DoubleBracketGeneratorType.group_commutator
)

assert np.linalg.norm(u - v) < 10 * s**1.49 * (
np.linalg.norm(h0) + np.linalg.norm(d)
) * np.linalg.norm(h0) * np.linalg.norm(d)


@pytest.mark.parametrize("nqubits", [1, 2])
def test_double_bracket_iteration_single_commutator(backend, nqubits):
h0 = random_hermitian(2**nqubits, backend=backend)
Expand Down
Loading