Repository: quantumlib/OpenFermion
Pull request #828 (2023-08-01 23:26:00)

Enable testing for resource estimates
Previously the resource estimation code was not being tested as part of the CI (#825) . This PR enables it, I've marked a few tests as slow and will open an issue to track the fact that one dependency (btas/pybtas) doesn't have any tests. I think we should add a weekly workflow which will run the slow tests and run a yet to be written pybtas unit test.

I'm only testing the resource estimate code for linux and mac as I don't think pyscf works on windows.

fdmalone: @mpharrigan PTAL
mpharrigan: the output from the ci job looks like the desired tests aren't being run (??)

```
Run check/pytest -m 'not slow' src/openfermion/resource_estimates
============================= test session starts ==============================
platform linux -- Python 3.10.12, pytest-[7](https://github.com/quantumlib/OpenFermion/actions/runs/5735347442/job/15542949189?pr=828#step:5:8).3.1, pluggy-1.0.0
rootdir: /home/runner/work/OpenFermion/OpenFermion/dev_tools/conf
configfile: pytest.ini
plugins: cov-4.0.0, asyncio-0.21.0, xdist-3.3.1
asyncio: mode=strict
collected 25 items / 7 deselected / 1[8](https://github.com/quantumlib/OpenFermion/actions/runs/5735347442/job/15542949189?pr=828#step:5:9) selected

dev_tools/conf/utils_test.py .....                                       [ 27%]
dev_tools/conf/df/compute_cost_df_test.py ..                             [ 38%]
dev_tools/conf/df/compute_lambda_df_test.py .                            [ 44%]
dev_tools/conf/sf/compute_cost_sf_test.py ..                             [ 55%]
dev_tools/conf/sf/compute_lambda_sf_test.py .                            [ 61%]
dev_tools/conf/sparse/costing_sparse_test.py ..                          [ 72%]
dev_tools/conf/thc/compute_cost_thc_test.py ..                           [ 83%]
dev_tools/conf/thc/compute_lambda_thc_test.py .                          [ 88%]
dev_tools/conf/thc/spacetime_test.py ..                                  [[10](https://github.com/quantumlib/OpenFermion/actions/runs/5735347442/job/15542949189?pr=828#step:5:11)0%]

=============================== warnings summary ===============================
```
mpharrigan: or is that a weird artifact of the runner? those test files aren't in `dev_tools/conf` and are indeed the correct tests
fdmalone: very odd, I have no idea why it's giving those paths...
fdmalone: Maybe to do with pytest saying the root_dir is dev_tools/conf, maybe I used the wrong command to specify the pytest.ini file.
mpharrigan: maybe that grep expression is doing something weird
fdmalone: Nah the grep is quieting the output I think. I needed to pass --rootdir through to pytest, somehow specifying the pytest.ini file sets the rootdir to where the ini file is (dev_tools/conf). Should be fixed now.

Patch:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7931bc5..b178a2c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -71,6 +71,27 @@ jobs:
       - name: Pytest check
         run: check/pytest
         shell: bash
+  pytest_resource_estimates:
+    name: Pytest Resource Estimates
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ubuntu-latest, macos-latest]
+        python-version: [ '3.10' ]
+        cirq-version: [ '~=1.0' ]
+      fail-fast: false
+    steps:
+      - uses: actions/checkout@v2
+      - uses: actions/setup-python@v2
+        with:
+          python-version: ${{ matrix.python-version }}
+      - name: Install requirements
+        run: |
+          pip install -r dev_tools/requirements/resource_estimates.env.txt
+          pip install cirq-core${{matrix.cirq-version}}
+      - name: Pytest check resources
+        run: check/pytest -m 'not slow' src/openfermion/resource_estimates
+        shell: bash
   coverage:
     name: Coverage check
     runs-on: ubuntu-latest
diff --git a/.gitignore b/.gitignore
index 122ce4b..e6ba1d5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -89,3 +89,5 @@ ENV/
 
 # no .dat files
 *.dat
