Repository: quantumlib/OpenFermion
Pull request #1071 (2025-06-04 18:57:16)

Fix warning about asyncio.get_event_loop() in Python 3.12
In Python 3.12, the code in `shell_tools.py` produces the following warnings when running `check/pytest`:

```
dev_tools/shell_tools_test.py::test_run_cmd_raise_on_fail
dev_tools/shell_tools.py:185:
DeprecationWarning: There is no current event loop
result = asyncio.get_event_loop().run_until_complete(
```

The fix involves managing the event loop. The updated functions now try to get the current running event loop; if no loop is running, they create a new one, use it, and then close it.

Note: commit [c3ab0fa](https://github.com/quantumlib/OpenFermion/pull/1071/commits/c3ab0fab591d02668647a5d9074b0118a9c4a19e) has the actual code changes. Commit [ba775f5](https://github.com/quantumlib/OpenFermion/pull/1071/commits/ba775f57d62870ed87edf5a48affcfbfbce82901) contains purely formatting changes from applying `check/format-incremental`.


Patch:
diff --git a/dev_tools/shell_tools.py b/dev_tools/shell_tools.py
index ce4ad1c..da0e728 100644
--- a/dev_tools/shell_tools.py
+++ b/dev_tools/shell_tools.py
@@ -15,24 +15,13 @@
 import asyncio
 import subprocess
 import sys
-from typing import (
-    List,
-    Optional,
-    Tuple,
-    Union,
-    IO,
-    Any,
-    cast,
-    NamedTuple,
-)
+from typing import List, Optional, Tuple, Union, IO, Any, cast, NamedTuple
 
 from collections.abc import AsyncIterable
 
-CommandOutput = NamedTuple("CommandOutput", [
-    ('out', Optional[str]),
-    ('err', Optional[str]),
-    ('exit_code', int),
-])
+CommandOutput = NamedTuple(
+    "CommandOutput", [('out', Optional[str]), ('err', Optional[str]), ('exit_code', int)]
+)
 
 BOLD = 1
 DIM = 2
@@ -52,11 +41,7 @@ def highlight(text: str, color_code: int, bold: bool = False) -> str:
     Returns:
         The highlighted string.
     """
-    return '{}\033[{}m{}\033[0m'.format(
-        '\033[1m' if bold else '',
-        color_code,
-        text,
-    )
+    return '{}\033[{}m{}\033[0m'.format('\033[1m' if bold else '', color_code, text)
 
 
 class TeeCapture:
@@ -70,9 +55,9 @@ class TeeCapture:
         self.out_pipe = out_pipe
 
 
-async def _async_forward(async_chunks: AsyncIterable,
-                         out: Optional[Union[TeeCapture, IO[str]]]
-                        ) -> Optional[str]:
+async def _async_forward(
+    async_chunks: AsyncIterable, out: Optional[Union[TeeCapture, IO[str]]]
+) -> Optional[str]:
     """Prints/captures output from the given asynchronous iterable.
 
     Args:
@@ -99,9 +84,9 @@ async def _async_forward(async_chunks: AsyncIterable,
 
 
 async def _async_wait_for_process(
-        future_process: Any,
-        out: Optional[Union[TeeCapture, IO[str]]] = sys.stdout,
-        err: Optional[Union[TeeCapture, IO[str]]] = sys.stderr
+    future_process: Any,
+    out: Optional[Union[TeeCapture, IO[str]]] = sys.stdout,
+    err: Optional[Union[TeeCapture, IO[str]]] = sys.stderr,
 ) -> CommandOutput:
     """Awaits the creation and completion of an asynchronous process.
 
@@ -122,8 +107,7 @@ async def _async_wait_for_process(
     return CommandOutput(output, err_output, process.returncode)
 
 
-def abbreviate_command_arguments_after_switches(cmd: Tuple[str, ...]
-                                               ) -> Tuple[str, ...]:
+def abbreviate_command_arguments_after_switches(cmd: Tuple[str, ...]) -> Tuple[str, ...]:
     result = [cmd[0]]
     for i in range(1, len(cmd)):
         if not cmd[i].startswith('-'):
@@ -133,13 +117,15 @@ def abbreviate_command_arguments_after_switches(cmd: Tuple[str, ...]
     return tuple(result)
 
 
-def run_cmd(*cmd: Optional[str],
-            out: Optional[Union[TeeCapture, IO[str]]] = sys.stdout,
-            err: Optional[Union[TeeCapture, IO[str]]] = sys.stderr,
-            raise_on_fail: bool = True,
-            log_run_to_stderr: bool = True,
-            abbreviate_non_option_arguments: bool = False,
-            **kwargs) -> CommandOutput:
+def run_cmd(
+    *cmd: Optional[str],
+    out: Optional[Union[TeeCapture, IO[str]]] = sys.stdout,
+    err: Optional[Union[TeeCapture, IO[str]]] = sys.stderr,
+    raise_on_fail: bool = True,
+    log_run_to_stderr: bool = True,
+    abbreviate_non_option_arguments: bool = False,
+    **kwargs,
+) -> CommandOutput:
     """Invokes a subprocess and waits for it to finish.
 
     Args:
@@ -182,23 +168,44 @@ def run_cmd(*cmd: Optional[str],
         if abbreviate_non_option_arguments:
             cmd_desc = abbreviate_command_arguments_after_switches(cmd_desc)
         print('run:', cmd_desc, file=sys.stderr)
-    result = asyncio.get_event_loop().run_until_complete(
-        _async_wait_for_process(
-            asyncio.create_subprocess_exec(*kept_cmd,
-                                           stdout=asyncio.subprocess.PIPE,
-                                           stderr=asyncio.subprocess.PIPE,
-                                           **kwargs), out, err))
+
+    loop = None
+    try:
+        loop = asyncio.get_running_loop()
+    except RuntimeError:
+        loop = asyncio.new_event_loop()
+        asyncio.set_event_loop(loop)
+
+    try:
+        result = loop.run_until_complete(
+            _async_wait_for_process(
+                asyncio.create_subprocess_exec(
+                    *kept_cmd,
+                    stdout=asyncio.subprocess.PIPE,
+                    stderr=asyncio.subprocess.PIPE,
+                    **kwargs,
+                ),
+                out,
+                err,
+            )
+        )
+    finally:
+        if loop != None:
+            loop.close()
+
     if raise_on_fail and result[2]:
         raise subprocess.CalledProcessError(result[2], kept_cmd)
     return result
 
 
-def run_shell(cmd: str,
-              out: Optional[Union[TeeCapture, IO[str]]] = sys.stdout,
-              err: Optional[Union[TeeCapture, IO[str]]] = sys.stderr,
-              raise_on_fail: bool = True,
-              log_run_to_stderr: bool = True,
-              **kwargs) -> CommandOutput:
+def run_shell(
+    cmd: str,
+    out: Optional[Union[TeeCapture, IO[str]]] = sys.stdout,
+    err: Optional[Union[TeeCapture, IO[str]]] = sys.stderr,
+    raise_on_fail: bool = True,
+    log_run_to_stderr: bool = True,
+    **kwargs,
+) -> CommandOutput:
     """Invokes a shell command and waits for it to finish.
 
     Args:
@@ -233,12 +240,28 @@ def run_shell(cmd: str,
     """
     if log_run_to_stderr:
         print('shell:', cmd, file=sys.stderr)
-    result = asyncio.get_event_loop().run_until_complete(
-        _async_wait_for_process(
-            asyncio.create_subprocess_shell(cmd,
-                                            stdout=asyncio.subprocess.PIPE,
-                                            stderr=asyncio.subprocess.PIPE,
-                                            **kwargs), out, err))
+
+    loop = None
+    try:
+        loop = asyncio.get_running_loop()
+    except RuntimeError:
+        loop = asyncio.new_event_loop()
+        asyncio.set_event_loop(loop)
+
+    try:
+        result = loop.run_until_complete(
+            _async_wait_for_process(
+                asyncio.create_subprocess_shell(
+                    cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, **kwargs
+                ),
+                out,
+                err,
+            )
+        )
+    finally:
+        if loop != None:
+            loop.close()
+
     if raise_on_fail and result[2]:
         raise subprocess.CalledProcessError(result[2], cmd)
     return result
@@ -261,9 +284,7 @@ def output_of(*cmd: Optional[str], **kwargs) -> str:
          subprocess.CalledProcessError: The process returned a non-zero error
             code and raise_on_fail was set.
     """
-    result = cast(
-        str,
-        run_cmd(*cmd, log_run_to_stderr=False, out=TeeCapture(), **kwargs).out)
+    result = cast(str, run_cmd(*cmd, log_run_to_stderr=False, out=TeeCapture(), **kwargs).out)
 
     # Strip final newline.
     if result.endswith('\n'):
diff --git a/setup.py b/setup.py
index 33fe33d..c3ea535 100755
--- a/setup.py
+++ b/setup.py
@@ -11,26 +11,28 @@
 #   limitations under the License.
 
 import os
+import runpy
 
 from setuptools import find_packages, setup
 
 # This reads the __version__ variable from openfermion/_version.py
-__version__ = ''
-exec(open('src/openfermion/_version.py').read())
+__version__ = runpy.run_path('src/openfermion/_version.py')['__version__']
+assert __version__, 'Version string cannot be empty'
 
-# Readme file as long_description:
+# The readme file is used as the long_description:
 long_description = '===========\n' + 'OpenFermion\n' + '===========\n\n'
 with open('README.rst', 'r', encoding='utf-8') as readme:
     long_description += readme.read()
 
-# Read in package requirements.txt
-requirements = open('dev_tools/requirements/deps/runtime.txt').readlines()
+# Read in package requirements.txt.
+with open('dev_tools/requirements/deps/runtime.txt') as r:
+    requirements = r.readlines()
 requirements = [r.strip() for r in requirements]
 requirements = [r for r in requirements if not r.startswith('#')]
-# Resource estimates requirements.
-resource_requirements = open(
-    'dev_tools/requirements/deps/resource_estimates_runtime.txt'
-).readlines()
+
+# Read in resource estimates requirements.
+with open('dev_tools/requirements/deps/resource_estimates_runtime.txt') as r:
+    resource_requirements = r.readlines()
 resource_requirements = [r.strip() for r in resource_requirements]
 resource_requirements = [r for r in resource_requirements if not r.startswith('#')]
 
diff --git a/src/openfermion/_version.py b/src/openfermion/_version.py
index ea3f219..d0a2fed 100644
--- a/src/openfermion/_version.py
+++ b/src/openfermion/_version.py
@@ -11,4 +11,4 @@
 #   limitations under the License.
 
 """Define version number here and read it from setup.py automatically"""
-__version__ = "1.7.1.dev0"
+__version__ = "1.7.1"
diff --git a/src/openfermion/chem/molecular_data.py b/src/openfermion/chem/molecular_data.py
index 82789b7..94c02e7 100644
--- a/src/openfermion/chem/molecular_data.py
+++ b/src/openfermion/chem/molecular_data.py
@@ -841,6 +841,7 @@ class MolecularData(object):
         try:
             os.remove("{}.hdf5".format(self.filename))
         except OSError:
+            # Nothing to do but carry on.
             pass
 
         shutil.move("{}.hdf5".format(tmp_name), "{}.hdf5".format(self.filename))
diff --git a/src/openfermion/circuits/gates/four_qubit_gates.py b/src/openfermion/circuits/gates/four_qubit_gates.py
index 59badda..401d378 100644
--- a/src/openfermion/circuits/gates/four_qubit_gates.py
+++ b/src/openfermion/circuits/gates/four_qubit_gates.py
@@ -120,8 +120,10 @@ class DoubleExcitationGate(cirq.EigenGate):
         if args.use_unicode_characters:
             wire_symbols = ('⇅', '⇅', '⇵', '⇵')
         else:
-            # pylint: disable=anomalous-backslash-in-string
-            wire_symbols = (r'/\ \/', r'/\ \/', '\/ /\\', '\/ /\\')
+            up_down = r'/\ \/'
+            # Split up this string to avoid SyntaxError in Python 3.12.
+            down_up = r'\/ /' + '\\'
+            wire_symbols = (up_down, up_down, down_up, down_up)
         return cirq.CircuitDiagramInfo(
             wire_symbols=wire_symbols, exponent=self._diagram_exponent(args)
         )
diff --git a/src/openfermion/circuits/slater_determinants.py b/src/openfermion/circuits/slater_determinants.py
index 6127d5b..da742f1 100644
--- a/src/openfermion/circuits/slater_determinants.py
+++ b/src/openfermion/circuits/slater_determinants.py
@@ -103,11 +103,9 @@ def gaussian_state_preparation_circuit(
     if not isinstance(quadratic_hamiltonian, QuadraticHamiltonian):
         raise ValueError('Input must be an instance of QuadraticHamiltonian.')
 
-    (
-        orbital_energies,
-        transformation_matrix,
-        _,
-    ) = quadratic_hamiltonian.diagonalizing_bogoliubov_transform(spin_sector=spin_sector)
+    (orbital_energies, transformation_matrix, _) = (
+        quadratic_hamiltonian.diagonalizing_bogoliubov_transform(spin_sector=spin_sector)
+    )
 
     if quadratic_hamiltonian.conserves_particle_number:
         if occupied_orbitals is None:
@@ -221,7 +219,7 @@ def jw_get_gaussian_state(quadratic_hamiltonian, occupied_orbitals=None):
     n_qubits = quadratic_hamiltonian.n_qubits
 
     # Compute the energy
-    orbital_energies, constant = quadratic_hamiltonian.orbital_energies()
+    orbital_energies, _, constant = quadratic_hamiltonian.diagonalizing_bogoliubov_transform()
     if occupied_orbitals is None:
         # The ground energy is desired
         if quadratic_hamiltonian.conserves_particle_number:
diff --git a/src/openfermion/linalg/erpa.py b/src/openfermion/linalg/erpa.py
index b93cbb0..84572aa 100644
--- a/src/openfermion/linalg/erpa.py
+++ b/src/openfermion/linalg/erpa.py
@@ -1,4 +1,5 @@
 """Code to generate the eigenvalue problem for the ERPA equations"""
+
 from typing import Dict, Tuple, Union
 from itertools import product
 import numpy
@@ -11,7 +12,7 @@ from openfermion.utils.rdm_mapping_functions import map_two_pdm_to_one_pdm
 def erpa_eom_hamiltonian(
     h_ijkl: numpy.ndarray, tpdm: numpy.ndarray, p: int, q: int, r: int, s: int
 ) -> Union[float, complex]:
-    """
+    r"""
     Evaluate $\sum_{a,b,c,d}h_{a, b, d, c}<\psi[p^ q, [a^ b^ c d, r^ s]]\psi>$
 
     Args:
diff --git a/src/openfermion/linalg/linear_qubit_operator.py b/src/openfermion/linalg/linear_qubit_operator.py
index a5b326c..656a6cd 100644
--- a/src/openfermion/linalg/linear_qubit_operator.py
+++ b/src/openfermion/linalg/linear_qubit_operator.py
@@ -144,6 +144,8 @@ class LinearQubitOperator(scipy.sparse.linalg.LinearOperator):
 class ParallelLinearQubitOperator(scipy.sparse.linalg.LinearOperator):
     """A LinearOperator from a QubitOperator with multiple processors."""
 
+    _start_method_set = False
+
     def __init__(self, qubit_operator, n_qubits=None, options=None):
         """
         Args:
@@ -162,6 +164,10 @@ class ParallelLinearQubitOperator(scipy.sparse.linalg.LinearOperator):
         self.n_qubits = n_qubits
         self.options = options or LinearQubitOperatorOptions()
 
+        if not ParallelLinearQubitOperator._start_method_set:
+            multiprocessing.set_start_method('forkserver', force=True)
+            ParallelLinearQubitOperator._start_method_set = True
+
         self.qubit_operator_groups = list(
             qubit_operator.get_operator_groups(self.options.processes)
         )
diff --git a/src/openfermion/linalg/sparse_tools.py b/src/openfermion/linalg/sparse_tools.py
index fcc9325..28e5731 100644
--- a/src/openfermion/linalg/sparse_tools.py
+++ b/src/openfermion/linalg/sparse_tools.py
@@ -1162,6 +1162,8 @@ def single_quad_op_sparse(n_modes, mode, quadrature, hbar, trunc):
         op = numpy.sqrt(hbar / 2) * (b + b.conj().T)
     elif quadrature == 'p':
         op = -1j * numpy.sqrt(hbar / 2) * (b - b.conj().T)
+    else:
+        raise ValueError(f'Invalid value {quadrature} for quadrature parameter.')
 
     Id = [scipy.sparse.identity(trunc, dtype=complex, format='csc')]
     operator_list = Id * mode + [op] + Id * (n_modes - mode - 1)
diff --git a/src/openfermion/linalg/wave_fitting.py b/src/openfermion/linalg/wave_fitting.py
index 4cff2ad..95f7566 100644
--- a/src/openfermion/linalg/wave_fitting.py
+++ b/src/openfermion/linalg/wave_fitting.py
@@ -40,7 +40,7 @@ def fit_known_frequencies(
 
 
 def prony(signal: numpy.ndarray) -> Tuple[numpy.ndarray, numpy.ndarray]:
-    """Estimates amplitudes and phases of a sparse signal using Prony's method.
+    r"""Estimates amplitudes and phases of a sparse signal using Prony's method.
 
     Single-ancilla quantum phase estimation returns a signal
     $g(k)=\sum (aj*exp(i*k*phij))$, where aj and phij are the amplitudes
diff --git a/src/openfermion/linalg/wedge_product.py b/src/openfermion/linalg/wedge_product.py
index 154332a..f5ab17e 100644
--- a/src/openfermion/linalg/wedge_product.py
+++ b/src/openfermion/linalg/wedge_product.py
@@ -67,7 +67,7 @@ def generate_parity_permutations(seq):
 
 
 def wedge(left_tensor, right_tensor, left_index_ranks, right_index_ranks):
-    """
+    r"""
     Implement the wedge product between left_tensor and right_tensor
 
     The wedge product is defined as
diff --git a/src/openfermion/resource_estimates/molecule/pyscf_utils.py b/src/openfermion/resource_estimates/molecule/pyscf_utils.py
index 4c22498..c0b070b 100644
--- a/src/openfermion/resource_estimates/molecule/pyscf_utils.py
+++ b/src/openfermion/resource_estimates/molecule/pyscf_utils.py
@@ -1,5 +1,5 @@
 # coverage:ignore
-""" Drivers for various PySCF electronic structure routines """
+"""Drivers for various PySCF electronic structure routines"""
 from typing import Tuple, Optional
 import sys
 import h5py
@@ -82,6 +82,9 @@ def localize(pyscf_mf, loc_type='pm', verbose=0):
         loc_virt_mo = lo.ER(pyscf_mf.mol, pyscf_mf.mo_coeff[:, virt_idx]).kernel(verbose=verbose)
         print("DONE")
 
+    else:
+        raise ValueError(f'Invalid value {loc_type} for localization type parameter.')
+
     # overwrite orbitals with localized orbitals
     pyscf_mf.mo_coeff[:, docc_idx] = loc_docc_mo.copy()
     pyscf_mf.mo_coeff[:, socc_idx] = loc_socc_mo.copy()
diff --git a/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py
index 45f9ce1..f644ba0 100644
--- a/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py
+++ b/src/openfermion/resource_estimates/pbc/thc/factorizations/thc_jax.py
@@ -943,6 +943,7 @@ def kpoint_thc_via_isdf(
     if verbose:
         print("Time for BFGS {:.4f}".format(time.time() - start))
     start = time.time()
+    opt_params = ()
     if perform_adagrad_opt:
         if save_checkpoints:
             chkfile_name = f"{checkpoint_basename}_adagrad.h5"
diff --git a/src/openfermion/resource_estimates/thc/spacetime.py b/src/openfermion/resource_estimates/thc/spacetime.py
index 55c3d92..8faac21 100644
--- a/src/openfermion/resource_estimates/thc/spacetime.py
+++ b/src/openfermion/resource_estimates/thc/spacetime.py
@@ -28,9 +28,6 @@ def qubit_vs_toffoli(lam, dE, eps, n, chi, beta, M, algorithm='half', verbose=Fa
         verbose (bool) - do additional printing of intermediates?
 
     """
-    # only valid algorithms accepted
-    assert algorithm in ['half', 'full']
-
     # The number of iterations for the phase estimation.
     iters = np.ceil(pi * lam / (dE * 2))
     # The number of bits used for each register.
@@ -487,8 +484,12 @@ def qubit_vs_toffoli(lam, dE, eps, n, chi, beta, M, algorithm='half', verbose=Fa
 
         colors = [color_dict[i] for i in labels]
 
+    else:
+        raise ValueError(f'Invalid value {algorithm} for algorithm parameter.')
+
     # check lists are at least consistent
-    assert all(len(element) == len(tgates) for element in [qubits, labels, colors])
+    if not all(len(element) == len(tgates) for element in [qubits, labels, colors]):
+        raise RuntimeError('Failed to get a consistent result.')
 
     return tgates, qubits, labels, colors
 
@@ -579,10 +580,11 @@ def group_steps(labels, tgates, qubits):
         grouped_labels = ['R', 'QROM', 'I-QROM', 'QROM', 'R']
         grouped_tgates = [ 13,     30,       14,     40,  20]  (sum)
         grouped_qubits = [ 10,     30,        4,     70,  60]  (mean)
-
     """
-    assert len(labels) == len(tgates)
-    assert len(labels) == len(qubits)
+    if len(labels) != len(tgates):
+        raise ValueError('Number of labels must equal number of Toffoli gates.')
+    if len(labels) != len(qubits):
+        raise ValueError('Number of labels must equal number of qubits.')
 
     # Key function -- group identical nearest neighbors in labels (x[0])
     key_func = lambda x: x[0]
@@ -604,5 +606,6 @@ def group_steps(labels, tgates, qubits):
         grouped_qubits.append(np.mean([i[1] for i in group]))
 
     # sanity check -- shouldn't be losing total value in toffoli
-    assert np.sum(tgates) == np.sum(grouped_tgates)
+    if np.sum(tgates) != np.sum(grouped_tgates):
+        raise RuntimeError('Sum of Toffoli gates does not equal sum of grouped gates.')
     return grouped_labels, grouped_tgates, grouped_qubits
diff --git a/src/openfermion/resource_estimates/thc/utils/adagrad.py b/src/openfermion/resource_estimates/thc/utils/adagrad.py
index c77b2f9..523d8e2 100644
--- a/src/openfermion/resource_estimates/thc/utils/adagrad.py
+++ b/src/openfermion/resource_estimates/thc/utils/adagrad.py
@@ -37,10 +37,12 @@ def adagrad(step_size, momentum=0.9):
         m = np.zeros_like(x0)
         return x0, g_sq, m
 
-    def update(i, g, state):
+    def update(i, g, state, eps=1e-9):
         x, g_sq, m = state
         g_sq += np.square(g)
-        g_sq_inv_sqrt = np.where(g_sq > 0, 1.0 / np.sqrt(g_sq), 0.0)
+        # Add a small number to avoid division by zero
+        g_sq_safe = g_sq + eps
+        g_sq_inv_sqrt = 1.0 / np.sqrt(g_sq_safe)
         m = (1.0 - momentum) * (g * g_sq_inv_sqrt) + momentum * m
         x = x - step_size(i) * m
         return x, g_sq, m
diff --git a/src/openfermion/transforms/opconversions/term_reordering.py b/src/openfermion/transforms/opconversions/term_reordering.py
index ba9652a..f7a59ab 100644
--- a/src/openfermion/transforms/opconversions/term_reordering.py
+++ b/src/openfermion/transforms/opconversions/term_reordering.py
@@ -189,6 +189,8 @@ def normal_ordered_ladder_term(term, coefficient, parity=-1):
         Op = FermionOperator
     elif parity == 1:
         Op = BosonOperator
+    else:
+        raise ValueError(f'Invalid value {parity} for parity parameter')
 
     ordered_term = Op()
 
diff --git a/src/openfermion/utils/bch_expansion.py b/src/openfermion/utils/bch_expansion.py
index d2a2f17..514118b 100644
--- a/src/openfermion/utils/bch_expansion.py
+++ b/src/openfermion/utils/bch_expansion.py
@@ -16,7 +16,7 @@ from scipy.special import comb, factorial
 
 
 def bch_expand(*ops, **kwargs):
-    """Compute $\log[e^{x_1} ... e^{x_N}]$ using the BCH formula.
+    r"""Compute $\log[e^{x_1} ... e^{x_N}]$ using the BCH formula.
 
     This implementation is explained in arXiv:1712.01348.
 
@@ -65,7 +65,7 @@ def _bch_expand_multiple_terms(*ops, **kwargs):
 
 
 def _bch_expand_two_terms(x, y, order=6):
-    """Compute $\log[e^x e^y]$ using the Baker-Campbell-Hausdorff formula.
+    r"""Compute $\log[e^x e^y]$ using the Baker-Campbell-Hausdorff formula.
 
     Args:
         x: An operator for which multiplication and addition are supported.


Test patch:
diff --git a/src/openfermion/chem/molecular_data_test.py b/src/openfermion/chem/molecular_data_test.py
index a6c222d..e17ac70 100644
--- a/src/openfermion/chem/molecular_data_test.py
+++ b/src/openfermion/chem/molecular_data_test.py
@@ -81,9 +81,7 @@ class MolecularDataTest(unittest.TestCase):
 
         # Check errors in naming
         with self.assertRaises(TypeError):
-            test_molecule = MolecularData(
-                self.geometry, self.basis, self.multiplicity, description=5
-            )
+            MolecularData(self.geometry, self.basis, self.multiplicity, description=5)
         correct_name = str('H2_sto-3g_singlet')
         test_molecule = self.molecule = MolecularData(
             self.geometry, self.basis, self.multiplicity, data_directory=DATA_DIRECTORY
@@ -222,11 +220,9 @@ class MolecularDataTest(unittest.TestCase):
         """Test simple active space truncation features"""
 
         # Start w/ no truncation
-        (
-            core_const,
-            one_body_integrals,
-            two_body_integrals,
-        ) = self.molecule.get_active_space_integrals(active_indices=[0, 1])
+        (core_const, one_body_integrals, two_body_integrals) = (
+            self.molecule.get_active_space_integrals(active_indices=[0, 1])
+        )
 
         self.assertAlmostEqual(core_const, 0.0)
         self.assertAlmostEqual(
diff --git a/src/openfermion/circuits/primitives/optimal_givens_decomposition_test.py b/src/openfermion/circuits/primitives/optimal_givens_decomposition_test.py
index f386cf2..414e30a 100644
--- a/src/openfermion/circuits/primitives/optimal_givens_decomposition_test.py
+++ b/src/openfermion/circuits/primitives/optimal_givens_decomposition_test.py
@@ -106,7 +106,6 @@ def test_col_eliminate():
     # mixing U[1, 0] and U[0, 0]
     unitary_original = unitary.copy()
     gmat = givens_matrix_elements(unitary[0, 0], unitary[1, 0], which='right')
-    vec = numpy.array([[unitary[0, 0]], [unitary[1, 0]]])
     fullgmat = create_givens(gmat, 0, 1, 3)
     zeroed_unitary = fullgmat.dot(unitary)
 
diff --git a/src/openfermion/circuits/slater_determinants_test.py b/src/openfermion/circuits/slater_determinants_test.py
index 56703a1..c23de15 100644
--- a/src/openfermion/circuits/slater_determinants_test.py
+++ b/src/openfermion/circuits/slater_determinants_test.py
@@ -198,7 +198,9 @@ class JWGetGaussianStateTest(unittest.TestCase):
             )
 
             # Compute the true energy
-            orbital_energies, constant = quadratic_hamiltonian.orbital_energies()
+            orbital_energies, _, constant = (
+                quadratic_hamiltonian.diagonalizing_bogoliubov_transform()
+            )
             energy = numpy.sum(orbital_energies[occupied_orbitals]) + constant
 
             # Check that the energies match
@@ -228,7 +230,9 @@ class JWGetGaussianStateTest(unittest.TestCase):
             )
 
             # Compute the true energy
-            orbital_energies, constant = quadratic_hamiltonian.orbital_energies()
+            orbital_energies, _, constant = (
+                quadratic_hamiltonian.diagonalizing_bogoliubov_transform()
+            )
             energy = numpy.sum(orbital_energies[occupied_orbitals]) + constant
 
             # Check that the energies match
diff --git a/src/openfermion/linalg/sparse_tools_test.py b/src/openfermion/linalg/sparse_tools_test.py
index 29be37f..30a555e 100644
--- a/src/openfermion/linalg/sparse_tools_test.py
+++ b/src/openfermion/linalg/sparse_tools_test.py
@@ -1192,6 +1192,11 @@ class BosonSparseTest(unittest.TestCase):
         expected = numpy.kron(self.Id, self.p)
         self.assertTrue(numpy.allclose(res, expected))
 
+    def test_single_quad_op_sparse_invalid_quadrature(self):
+        """Test ValueError for invalid quadrature in single_quad_op_sparse."""
+        with self.assertRaisesRegex(ValueError, "Invalid value x for quadrature parameter."):
+            single_quad_op_sparse(n_modes=1, mode=0, quadrature='x', hbar=1.0, trunc=2)
+
     def test_boson_operator_sparse_trunc(self):
         op = BosonOperator('0')
         with self.assertRaises(ValueError):
diff --git a/src/openfermion/ops/representations/polynomial_tensor_test.py b/src/openfermion/ops/representations/polynomial_tensor_test.py
index 62b6d68..8b16611 100644
--- a/src/openfermion/ops/representations/polynomial_tensor_test.py
+++ b/src/openfermion/ops/representations/polynomial_tensor_test.py
@@ -488,7 +488,7 @@ class PolynomialTensorTest(unittest.TestCase):
         # Initialize a particle-number-conserving quadratic Hamiltonian
         # and compute its orbital energies
         quad_ham = random_quadratic_hamiltonian(n_qubits, True, real=real)
-        orbital_energies, constant = quad_ham.orbital_energies()
+        orbital_energies, _, constant = quad_ham.diagonalizing_bogoliubov_transform()
 
         # Rotate a basis where the Hamiltonian is diagonal
         _, diagonalizing_unitary, _ = quad_ham.diagonalizing_bogoliubov_transform()
@@ -504,7 +504,8 @@ class PolynomialTensorTest(unittest.TestCase):
         self.assertTrue(quad_ham.conserves_particle_number)
 
         # Check that the orbital energies and constant are the same
-        new_orbital_energies, new_constant = quad_ham.orbital_energies()
+        new_orbital_energies, _, new_constant = quad_ham.diagonalizing_bogoliubov_transform()
+
         self.assertTrue(numpy.allclose(orbital_energies, new_orbital_energies))
         self.assertAlmostEqual(constant, new_constant)
 
diff --git a/src/openfermion/ops/representations/quadratic_hamiltonian_test.py b/src/openfermion/ops/representations/quadratic_hamiltonian_test.py
index 9fe9261..4937b78 100644
--- a/src/openfermion/ops/representations/quadratic_hamiltonian_test.py
+++ b/src/openfermion/ops/representations/quadratic_hamiltonian_test.py
@@ -121,13 +121,15 @@ class QuadraticHamiltonianTest(unittest.TestCase):
     def test_orbital_energies(self):
         """Test getting the orbital energies."""
         # Test the particle-number-conserving case
-        orbital_energies, constant = self.quad_ham_pc.orbital_energies()
+        orbital_energies, _, constant = self.quad_ham_pc.diagonalizing_bogoliubov_transform()
+
         # Test the ground energy
         energy = numpy.sum(orbital_energies[orbital_energies < 0.0]) + constant
         self.assertAlmostEqual(energy, self.pc_ground_energy)
 
         # Test the non-particle-number-conserving case
-        orbital_energies, constant = self.quad_ham_npc.orbital_energies()
+        orbital_energies, _, constant = self.quad_ham_npc.diagonalizing_bogoliubov_transform()
+
         # Test the ground energy
         energy = constant
         self.assertAlmostEqual(energy, self.npc_ground_energy)
@@ -265,11 +267,9 @@ class QuadraticHamiltonianTest(unittest.TestCase):
         quad_ham = get_quadratic_hamiltonian(reorder(get_fermion_operator(quad_ham), up_then_down))
 
         for spin_sector in range(2):
-            (
-                orbital_energies,
-                transformation_matrix,
-                _,
-            ) = quad_ham.diagonalizing_bogoliubov_transform(spin_sector=spin_sector)
+            (orbital_energies, transformation_matrix, _) = (
+                quad_ham.diagonalizing_bogoliubov_transform(spin_sector=spin_sector)
+            )
 
             def index_map(i):
                 return i + spin_sector * 5
@@ -286,7 +286,7 @@ class QuadraticHamiltonianTest(unittest.TestCase):
         quad_ham = random_quadratic_hamiltonian(
             5, conserves_particle_number=True, expand_spin=False
         )
-        orbital_energies, _ = quad_ham.orbital_energies()
+        orbital_energies, _, _ = quad_ham.diagonalizing_bogoliubov_transform()
 
         _, transformation_matrix, _ = quad_ham.diagonalizing_bogoliubov_transform()
         numpy.testing.assert_allclose(
@@ -337,7 +337,9 @@ class DiagonalizingCircuitTest(unittest.TestCase):
             numpy.testing.assert_allclose(discrepancy, 0.0, atol=1e-7)
 
             # Check that the eigenvalues are in the expected order
-            orbital_energies, constant = quadratic_hamiltonian.orbital_energies()
+            orbital_energies, _, constant = (
+                quadratic_hamiltonian.diagonalizing_bogoliubov_transform()
+            )
             for index in range(2**n_qubits):
                 bitstring = bin(index)[2:].zfill(n_qubits)
                 subset = [j for j in range(n_qubits) if bitstring[j] == '1']
@@ -374,7 +376,9 @@ class DiagonalizingCircuitTest(unittest.TestCase):
             numpy.testing.assert_allclose(discrepancy, 0.0, atol=1e-7)
 
             # Check that the eigenvalues are in the expected order
-            orbital_energies, constant = quadratic_hamiltonian.orbital_energies()
+            orbital_energies, _, constant = (
+                quadratic_hamiltonian.diagonalizing_bogoliubov_transform()
+            )
             for index in range(2**n_qubits):
                 bitstring = bin(index)[2:].zfill(n_qubits)
                 subset = [j for j in range(n_qubits) if bitstring[j] == '1']
diff --git a/src/openfermion/transforms/opconversions/term_reordering_test.py b/src/openfermion/transforms/opconversions/term_reordering_test.py
index 0e4d446..0a75205 100644
--- a/src/openfermion/transforms/opconversions/term_reordering_test.py
+++ b/src/openfermion/transforms/opconversions/term_reordering_test.py
@@ -23,6 +23,7 @@ from openfermion.utils import up_then_down
 
 from openfermion.transforms.opconversions.term_reordering import (
     normal_ordered,
+    normal_ordered_ladder_term,
     chemist_ordered,
     reorder,
 )
@@ -221,6 +222,22 @@ class TestNormalOrdering(unittest.TestCase):
         with self.assertRaises(TypeError):
             _ = normal_ordered(1)
 
+    def test_normal_ordered_ladder_term_invalid_parity(self):
+        term = ((0, 1), (1, 0))
+        coefficient = 1.0
+        invalid_parity_1 = 2
+        invalid_parity_2 = -2
+
+        with self.assertRaisesRegex(
+            ValueError, f"Invalid value {invalid_parity_1} for parity parameter"
+        ):
+            normal_ordered_ladder_term(term, coefficient, parity=invalid_parity_1)
+
+        with self.assertRaisesRegex(
+            ValueError, f"Invalid value {invalid_parity_2} for parity parameter"
+        ):
+            normal_ordered_ladder_term(term, coefficient, parity=invalid_parity_2)
+
 
 class TestReorder(unittest.TestCase):
     def test_reorder(self):