Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 9 additions & 5 deletions src/fermilib/circuits/_low_depth_trotter_simulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,22 +144,26 @@ def simulation_gate_trotter_step(register, hamiltonian, input_ordering=None,
# Divide by two for second order Trotter.
if not first_order:
z_angle /= 2.
Rz(z_angle) | register[i]
# After special_F_adjacent, qubits i and i+1 have swapped;
# the ith rotation must thus be applied to qubit i+1.
Rz(z_angle) | register[i + 1]
Ph(z_angle / 2.) | register

num_operator_right = ((input_ordering[i + 1], 1),
(input_ordering[i + 1], 0))
num_operator_right = ((input_ordering[i+1], 1),
(input_ordering[i+1], 0))
if num_operator_right in hamiltonian.terms:
# Jordan-Wigner maps a number term c*n_i to c*(I-Z_i)/2.
# Time evolution is then exp(-i c*(I-Z_i)/2), which is equal to
# a phase exp(-ic/2) and a rotation Rz(-c).
z_angle = (-hamiltonian.terms[num_operator_left] /
z_angle = (-hamiltonian.terms[num_operator_right] /
(n_qubits - 1))

# Divide by two for second order Trotter.
if not first_order:
z_angle /= 2
Rz(z_angle) | register[i + 1]
# After special_F_adjacent, qubits i and i+1 have swapped;
# the (i+1)th rotation must thus be applied to qubit i.
Rz(z_angle) | register[i]
Ph(z_angle / 2.) | register

# Finally, swap the two modes in input_ordering.
Expand Down
110 changes: 101 additions & 9 deletions src/fermilib/circuits/_low_depth_trotter_simulation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ def test_simulate_n0n1(self):
2 ** self.size).T

self.assertTrue(numpy.allclose(ordered_wavefunction(self.engine),
expected.T))
expected.T),
msg=str(ordered_wavefunction(self.engine) -
expected.T))

def test_simulate_n0n3(self):
hamiltonian = FermionOperator('3^ 0^ 3 0')
Expand All @@ -114,7 +116,9 @@ def test_simulate_n0n3(self):
2 ** self.size).T

self.assertTrue(numpy.allclose(ordered_wavefunction(self.engine),
expected.T))
expected.T),
msg=str(ordered_wavefunction(self.engine) -
expected.T))

def test_simulate_n1n3(self):
hamiltonian = FermionOperator('3^ 1^ 3 1')
Expand All @@ -132,7 +136,9 @@ def test_simulate_n1n3(self):
2 ** self.size).T

self.assertTrue(numpy.allclose(ordered_wavefunction(self.engine),
expected.T))
expected.T),
msg=str(ordered_wavefunction(self.engine) -
expected.T))

def test_single_trotter_step_no_input_ordering_n1n3(self):
hamiltonian = FermionOperator('3^ 1^ 3 1')
Expand All @@ -150,7 +156,9 @@ def test_single_trotter_step_no_input_ordering_n1n3(self):
2 ** self.size).T

self.assertTrue(numpy.allclose(ordered_wavefunction(self.engine),
expected.T))
expected.T),
msg=str(ordered_wavefunction(self.engine) -
expected.T))

def test_simulate_hopping_0_to_1(self):
hamiltonian = FermionOperator('1^ 0') + FermionOperator('0^ 1')
Expand All @@ -169,7 +177,9 @@ def test_simulate_hopping_0_to_1(self):
2 ** self.size).T

self.assertTrue(numpy.allclose(ordered_wavefunction(self.engine),
expected.T))
expected.T),
msg=str(ordered_wavefunction(self.engine) -
expected.T))

def test_simulate_hopping_1_to_3(self):
hamiltonian = FermionOperator('1^ 3') + FermionOperator('3^ 1')
Expand Down Expand Up @@ -236,7 +246,7 @@ def test_simulate_multiple_hopping_terms(self):
self.assertTrue(numpy.allclose(ordered_wavefunction(self.engine),
expected.T, atol=1e-2))

def test_simulate_multiple_number_terms(self):
def test_simulate_multiple_two_number_terms(self):
hamiltonian = (0.37 * FermionOperator('1^ 0^ 1 0') +
2.4 * FermionOperator('3^ 0^ 3 0') -
2 * FermionOperator('3^ 1^ 3 1'))
Expand All @@ -259,7 +269,42 @@ def test_simulate_multiple_number_terms(self):
2 ** self.size).T