+
+*tmp*
diff --git a/dev_tools/requirements/deps/resource_estimates.txt b/dev_tools/requirements/deps/resource_estimates.txt
new file mode 100644
index 0000000..2a580db
--- /dev/null
+++ b/dev_tools/requirements/deps/resource_estimates.txt
@@ -0,0 +1,3 @@
+pyscf
+jax
+jaxlib
diff --git a/dev_tools/requirements/re-pip-compile.sh b/dev_tools/requirements/re-pip-compile.sh
index 513ce2e..2abd2f6 100644
--- a/dev_tools/requirements/re-pip-compile.sh
+++ b/dev_tools/requirements/re-pip-compile.sh
@@ -11,3 +11,4 @@ pip-compile $@ --output-file=pylint.env.txt --resolver=backtracking deps/pylint.
 pip-compile $@ --output-file=pytest.env.txt --resolver=backtracking deps/pytest.txt    runtime.env.txt
 pip-compile $@ --output-file=mypy.env.txt   --resolver=backtracking deps/mypy.txt      runtime.env.txt
 pip-compile $@ --output-file=dev.env.txt    --resolver=backtracking deps/dev-tools.txt runtime.env.txt
+pip-compile $@ --output-file=dev.env.txt    --resolver=backtracking deps/resource_estimates.txt pytest.env.txt
diff --git a/dev_tools/requirements/resource_estimates.env.txt b/dev_tools/requirements/resource_estimates.env.txt
new file mode 100644
index 0000000..68b7b49
--- /dev/null
+++ b/dev_tools/requirements/resource_estimates.env.txt
@@ -0,0 +1,218 @@
+#
+# This file is autogenerated by pip-compile with Python 3.10
+# by the following command:
+#
+#    pip-compile --output-file=resource_estimates.env.txt deps/resource_estimates.txt pytest.env.txt
+#
+attrs==23.1.0
+    # via
+    #   -r pytest.env.txt
+    #   jsonschema
+certifi==2023.5.7
+    # via
+    #   -r pytest.env.txt
+    #   requests
+charset-normalizer==3.1.0
+    # via
+    #   -r pytest.env.txt
+    #   requests
+cirq-core==1.1.0
+    # via -r pytest.env.txt
+contourpy==1.0.7
+    # via
+    #   -r pytest.env.txt
+    #   matplotlib
+coverage[toml]==7.2.5
+    # via
+    #   -r pytest.env.txt
+    #   pytest-cov
+cycler==0.11.0
+    # via
+    #   -r pytest.env.txt
+    #   matplotlib
+deprecation==2.1.0
+    # via -r pytest.env.txt
+duet==0.2.8
+    # via
+    #   -r pytest.env.txt
+    #   cirq-core
+exceptiongroup==1.1.1
+    # via
+    #   -r pytest.env.txt
+    #   pytest
+execnet==1.9.0
+    # via
+    #   -r pytest.env.txt
+    #   pytest-xdist
+fastjsonschema==2.17.1
+    # via
+    #   -r pytest.env.txt
+    #   nbformat
+fonttools==4.39.4
+    # via
+    #   -r pytest.env.txt
+    #   matplotlib
+h5py==3.8.0
+    # via
+    #   -r pytest.env.txt
+    #   pyscf
+idna==3.4
+    # via
+    #   -r pytest.env.txt
+    #   requests
+iniconfig==2.0.0
+    # via
+    #   -r pytest.env.txt
+    #   pytest
+jax==0.4.14
+    # via -r deps/resource_estimates.txt
+jaxlib==0.4.14
+    # via -r deps/resource_estimates.txt
+jsonschema==4.17.3
+    # via
+    #   -r pytest.env.txt
+    #   nbformat
+jupyter-core==5.3.0
+    # via
+    #   -r pytest.env.txt
+    #   nbformat
+kiwisolver==1.4.4
+    # via
+    #   -r pytest.env.txt
+    #   matplotlib
+matplotlib==3.7.1
+    # via
+    #   -r pytest.env.txt
+    #   cirq-core
+ml-dtypes==0.2.0
+    # via
+    #   jax
+    #   jaxlib
+mpmath==1.3.0
+    # via
+    #   -r pytest.env.txt
+    #   sympy
+nbformat==5.8.0
+    # via -r pytest.env.txt
+networkx==2.8.8
+    # via
+    #   -r pytest.env.txt
+    #   cirq-core
+numpy==1.23.5
+    # via
+    #   -r pytest.env.txt
+    #   cirq-core
+    #   contourpy
+    #   h5py
+    #   jax
+    #   jaxlib
+    #   matplotlib
+    #   ml-dtypes
+    #   opt-einsum
+    #   pandas
+    #   pyscf
+    #   scipy
+opt-einsum==3.3.0
+    # via jax
+packaging==23.1
+    # via
+    #   -r pytest.env.txt
+    #   deprecation
+    #   matplotlib
+    #   pytest
+pandas==2.0.1
+    # via
+    #   -r pytest.env.txt
+    #   cirq-core
+pillow==9.5.0
+    # via
+    #   -r pytest.env.txt
+    #   matplotlib
+platformdirs==3.5.1
+    # via
+    #   -r pytest.env.txt
+    #   jupyter-core
+pluggy==1.0.0
+    # via
+    #   -r pytest.env.txt
+    #   pytest
+pubchempy==1.0.4
+    # via -r pytest.env.txt
+pyparsing==3.0.9
+    # via
+    #   -r pytest.env.txt
+    #   matplotlib
+pyrsistent==0.19.3
+    # via
+    #   -r pytest.env.txt
+    #   jsonschema
+pyscf==2.3.0
+    # via -r deps/resource_estimates.txt
+pytest==7.3.1
+    # via
+    #   -r pytest.env.txt
+    #   pytest-asyncio
+    #   pytest-cov
+    #   pytest-xdist
+pytest-asyncio==0.21.0
+    # via -r pytest.env.txt
+pytest-cov==4.0.0
+    # via -r pytest.env.txt
+pytest-xdist==3.3.1
+    # via -r pytest.env.txt
+python-dateutil==2.8.2
+    # via
+    #   -r pytest.env.txt
+    #   matplotlib
+    #   pandas
+pytz==2023.3
+    # via
+    #   -r pytest.env.txt
+    #   pandas
+requests==2.31.0
+    # via -r pytest.env.txt
+scipy==1.9.3
+    # via
+    #   -r pytest.env.txt
+    #   cirq-core
+    #   jax
+    #   jaxlib
+    #   pyscf
+six==1.16.0
+    # via
+    #   -r pytest.env.txt
+    #   python-dateutil
+sortedcontainers==2.4.0
+    # via
+    #   -r pytest.env.txt
+    #   cirq-core
+sympy==1.12
+    # via
+    #   -r pytest.env.txt
+    #   cirq-core
+tomli==2.0.1
+    # via
+    #   -r pytest.env.txt
+    #   coverage
+    #   pytest
+tqdm==4.65.0
+    # via
+    #   -r pytest.env.txt
+    #   cirq-core
+traitlets==5.9.0
+    # via
+    #   -r pytest.env.txt
+    #   jupyter-core
+    #   nbformat
+typing-extensions==4.6.0
+    # via
+    #   -r pytest.env.txt
+    #   cirq-core
+tzdata==2023.3
+    # via
+    #   -r pytest.env.txt
+    #   pandas
+urllib3==2.0.2
+    # via
+    #   -r pytest.env.txt
+    #   requests
diff --git a/src/openfermion/resource_estimates/__init__.py b/src/openfermion/resource_estimates/__init__.py
index d91d98e..70e4764 100644
--- a/src/openfermion/resource_estimates/__init__.py
+++ b/src/openfermion/resource_estimates/__init__.py
@@ -1,4 +1,4 @@
-#coverage:ignore
+# coverage:ignore
 #   Licensed under the Apache License, Version 2.0 (the "License");
 #   you may not use this file except in compliance with the License.
 #   You may obtain a copy of the License at
