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

Tfim simulation using 2 CNOT rather than 3 CNOT #94

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
62 changes: 62 additions & 0 deletions notebooks/tfim_2cnot_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
from qibo.hamiltonians import SymbolicHamiltonian
from boostvqe.models.dbi.double_bracket_evolution_oracles import *
from functools import reduce
import numpy as np
from qibo import hamiltonians
import matplotlib.pyplot as plt

n_qubits = 3
h_coeff = 1
hamiltonian = SymbolicHamiltonian(nqubits=n_qubits)

oracle = TFIM_EvolutionOracle(h=hamiltonian, evolution_oracle_type="trotter", steps=1, B_a=0, order=2)

circuit = oracle.circuit(t_duration=1.0)

unitary = circuit.unitary()
def multikron(matrix_list):
"""Calculates Kronecker product of a list of matrices.

Args:
matrix_list (list): List of matrices as ``ndarray``.

Returns:
ndarray: Kronecker product of all matrices in ``matrix_list``.
"""
return reduce(np.kron, matrix_list)

from numpy.linalg import norm

def our_TFIM(nqubits, h: float = 0.0, dense: bool = True, backend=None):
def multikron(matrix_list):
"""Calculates Kronecker product of a list of matrices."""
return reduce(np.kron, matrix_list)

from qibo.backends import matrices

matrix = (
- multikron([matrices.X, matrices.X]) - h * multikron([matrices.Z, matrices.I])
)
terms = [hamiltonians.terms.HamiltonianTerm(matrix, i, i + 1) for i in range(nqubits - 1)]
terms.append(hamiltonians.terms.HamiltonianTerm(matrix, nqubits - 1, 0))
ham = SymbolicHamiltonian(backend=backend)
ham.terms = terms
return ham

ham = our_TFIM(nqubits=n_qubits, h=h_coeff, dense=False)
truth = ham.exp(1)
verification_norm = []
for step in range(1, 21):
oracle = TFIM_EvolutionOracle(h=hamiltonian, evolution_oracle_type="trotter", steps=step, B_a=h_coeff, order=2)
circuit = oracle.circuit(t_duration=1.0)
unitary = circuit.unitary()
verification_norm.append(norm(truth-unitary))


x = np.array([i for i in range(1, 21)])
plt.plot(x, verification_norm, 'o')
plt.title("verification of TFIM 2 CNOT implementation")
plt.xlabel("steps")
plt.ylabel("norm of difference")

plt.show()
53 changes: 51 additions & 2 deletions src/boostvqe/models/dbi/double_bracket_evolution_oracles.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
from functools import cached_property, reduce
from typing import Union

import hyperopt
import matplotlib.pyplot as plt

#import hyperopt
#import matplotlib.pyplot as plt
import numpy as np
from qibo import Circuit, gates, symbols
from qibo.config import raise_error
Expand Down Expand Up @@ -307,3 +308,51 @@ def circuit(self, t_duration, steps=None, order=None):
steps=steps,
order=order,
)


@dataclass
class TFIM_EvolutionOracle(EvolutionOracle):
steps: int = None
B_a: float = None
order: int = None

def circuit(self, t_duration):
circuit_v = Circuit(self.h.nqubits) # Initialize the circuit with the number of qubits
t_duration /= self.steps
def routine(tmp_circuit, enum_list, routine_t):
for a in enum_list:
tmp_circuit.add(gates.CNOT(a, (a + 1) % self.h.nqubits))
# Time evolution under the transverse field Ising model Hamiltonian
# exp(-i t (X(a) + B_a * Z(a)))
self._time_evolution_step(tmp_circuit, a, routine_t)

# Add second CNOT(a, a+1)
tmp_circuit.add(gates.CNOT(a, (a + 1) % self.h.nqubits))
if self.order is None:
for _ in range(self.steps):
routine(circuit_v, range(self.h.nqubits), t_duration)
elif self.order == 1:
for _ in range(self.steps):
routine(circuit_v, range(0, self.h.nqubits, 2), t_duration)
routine(circuit_v, range(1, self.h.nqubits, 2), t_duration)

elif self.order == 2:
for _ in range(self.steps):
routine(circuit_v, range(1, self.h.nqubits, 2), t_duration/2)
routine(circuit_v, range(0, self.h.nqubits, 2), t_duration)
routine(circuit_v, range(1, self.h.nqubits, 2), t_duration / 2)
else:
print("order must be either 1 or 2")
return circuit_v

def _time_evolution_step(self, tmp_circuit: Circuit, a: int, dt: float):
"""Apply a single Trotter step of the time evolution operator exp(-i dt (X(a) + B_a Z(a)))."""

# Time evolution for X(a)
tmp_circuit.add(gates.RX(a, theta=-2*dt)) # Apply exp(-i dt X(a))

# Time evolution for Z(a)
tmp_circuit.add(gates.RZ(a, theta=-2*dt * self.B_a)) # Apply exp(-i dt B_a Z(a))

return tmp_circuit