self.assertTrue(numpy.allclose(ordered_wavefunction(self.engine),
expected.T))
expected.T),
msg=str(numpy.array(ordered_wavefunction(self.engine) -
expected.T)))

def test_simulate_single_and_double_number_terms(self):
hamiltonian = (0.37 * FermionOperator('1^ 0^ 1 0') +
2.4 * FermionOperator('3^ 0^ 3 0') -
2 * FermionOperator('3^ 1^ 3 1') +
1.1 * FermionOperator('2^ 2') +
1.7 * FermionOperator('0^ 0') -
0.3 * FermionOperator('3^ 3'))

projectq.ops.All(projectq.ops.H) | self.register

_low_depth_trotter_simulation.simulate_dual_basis_evolution(
self.register, hamiltonian, trotter_steps=1, first_order=False)

self.engine.flush()

# get_sparse_operator reverses the indices, so reverse the sites
# the Hamiltonian acts on so as to compare them.
reversed_operator = (0.37 * FermionOperator('3^ 2^ 3 2') +
2.4 * FermionOperator('3^ 0^ 3 0') -
2 * FermionOperator('2^ 0^ 2 0') +
1.1 * FermionOperator('1^ 1') +
1.7 * FermionOperator('3^ 3') -
0.3 * FermionOperator('0^ 0'))
evol_matrix = expm(-1j * get_sparse_operator(
reversed_operator, n_qubits=self.size)).todense()
expected = evol_matrix * numpy.matrix([2 ** (-self.size / 2.)] *
2 ** self.size).T

self.assertTrue(numpy.allclose(ordered_wavefunction(self.engine),
expected.T),
msg=str(numpy.array(ordered_wavefunction(self.engine) -
expected.T)))

def test_simulate_overlapping_number_and_hopping_terms(self):
hamiltonian = (0.37 * FermionOperator('1^ 0^ 1 0') +
Expand All @@ -283,7 +328,9 @@ def test_simulate_overlapping_number_and_hopping_terms(self):

self.assertTrue(numpy.allclose(ordered_wavefunction(self.engine),
expected.T,
atol=1e-2))
atol=1e-2),
msg=str(numpy.array(ordered_wavefunction(self.engine) -
expected.T)))

def test_simulate_dual_basis_hamiltonian(self):
hamiltonian = dual_basis_hamiltonian(1, self.size)
Expand Down Expand Up @@ -313,7 +360,52 @@ def test_simulate_dual_basis_hamiltonian(self):

self.assertTrue(numpy.allclose(ordered_wavefunction(self.engine),
expected.T,
atol=1e-2))
atol=1e-2),
msg=str(numpy.array(ordered_wavefunction(self.engine) -
expected.T)))

def test_simulate_dual_basis_hamiltonian_with_spin_and_potentials(self):
big_eng = projectq.MainEngine()
big_reg = big_eng.allocate_qureg(2 * self.size)
hamiltonian = dual_basis_hamiltonian(1, self.size, spinless=False)

for i in range(2 * self.size):
coefficient = 1. / (i + 1)
if i % 3:
coefficient = -coefficient
hamiltonian += FermionOperator(((i, 1), (i, 0)), coefficient)

# Choose random state.
initial_state = numpy.zeros(2 ** (2 * self.size), dtype=complex)
for i in range(len(initial_state)):
initial_state[i] = (random.random() *
numpy.exp(1j * 2 * numpy.pi * random.random()))
initial_state /= numpy.linalg.norm(initial_state)

# Put randomly chosen state in the registers.
big_eng.flush()
big_eng.backend.set_wavefunction(initial_state, big_reg)

_low_depth_trotter_simulation.simulate_dual_basis_evolution(
big_reg, hamiltonian, trotter_steps=7, first_order=False,
input_ordering=list(range(7, -1, -1)))

big_eng.flush()

# get_sparse_operator reverses the indices - we've accounted for this
# with the reversed input_ordering.
evol_matrix = expm(-1j * get_sparse_operator(
hamiltonian, n_qubits=2*self.size)).todense()
initial_state = numpy.matrix(initial_state).T
expected = evol_matrix * initial_state

self.assertTrue(numpy.allclose(ordered_wavefunction(big_eng),
expected.T,
atol=1e-2),
msg=str(numpy.array(ordered_wavefunction(big_eng) -
expected.T)))

projectq.ops.All(projectq.ops.Measure) | big_reg

def test_simulate_dual_basis_evolution_bad_input_ordering(self):
with self.assertRaises(ValueError):
Expand Down