@@ -10,10 +10,10 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
-#coverage: ignore
-import pytest
 
 try:
+    import jax
     import pyscf
-except ImportError:
-    pytest.skip('Need pyscf for resource estimates', allow_module_level=True)
+    HAVE_DEPS_FOR_RESOURCE_ESTIMATES = True
+except ModuleNotFoundError:
+    HAVE_DEPS_FOR_RESOURCE_ESTIMATES = False
diff --git a/src/openfermion/resource_estimates/df/__init__.py b/src/openfermion/resource_estimates/df/__init__.py
index e8fbffd..c888822 100644
--- a/src/openfermion/resource_estimates/df/__init__.py
+++ b/src/openfermion/resource_estimates/df/__init__.py
@@ -13,7 +13,10 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from .compute_lambda_df import compute_lambda
-from .compute_cost_df import compute_cost
-from .factorize_df import factorize
-from .generate_costing_table_df import generate_costing_table
+from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES
+
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
+    from .compute_cost_df import compute_cost
+    from .compute_lambda_df import compute_lambda
+    from .factorize_df import factorize
+    from .generate_costing_table_df import generate_costing_table
\ No newline at end of file
diff --git a/src/openfermion/resource_estimates/molecule/__init__.py b/src/openfermion/resource_estimates/molecule/__init__.py
index 1aae53f..b9713f5 100644
--- a/src/openfermion/resource_estimates/molecule/__init__.py
+++ b/src/openfermion/resource_estimates/molecule/__init__.py
@@ -11,16 +11,10 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from .pyscf_utils import (
-    stability,
-    localize,
-    avas_active_space,
-    cas_to_pyscf,
-    pyscf_to_cas,
-    get_num_active_alpha_beta,
-    load_casfile_to_pyscf,
-    save_pyscf_to_casfile,
-    factorized_ccsd_t,
-    ccsd_t,
-    open_shell_t1_d1,
-)
+from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES
+
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
+    from .pyscf_utils import (avas_active_space, cas_to_pyscf, ccsd_t,
+                              factorized_ccsd_t, get_num_active_alpha_beta,
+                              load_casfile_to_pyscf, localize, open_shell_t1_d1,
+                              pyscf_to_cas, save_pyscf_to_casfile, stability)
diff --git a/src/openfermion/resource_estimates/sf/__init__.py b/src/openfermion/resource_estimates/sf/__init__.py
index 0918545..1825cf6 100644
--- a/src/openfermion/resource_estimates/sf/__init__.py
+++ b/src/openfermion/resource_estimates/sf/__init__.py
@@ -13,7 +13,10 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from .compute_lambda_sf import compute_lambda
-from .compute_cost_sf import compute_cost
-from .factorize_sf import factorize
-from .generate_costing_table_sf import generate_costing_table
+from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES
+
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
+    from .compute_cost_sf import compute_cost
+    from .compute_lambda_sf import compute_lambda
+    from .factorize_sf import factorize
+    from .generate_costing_table_sf import generate_costing_table
diff --git a/src/openfermion/resource_estimates/sf/generate_costing_table_sf.py b/src/openfermion/resource_estimates/sf/generate_costing_table_sf.py
index 7eadb94..003feaf 100644
--- a/src/openfermion/resource_estimates/sf/generate_costing_table_sf.py
+++ b/src/openfermion/resource_estimates/sf/generate_costing_table_sf.py
@@ -1,12 +1,20 @@
 #coverage:ignore
 """ Pretty-print a table comparing number of SF vectors versus acc and cost """
 import numpy as np
