Skip to content
Open
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
1 change: 1 addition & 0 deletions src/openfermion/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@
error_bound,
error_operator,
trotter_steps_required,
trotter_steps_required_propagator,
vpe_single_circuit,
vpe_circuits_single_timestep,
standard_vpe_rotation_set,
Expand Down
1 change: 1 addition & 0 deletions src/openfermion/circuits/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@
error_bound,
error_operator,
trotter_steps_required,
trotter_steps_required_propagator,
)

from .vpe_circuits import (
Expand Down
7 changes: 6 additions & 1 deletion src/openfermion/circuits/trotter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,9 @@

from .trotter_algorithm import TrotterAlgorithm, TrotterStep

from .trotter_error import error_bound, error_operator, trotter_steps_required
from .trotter_error import (
error_bound,
error_operator,
trotter_steps_required,
trotter_steps_required_propagator,
)
62 changes: 55 additions & 7 deletions src/openfermion/circuits/trotter/trotter_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,20 @@ def error_bound(terms, tight=False):


def trotter_steps_required(trotter_error_bound, time, energy_precision):
"""Determine the number of Trotter steps for accurate simulation.
r"""Determine the number of Trotter steps for accurate energy estimation.

This function calculates the number of steps required to bound the
systematic energy shift (eigenvalue shift) of the effective Hamiltonian
to a given precision. This is appropriate for applications like Quantum
Phase Estimation (QPE) where we only care about the eigenvalues of the
propagator. See appendix B from https://arxiv.org/pdf/1312.1695
This uses the following definition of error for time t and
number of steps r:


$$
\lvert E - E^{TS} \rvert = \frac{t^2}{r^2} E_{bound}(H)
$$

Args:
trotter_error_bound (float): Upper bound on Trotter error in the
Expand All @@ -187,11 +200,46 @@ def trotter_steps_required(trotter_error_bound, time, energy_precision):
energy_precision (float): Acceptable shift in state energy.

Returns:
The integer minimum number of Trotter steps required for
simulation to the desired precision.
The integer minimum number of Trotter steps required.
"""
if energy_precision <= 0:
raise ValueError("energy_precision must be strictly positive.")
if trotter_error_bound < 0:
raise ValueError("trotter_error_bound must be non-negative.")
if time == 0:
return 0
return max(1, int(ceil(abs(time) * sqrt(trotter_error_bound / energy_precision))))
Comment thread
arettig marked this conversation as resolved.

Notes:
The number of Trotter steps required is an upper bound on the
true requirement, which may be lower.

def trotter_steps_required_propagator(trotter_error_bound, time, prop_precision):
r"""Determine the number of Trotter steps for accurate state evolution.

This function calculates the number of steps required to bound the
error in the time evolution operator (propagator error / spectral norm error)
to a given fidelity precision. This is appropriate for quantum dynamics
applications where we care about the accuracy of the state itself over
time. This uses the following definition of error for time t and
number of steps r:

$$
\lVert U - U^{TS} \rVert = \frac{t^3}{r^2} E_{bound}(H)
$$

Args:
trotter_error_bound (float): Upper bound on Trotter error in the
state of interest.
time (float): The total simulation time.
prop_precision (float): Acceptable error in the time evolution
operator (propagator error).

Returns:
The integer minimum number of Trotter steps required.
"""
return int(ceil(time * sqrt(trotter_error_bound / energy_precision)))
if prop_precision <= 0:
raise ValueError("prop_precision must be strictly positive.")
if trotter_error_bound < 0:
raise ValueError("trotter_error_bound must be non-negative.")
if time == 0:
return 0
atime = abs(time)
return max(1, int(ceil(atime * sqrt(atime * trotter_error_bound / prop_precision))))
Comment thread
arettig marked this conversation as resolved.
59 changes: 58 additions & 1 deletion src/openfermion/circuits/trotter/trotter_error_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
error_operator,
error_bound,
trotter_steps_required,
trotter_steps_required_propagator,
)


Expand Down Expand Up @@ -158,11 +159,67 @@ def test_trotter_steps_required(self):
self.assertEqual(
trotter_steps_required(trotter_error_bound=0.3, time=2.5, energy_precision=0.04), 7
)
self.assertEqual(
trotter_steps_required_propagator(
trotter_error_bound=0.3, time=2.5, prop_precision=0.04
),
11,
)

def test_trotter_steps_required_negative_time(self):
self.assertEqual(
trotter_steps_required(trotter_error_bound=0.1, time=3.3, energy_precision=0.11), 4
trotter_steps_required(trotter_error_bound=0.1, time=-3.3, energy_precision=0.11), 4
)
self.assertEqual(
trotter_steps_required_propagator(
trotter_error_bound=0.1, time=-3.3, prop_precision=0.11
),
6,
)

def test_trotter_steps_required_zero_time(self):
self.assertEqual(
trotter_steps_required(trotter_error_bound=0.3, time=0.0, energy_precision=0.04), 0
)
self.assertEqual(
trotter_steps_required_propagator(
trotter_error_bound=0.3, time=0.0, prop_precision=0.04
),
0,
)

def test_trotter_steps_required_zero_error_bound(self):
# Should return at least 1 step for non-zero time even if error bound is 0
self.assertEqual(
trotter_steps_required(trotter_error_bound=0.0, time=2.5, energy_precision=0.04), 1
)
self.assertEqual(
trotter_steps_required_propagator(
trotter_error_bound=0.0, time=2.5, prop_precision=0.04
),
1,
)

def test_trotter_steps_required_invalid_precision(self):
with self.assertRaises(ValueError):
trotter_steps_required(trotter_error_bound=0.3, time=2.5, energy_precision=0.0)
with self.assertRaises(ValueError):
trotter_steps_required(trotter_error_bound=0.3, time=2.5, energy_precision=-0.04)
with self.assertRaises(ValueError):
trotter_steps_required_propagator(trotter_error_bound=0.3, time=2.5, prop_precision=0.0)
with self.assertRaises(ValueError):
trotter_steps_required_propagator(
trotter_error_bound=0.3, time=2.5, prop_precision=-0.04
)

def test_trotter_steps_required_invalid_error_bound(self):
with self.assertRaises(ValueError):
trotter_steps_required(trotter_error_bound=-0.3, time=2.5, energy_precision=0.04)
with self.assertRaises(ValueError):
trotter_steps_required_propagator(
trotter_error_bound=-0.3, time=2.5, prop_precision=0.04
)

def test_return_type(self):
self.assertIsInstance(trotter_steps_required(0.1, 0.1, 0.1), int)
self.assertIsInstance(trotter_steps_required_propagator(0.1, 0.1, 0.1), int)
Loading