Skip to content

Commit

Permalink
changed the sign convention of the hamiltonian simulation
Browse files Browse the repository at this point in the history
  • Loading branch information
positr0nium committed Nov 21, 2024
1 parent 449eb1b commit 695043a
Show file tree
Hide file tree
Showing 7 changed files with 39 additions and 25 deletions.
14 changes: 10 additions & 4 deletions src/qrisp/operators/fermionic/fermionic_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -708,14 +708,19 @@ def get_measurement(
#


def trotterization(self, t = 1, steps = 1, iter = 1):
def trotterization(self, forward_evolution = True):
r"""
Returns a function for performing Hamiltonian simulation, i.e., approximately implementing the unitary operator $e^{itH}$ via Trotterization.
Returns a function for performing Hamiltonian simulation, i.e., approximately implementing the unitary operator $U(t) = e^{-itH}$ via Trotterization.
Note that this method will always simulate the **hermitized** operator, i.e.
.. math::
H = (O + O^\dagger)/2
Parameters
----------
forward_evolution, bool, optional
If set to False $U(t)^\dagger = e^{itH}$ will be executed (usefull for quantum phase estimation). The default is True.
Returns
-------
Expand Down Expand Up @@ -801,7 +806,7 @@ def trotter_step(qarg, t, steps):
coeff = reduced_H.terms_dict[ferm_term]
pauli_hamiltonian = ferm_term.fermionic_swap(permutation).to_qubit_term()
pauli_term = list(pauli_hamiltonian.terms_dict.keys())[0]
pauli_term.simulate(coeff*t/steps*pauli_hamiltonian.terms_dict[pauli_term], new_qarg)
pauli_term.simulate(-coeff*t/steps*pauli_hamiltonian.terms_dict[pauli_term]*(-1)**int(forward_evolution), new_qarg)


def U(qarg, t=1, steps=1, iter=1):
Expand All @@ -812,8 +817,9 @@ def U(qarg, t=1, steps=1, iter=1):
return U

def group_up(self, denominator):

term_groups = group_up_terms(self, denominator)
if len(term_groups) == 0:
return [self]
groups = []
for term_group in term_groups:
O = FermionicOperator({term : self.terms_dict[term] for term in term_group})
Expand Down
6 changes: 6 additions & 0 deletions src/qrisp/operators/hamiltonian_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@ def dsatur_coloring(num_vertices, adjacency_matrix):
return colors

def find_coloring(G):
if len(G) == 0:
return []

adjacency_matrix = nx.to_numpy_array(G)

coloring_1 = rlf_coloring(len(G), adjacency_matrix)
Expand All @@ -202,6 +205,9 @@ def group_up_terms(H, group_denominator):

coloring = find_coloring(G)

if len(coloring) == 0:
return []

groups = []
for i in range(np.max(coloring)+1): groups.append([])

Expand Down
1 change: 0 additions & 1 deletion src/qrisp/operators/qubit/bound_qubit_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,6 @@ def get_measurement(
return unbound_operator.get_measurement(qarg,
precision=precision,
backend=backend,
shots=shots,
compile=compile,
compilation_kwargs=compilation_kwargs,
subs_dic=subs_dic,
Expand Down
12 changes: 8 additions & 4 deletions src/qrisp/operators/qubit/qubit_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,8 @@ def commuting_groups(self):

def group_up(self, group_denominator):
term_groups = group_up_terms(self, group_denominator)
if len(term_groups) == 0:
return [self]
groups = []
for term_group in term_groups:
H = QubitOperator({term : self.terms_dict[term] for term in term_group})
Expand Down Expand Up @@ -1423,11 +1425,11 @@ def get_measurement(
# Trotterization
#

def trotterization(self, method='commuting_qw'):
def trotterization(self, method='commuting_qw', forward_evolution = True):
r"""
.. _ham_sim:
Returns a function for performing Hamiltonian simulation, i.e., approximately implementing the unitary operator $e^{itH}$ via Trotterization.
Returns a function for performing Hamiltonian simulation, i.e., approximately implementing the unitary operator $U(t) = e^{-itH}$ via Trotterization.
Note that this method will always simulate the **hermitized** operator, i.e.
.. math::
Expand All @@ -1441,6 +1443,8 @@ def trotterization(self, method='commuting_qw'):
The method for grouping the QubitTerms.
Available are ``commuting`` (groups such that all QubitTerms mutually commute) and ``commuting_qw`` (groups such that all QubitTerms mutually commute qubit-wise).
The default is ``commuting_qw``.
forward_evolution : bool, optional
If set to False $U(t)^\dagger = e^{itH}$ will be executed (usefull for quantum phase estimation). The default is True.
Returns
-------
Expand Down Expand Up @@ -1512,7 +1516,7 @@ def trotter_step(qarg, t, steps):
intersect_groups = diagonal_operator.group_up(lambda a, b: not a.intersect(b))
for intersect_group in intersect_groups:
for term,coeff in intersect_group.terms_dict.items():
term.simulate(coeff*t/steps, qarg)
term.simulate(-coeff*t/steps*(-1)**int(forward_evolution), qarg)

if method=='commuting':
def trotter_step(qarg, t, steps):
Expand All @@ -1521,7 +1525,7 @@ def trotter_step(qarg, t, steps):
intersect_groups = diagonal_operator.group_up(lambda a, b: not a.intersect(b))
for intersect_group in intersect_groups:
for term,coeff in intersect_group.terms_dict.items():
term.simulate(coeff*t/steps, qarg)
term.simulate(-coeff*t/steps*(-1)**int(forward_evolution), qarg)

def U(qarg, t=1, steps=1, iter=1):
merge([qarg])
Expand Down
17 changes: 8 additions & 9 deletions src/qrisp/operators/qubit/qubit_term.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ def simulate(self, coeff, qv, ctrl = None):
# If no non-trivial indices are found, we perform a global phase
# and are done.
if len(Z_indices + projector_qubits) == 0:
gphase(coeff, qv[0])
gphase(-coeff, qv[0])
return

# If there are only projectors, the circuit is a mcp gate
Expand All @@ -260,14 +260,13 @@ def simulate(self, coeff, qv, ctrl = None):
# Perform the mcp
if len(projector_qubits) == 1:
if len(flip_qubits) == 1:
p(-coeff, projector_qubits[0])
gphase(coeff, projector_qubits[0])
else:
p(coeff, projector_qubits[0])
gphase(-coeff, projector_qubits[0])
else:
p(-coeff, projector_qubits[0])
else:
with env:

mcp(coeff, projector_qubits, method = "balauca")
mcp(-coeff, projector_qubits, method = "balauca")

return

Expand Down Expand Up @@ -400,12 +399,12 @@ def flip_anchor_qubit(qv, anchor_index, Z_indices):
if control_qubit_available:
# Use Selinger's circuit (page 5)
with conjugate(cx)(qv[anchor_index], hs_anc):
rz(-coeff, qv[anchor_index])
rz(coeff, qv[anchor_index])
if flip_control_phase:
coeff = -coeff
rz(coeff, hs_anc)
rz(-coeff, hs_anc)
else:
rz(-coeff*2, qv[anchor_index])
rz(coeff*2, qv[anchor_index])


if len(projector_indices) >= 2:
Expand Down
2 changes: 1 addition & 1 deletion tests/operator_tests/test_hamiltonian.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def test_trotterization():
E0 = G.ground_state_energy()
assert np.abs(E0-(-0.804899065613056)) < 2e-2

U = G.trotterization()
U = G.trotterization(forward_evolution = False)

qv = QuantumVariable(2)
x(qv)
Expand Down
12 changes: 6 additions & 6 deletions tests/operator_tests/test_qubit_hamiltonian_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"""

from qrisp import QuantumVariable, x, QPE
from qrisp.operators import X, Y, Z, A, C, P0, P1
from qrisp.operators import X, Y, Z, A, C, P0, P1, QubitOperator
import numpy as np

def test_qubit_hamiltonian_simulation():
Expand All @@ -29,7 +29,7 @@ def test_qubit_hamiltonian_simulation():

for method in ['commuting_qw', 'commuting']:

U = H.trotterization(method=method)
U = H.trotterization(method=method, forward_evolution = False)

# Find minimum eigenvalue of H with Hamiltonian simulation and QPE

Expand All @@ -38,7 +38,7 @@ def test_qubit_hamiltonian_simulation():
x(qv[0])
E1 = H.get_measurement(qv)
assert abs(E1-E0)<5e-2

qpe_res = QPE(qv,U,precision=10,kwargs={"steps":3},iter_spec=True)

results = qpe_res.get_measurement()
Expand All @@ -53,10 +53,10 @@ def test_qubit_hamiltonian_simulation():
def verify_trotterization(H,method):

# Compute hermitean matrix
H_matrix = H.to_sparse_matrix().todense()
H_matrix = H.to_sparse_matrix(4).todense()
H_matrix = (H_matrix + H_matrix.transpose().conjugate())/2
# Compute unitary matrix
U_matrix = expm(1j*H_matrix)
U_matrix = expm(-1j*H_matrix)

# Perform trotterization
qv = QuantumVariable(int(np.log2(H_matrix.shape[0])))
Expand Down Expand Up @@ -96,7 +96,7 @@ def verify_trotterization(H,method):
for O3 in operator_list:
H = O0(0)*O1(1)*O2(2)*O3(3)
if H is 1:
continue
H = QubitOperator() + 1
print(H)
verify_trotterization(H,'commuting_qw')
verify_trotterization(H,'commuting')
Expand Down

0 comments on commit 695043a

Please sign in to comment.