-from pyscf import scf
-from openfermion.resource_estimates import sf
-from openfermion.resource_estimates.molecule import (factorized_ccsd_t,
-                                                     cas_to_pyscf, pyscf_to_cas)
+import pytest
 
+from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES, sf
 
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
+    from pyscf import scf
+
+    from openfermion.resource_estimates.molecule import (cas_to_pyscf,
+                                                         factorized_ccsd_t,
+                                                         pyscf_to_cas)
+
+
+@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES,
+                    reason='pyscf and/or jax not installed.')
 def generate_costing_table(pyscf_mf,
                            name='molecule',
                            rank_range=range(50, 401, 25),
diff --git a/src/openfermion/resource_estimates/sparse/__init__.py b/src/openfermion/resource_estimates/sparse/__init__.py
index 0983968..4680861 100644
--- a/src/openfermion/resource_estimates/sparse/__init__.py
+++ b/src/openfermion/resource_estimates/sparse/__init__.py
@@ -1,4 +1,3 @@
-#coverage:ignore
 #   Copyright 2020 Google LLC
 
 #   Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/src/openfermion/resource_estimates/sparse/costing_sparse.py b/src/openfermion/resource_estimates/sparse/costing_sparse.py
index 767640c..a33f2c2 100644
--- a/src/openfermion/resource_estimates/sparse/costing_sparse.py
+++ b/src/openfermion/resource_estimates/sparse/costing_sparse.py
@@ -1,4 +1,3 @@
-#coverage:ignore
 """ Determine costs for sparse decomposition in QC
     Note this is WIP
 """
diff --git a/src/openfermion/resource_estimates/thc/__init__.py b/src/openfermion/resource_estimates/thc/__init__.py
index da686a0..2dadc8d 100644
--- a/src/openfermion/resource_estimates/thc/__init__.py
+++ b/src/openfermion/resource_estimates/thc/__init__.py
@@ -12,14 +12,13 @@
 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
-import pytest
 
-try:
-    import jax, pybtas
-    from .spacetime import qubit_vs_toffoli
+from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES
+
+from .compute_cost_thc import compute_cost
+from .spacetime import qubit_vs_toffoli
+
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
     from .compute_lambda_thc import compute_lambda
-    from .compute_cost_thc import compute_cost
     from .factorize_thc import thc_via_cp3 as factorize
     from .generate_costing_table_thc import generate_costing_table
-except ImportError:
-    pytest.skip('Need jax and pybtas for THC', allow_module_level=True)
diff --git a/src/openfermion/resource_estimates/thc/factorize_thc.py b/src/openfermion/resource_estimates/thc/factorize_thc.py
index 0b2a2f8..3c2ac47 100644
--- a/src/openfermion/resource_estimates/thc/factorize_thc.py
+++ b/src/openfermion/resource_estimates/thc/factorize_thc.py
@@ -1,12 +1,11 @@
 #coverage:ignore
 """ THC rank reduction of ERIs """
-import sys
 import time
-import uuid
-import numpy as np
+
 import h5py
-from openfermion.resource_estimates.thc.utils import (lbfgsb_opt_thc_l2reg,
-                                                      adagrad_opt_thc)
+import numpy as np
+
+from openfermion.resource_estimates.thc.utils import lbfgsb_opt_thc_l2reg
 
 
 def thc_via_cp3(eri_full,
diff --git a/src/openfermion/resource_estimates/thc/generate_costing_table_thc.py b/src/openfermion/resource_estimates/thc/generate_costing_table_thc.py
index 1d78acf..ec82789 100644
--- a/src/openfermion/resource_estimates/thc/generate_costing_table_thc.py
+++ b/src/openfermion/resource_estimates/thc/generate_costing_table_thc.py
@@ -1,10 +1,16 @@
 #coverage:ignore
 """ Pretty-print a table comparing number of THC vectors vs accy and cost """
 import numpy as np
-from pyscf import scf
-from openfermion.resource_estimates import thc
-from openfermion.resource_estimates.molecule import (factorized_ccsd_t,
-                                                     cas_to_pyscf, pyscf_to_cas)
+
+from openfermion.resource_estimates import (HAVE_DEPS_FOR_RESOURCE_ESTIMATES,
+                                            thc)
+
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
+    from pyscf import scf
+
+    from openfermion.resource_estimates.molecule import (cas_to_pyscf,
+                                                         factorized_ccsd_t,
+                                                         pyscf_to_cas)
 
 
 def generate_costing_table(pyscf_mf,
diff --git a/src/openfermion/resource_estimates/thc/utils/__init__.py b/src/openfermion/resource_estimates/thc/utils/__init__.py
index 26b33a8..f296201 100644
--- a/src/openfermion/resource_estimates/thc/utils/__init__.py
+++ b/src/openfermion/resource_estimates/thc/utils/__init__.py
@@ -13,11 +13,15 @@
 #   See the License for the specific language governing permissions and
 #   limitations under the License.
 
-from .thc_factorization import (lbfgsb_opt_thc, lbfgsb_opt_thc_l2reg,
-                                adagrad_opt_thc, lbfgsb_opt_cholesky)
-from .thc_objectives import (thc_objective_jax, thc_objective_grad_jax,
-                             thc_objective, thc_objective_regularized,
-                             thc_objective_grad, thc_objective_and_grad,
-                             cp_ls_cholesky_factor_objective)
-from .adagrad import (adagrad, constant, exponential_decay, inverse_time_decay,
-                      polynomial_decay, piecewise_constant, make_schedule)
+from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES
+
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
+    from .adagrad import (adagrad, constant, exponential_decay,
+                          inverse_time_decay, make_schedule, piecewise_constant,
+                          polynomial_decay)
+    from .thc_factorization import (adagrad_opt_thc, lbfgsb_opt_cholesky,
+                                    lbfgsb_opt_thc, lbfgsb_opt_thc_l2reg)
+    from .thc_objectives import (cp_ls_cholesky_factor_objective, thc_objective,
+                                 thc_objective_and_grad, thc_objective_grad,
+                                 thc_objective_grad_jax, thc_objective_jax,
+                                 thc_objective_regularized)
\ No newline at end of file
diff --git a/src/openfermion/resource_estimates/utils.py b/src/openfermion/resource_estimates/utils.py
index 3deec27..3630788 100644
--- a/src/openfermion/resource_estimates/utils.py
+++ b/src/openfermion/resource_estimates/utils.py
@@ -23,10 +23,9 @@ def QR(L: int, M1: int) -> Tuple[int, int]:
     try:
         assert k >= 0
     except AssertionError:
-        sys.exit("In function QR: \
-        \n  L is smaller than M: increase RANK or lower THRESH \
-        (or alternatively decrease CHI)")
-    value = lambda k: L / np.power(2, k) + M1 * (np.power(2, k) - 1)
+        sys.exit("In function QR: L is smaller than M: increase RANK or lower "
+                 "THRESH (or alternatively decrease CHI)")
+    value = lambda k: L / np.power(2.0, k) + M1 * (np.power(2.0, k) - 1)
     k_int = [np.floor(k), np.ceil(k)]  # restrict optimal k to integers
     k_opt = k_int[np.argmin(value(k_int))]  # obtain optimal k
     val_opt = np.ceil(value(k_opt))  # obtain ceiling of optimal value given k
@@ -56,9 +55,9 @@ def QR2(L1: int, L2: int, M1: int) -> Tuple[int, int, int]:
     # Biggest concern is if k1 / k2 range is not large enough!
     for k1 in range(1, 17):
         for k2 in range(1, 17):
-            value = np.ceil(L1 / np.power(2, k1)) * np.ceil(L2 / \
-                np.power(2, k2)) +\
-                M1 * (np.power(2, k1 + k2) - 1)
+            value = np.ceil(L1 / np.power(2.0, k1)) * np.ceil(L2 / \
+                np.power(2.0, k2)) +\
+                M1 * (np.power(2.0, k1 + k2) - 1)
             if value < val_opt:
                 val_opt = value
                 k1_opt = k1
@@ -80,7 +79,7 @@ def QI(L: int) -> Tuple[int, int]:
     """
     k = 0.5 * np.log2(L)
     assert k >= 0
-    value = lambda k: L / np.power(2, k) + np.power(2, k)
+    value = lambda k: L / np.power(2.0, k) + np.power(2.0, k)
     k_int = [np.floor(k), np.ceil(k)]  # restrict optimal k to integers
     k_opt = k_int[np.argmin(value(k_int))]  # obtain optimal k
     val_opt = np.ceil(value(k_opt))  # obtain ceiling of optimal value given k
@@ -111,9 +110,9 @@ def QI2(L1: int, L2: int) -> Tuple[int, int, int]:
     # Biggest concern is if k1 / k2 range is not large enough!
     for k1 in range(1, 17):
         for k2 in range(1, 17):
-            value = np.ceil(L1 / np.power(2, k1)) * np.ceil(L2 / \
-                np.power(2, k2)) +\
-                np.power(2, k1 + k2)
+            value = np.ceil(L1 / np.power(2.0, k1)) * np.ceil(L2 / \
+                np.power(2.0, k2)) +\
+                np.power(2.0, k1 + k2)
             if value < val_opt:
                 val_opt = value
                 k1_opt = k1


Test patch:
diff --git a/check/pytest b/check/pytest
index 5e1af16..dc21dd5 100755
--- a/check/pytest
+++ b/check/pytest
@@ -17,9 +17,11 @@
 cd "$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
 cd "$(git rev-parse --show-toplevel)"
 
+rootdir=$(pwd)
+
 PYTEST_ARGS=()
 ACTUALLY_QUIET=""
-for arg in $@; do
+for arg in "$@"; do
     if [[ "${arg}" == "--actually-quiet" ]]; then
         ACTUALLY_QUIET=1
     else
@@ -28,10 +30,10 @@ for arg in $@; do
 done
 
 if [ -z "${ACTUALLY_QUIET}" ]; then
-    pytest "${PYTEST_ARGS[@]}"
+    pytest -c dev_tools/conf/pytest.ini --rootdir=$rootdir "${PYTEST_ARGS[@]}"
 else
     # Filter out lines like "...F....x...      [ 42%]", with coloring.
-    pytest -q --color=yes "${PYTEST_ARGS[@]}" |
+    pytest -c dev_tools/conf/pytest.ini --rootdir=$rootdir -q --color=yes "${PYTEST_ARGS[@]}" |
         grep -Pv '^(.\[0m)?[\.FEsx]+(.\[36m)?\s+\[\s*\d+%\](.\[0m)?$'
     exit "${PIPESTATUS[0]}"
 fi
diff --git a/dev_tools/conf/pytest.ini b/dev_tools/conf/pytest.ini
new file mode 100644
index 0000000..7617853
--- /dev/null
+++ b/dev_tools/conf/pytest.ini
@@ -0,0 +1,3 @@
+[pytest]
+markers =
+    slow: marks tests as slow (deselect with '-m "not slow"')
diff --git a/src/openfermion/resource_estimates/df/compute_cost_df_test.py b/src/openfermion/resource_estimates/df/compute_cost_df_test.py
index 762fa17..6632a14 100644
--- a/src/openfermion/resource_estimates/df/compute_cost_df_test.py
+++ b/src/openfermion/resource_estimates/df/compute_cost_df_test.py
@@ -1,9 +1,16 @@
 #coverage:ignore
 """Test cases for costing_df.py
 """
-from openfermion.resource_estimates import df
+import pytest
 
+from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES
 
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
+    from openfermion.resource_estimates import df
+
+
+@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES,
+                    reason='pyscf and/or jax not installed.')
 def test_reiher_df():
     """ Reproduce Reiher et al orbital DF FT costs from paper """
     DE = 0.001
@@ -25,6 +32,8 @@ def test_reiher_df():
     assert output == (21753, 10073183463, 3725)
 
 
+@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES,
+                    reason='pyscf and/or jax not installed.')
 def test_li_df():
     """ Reproduce Li et al orbital DF FT costs from paper """
     DE = 0.001
diff --git a/src/openfermion/resource_estimates/df/compute_lambda_df_test.py b/src/openfermion/resource_estimates/df/compute_lambda_df_test.py
index 7203b82..c5a1403 100644
--- a/src/openfermion/resource_estimates/df/compute_lambda_df_test.py
+++ b/src/openfermion/resource_estimates/df/compute_lambda_df_test.py
@@ -2,11 +2,18 @@
 """Test cases for compute_lambda_df.py
 """
 from os import path
+
 import numpy as np
-from openfermion.resource_estimates import df
-from openfermion.resource_estimates.molecule import load_casfile_to_pyscf
+import pytest
+
+from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES, df
+
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
+    from openfermion.resource_estimates.molecule import load_casfile_to_pyscf
 
 
+@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES,
+                    reason="pyscf and/or jax not installed.")
 def test_reiher_df_lambda():
     """ Reproduce Reiher et al orbital DF lambda from paper """
 
diff --git a/src/openfermion/resource_estimates/molecule/pyscf_utils_test.py b/src/openfermion/resource_estimates/molecule/pyscf_utils_test.py
index 27a50f6..c092efe 100644
--- a/src/openfermion/resource_estimates/molecule/pyscf_utils_test.py
+++ b/src/openfermion/resource_estimates/molecule/pyscf_utils_test.py
@@ -1,29 +1,26 @@
 #coverage:ignore
 """Test cases for pyscf_utils.py
 """
-from os import path
 import unittest
-import pytest
+from os import path
+
 import numpy as np
+import pytest
+
+from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES
 
-try:
-    from pyscf import gto, scf, cc
-    HAS_PYSCF = True
-except ModuleNotFoundError:
-    # resource_estimates depend on pyscf, which may not be installed
-    HAS_PYSCF = False
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
+    from pyscf import cc, gto, scf
 
-if HAS_PYSCF:
-    from openfermion.resource_estimates import sf, df
-    from openfermion.resource_estimates.utils import QR, QI, QR2, QI2, power_two
-    from openfermion.resource_estimates.molecule import (load_casfile_to_pyscf,
-                                                         pyscf_to_cas, ccsd_t,
-                                                         stability,
-                                                         factorized_ccsd_t,
-                                                         open_shell_t1_d1)
+    from openfermion.resource_estimates import df, sf
+    from openfermion.resource_estimates.molecule import (
+        ccsd_t, factorized_ccsd_t, load_casfile_to_pyscf, open_shell_t1_d1,
+        pyscf_to_cas, stability)
 
 
-@pytest.mark.skipif(not HAS_PYSCF, reason='Not detecting `pyscf`.')
+@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES,
+                    reason='pyscf and/or jax not installed.')
+@pytest.mark.slow
 class OpenFermionPyscfUtilsTest(unittest.TestCase):
 
     def test_full_ccsd_t(self):
diff --git a/src/openfermion/resource_estimates/sf/compute_cost_sf_test.py b/src/openfermion/resource_estimates/sf/compute_cost_sf_test.py
index ebd3551..340bcde 100644
--- a/src/openfermion/resource_estimates/sf/compute_cost_sf_test.py
+++ b/src/openfermion/resource_estimates/sf/compute_cost_sf_test.py
@@ -1,9 +1,16 @@
 #coverage:ignore
 """Test cases for costing_sf.py
 """
-from openfermion.resource_estimates import sf
+import pytest
 
+from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES
 
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
+    from openfermion.resource_estimates import sf
+
+
+@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES,
+                    reason='pyscf and/or jax not installed.')
 def test_reiher_sf():
     """ Reproduce Reiher et al orbital SF FT costs from paper """
     DE = 0.001
@@ -23,6 +30,8 @@ def test_reiher_sf():
     assert output == (14184, 94868988984, 3320)
 
 
+@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES,
+                    reason='pyscf and/or jax not installed.')
 def test_li_sf():
     """ Reproduce Li et al orbital SF FT costs from paper """
     DE = 0.001
diff --git a/src/openfermion/resource_estimates/sf/compute_lambda_sf_test.py b/src/openfermion/resource_estimates/sf/compute_lambda_sf_test.py
index 6b90923..612ba8b 100644
--- a/src/openfermion/resource_estimates/sf/compute_lambda_sf_test.py
+++ b/src/openfermion/resource_estimates/sf/compute_lambda_sf_test.py
@@ -2,11 +2,18 @@
 """Test cases for compute_lambda_sf.py
 """
 from os import path
+
 import numpy as np
-from openfermion.resource_estimates import sf
-from openfermion.resource_estimates.molecule import load_casfile_to_pyscf
+import pytest
+
+from openfermion.resource_estimates import HAVE_DEPS_FOR_RESOURCE_ESTIMATES, sf
+
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
+    from openfermion.resource_estimates.molecule import load_casfile_to_pyscf
 
 
+@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES,
+                    reason='pyscf and/or jax not installed.')
 def test_reiher_sf_lambda():
     """ Reproduce Reiher et al orbital SF lambda from paper """
 
diff --git a/src/openfermion/resource_estimates/sparse/costing_sparse_test.py b/src/openfermion/resource_estimates/sparse/costing_sparse_test.py
index 33c2adf..0de9ad1 100644
--- a/src/openfermion/resource_estimates/sparse/costing_sparse_test.py
+++ b/src/openfermion/resource_estimates/sparse/costing_sparse_test.py
@@ -1,4 +1,3 @@
-#coverage:ignore
 """Test cases for costing_sparse.py
 """
 from openfermion.resource_estimates.sparse.costing_sparse import cost_sparse
diff --git a/src/openfermion/resource_estimates/thc/compute_cost_thc_test.py b/src/openfermion/resource_estimates/thc/compute_cost_thc_test.py
index 111ab4d..c2f4943 100644
--- a/src/openfermion/resource_estimates/thc/compute_cost_thc_test.py
+++ b/src/openfermion/resource_estimates/thc/compute_cost_thc_test.py
@@ -2,6 +2,7 @@
 """Test cases for costing_thc.py
 """
 import unittest
+
 import pytest
 
 from openfermion.resource_estimates import thc
diff --git a/src/openfermion/resource_estimates/thc/compute_lambda_thc_test.py b/src/openfermion/resource_estimates/thc/compute_lambda_thc_test.py
index e43ee03..5c7427d 100644
--- a/src/openfermion/resource_estimates/thc/compute_lambda_thc_test.py
+++ b/src/openfermion/resource_estimates/thc/compute_lambda_thc_test.py
@@ -1,12 +1,20 @@
 #coverage:ignore
 import os
+
 import h5py
 import numpy as np
+import pytest
+
 import openfermion.resource_estimates.integrals as int_folder
-from openfermion.resource_estimates import thc
-from openfermion.resource_estimates.molecule import load_casfile_to_pyscf
+from openfermion.resource_estimates import (HAVE_DEPS_FOR_RESOURCE_ESTIMATES,
+                                            thc)
+
+if HAVE_DEPS_FOR_RESOURCE_ESTIMATES:
+    from openfermion.resource_estimates.molecule import load_casfile_to_pyscf
 
 
+@pytest.mark.skipif(not HAVE_DEPS_FOR_RESOURCE_ESTIMATES,
+                    reason='pyscf and/or jax not installed.')
 def test_lambda():
     integral_path = int_folder.__file__.replace('__init__.py', '')
     thc_factor_file = os.path.join(integral_path, 'M_250_beta_16_eta_10.h5')