ffsim 0.0.78__tar.gz → 0.0.79__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. {ffsim-0.0.78 → ffsim-0.0.79}/CONTRIBUTING.md +3 -8
  2. {ffsim-0.0.78 → ffsim-0.0.79}/PKG-INFO +3 -3
  3. {ffsim-0.0.78 → ffsim-0.0.79}/README.md +2 -2
  4. {ffsim-0.0.78 → ffsim-0.0.79}/docs/_templates/autosummary/class.rst +4 -0
  5. {ffsim-0.0.78 → ffsim-0.0.79}/docs/conf.py +0 -1
  6. {ffsim-0.0.78 → ffsim-0.0.79}/docs/install.md +6 -0
  7. {ffsim-0.0.78 → ffsim-0.0.79}/pyproject.toml +26 -25
  8. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/linalg/__init__.py +1 -6
  9. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/linalg/givens.py +0 -44
  10. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/gates/diag_coulomb_trotter_split_op.py +12 -2
  11. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/gates/double_factorized_trotter.py +12 -2
  12. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/gates/ucj.py +47 -18
  13. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/trotter/qdrift.py +6 -8
  14. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/variational/ucj_spin_unbalanced.py +2 -0
  15. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/variational/ucj_spinless.py +2 -0
  16. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/_slow/contract/diag_coulomb_test.py +14 -14
  17. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/_slow/contract/num_op_sum_test.py +6 -5
  18. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/_slow/gates/diag_coulomb_test.py +17 -18
  19. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/_slow/gates/num_op_sum_test.py +6 -5
  20. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/_slow/gates/orbital_rotation_test.py +7 -6
  21. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/contract/diag_coulomb_test.py +15 -18
  22. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/contract/num_op_sum_test.py +12 -12
  23. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/gates/basic_gates_test.py +29 -43
  24. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/gates/diag_coulomb_test.py +22 -25
  25. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/gates/num_op_sum_test.py +9 -10
  26. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/gates/orbital_rotation_test.py +22 -28
  27. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/hamiltonians/single_factorized_hamiltonian_test.py +13 -19
  28. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/linalg/givens_test.py +6 -8
  29. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/linalg/linalg_test.py +5 -5
  30. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/operators/fermion_operator_test.py +6 -7
  31. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/optimize/linear_method_test.py +4 -3
  32. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/gates/diag_coulomb_test.py +20 -21
  33. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/gates/diag_coulomb_trotter_split_op_test.py +23 -3
  34. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/gates/double_factorized_trotter_test.py +26 -3
  35. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/gates/givens_ansatz_test.py +10 -10
  36. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/gates/num_num_ansatz_test.py +5 -4
  37. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/gates/num_op_sum_test.py +19 -20
  38. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/gates/orbital_rotation_test.py +21 -26
  39. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/gates/slater_determinant_test.py +7 -9
  40. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/gates/ucj_test.py +51 -9
  41. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/jordan_wigner_test.py +26 -12
  42. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/sampler_test.py +50 -64
  43. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/sim_test.py +62 -70
  44. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/transpiler_passes/merge_orbital_rotations_test.py +25 -41
  45. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/util_test.py +3 -2
  46. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/random_test.py +20 -20
  47. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/states/bitstring_test.py +4 -4
  48. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/states/sample_slater_test.py +13 -15
  49. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/states/slater_test.py +19 -33
  50. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/states/wick_test.py +8 -10
  51. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/testing_test.py +4 -3
  52. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/trotter/diag_coulomb_split_op_test.py +8 -10
  53. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/trotter/double_factorized_test.py +6 -6
  54. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/trotter/qdrift_test.py +58 -42
  55. {ffsim-0.0.78 → ffsim-0.0.79}/uv.lock +13 -9
  56. {ffsim-0.0.78 → ffsim-0.0.79}/.cargo/config.toml +0 -0
  57. {ffsim-0.0.78 → ffsim-0.0.79}/.dockerignore +0 -0
  58. {ffsim-0.0.78 → ffsim-0.0.79}/.github/workflows/build-and-release.yml +0 -0
  59. {ffsim-0.0.78 → ffsim-0.0.79}/.github/workflows/check.yml +0 -0
  60. {ffsim-0.0.78 → ffsim-0.0.79}/.github/workflows/docker.yml +0 -0
  61. {ffsim-0.0.78 → ffsim-0.0.79}/.github/workflows/docs-dev.yml +0 -0
  62. {ffsim-0.0.78 → ffsim-0.0.79}/.github/workflows/docs-release.yml +0 -0
  63. {ffsim-0.0.78 → ffsim-0.0.79}/.github/workflows/test-latest-versions.yml +0 -0
  64. {ffsim-0.0.78 → ffsim-0.0.79}/.github/workflows/test-minimum-versions.yml +0 -0
  65. {ffsim-0.0.78 → ffsim-0.0.79}/.gitignore +0 -0
  66. {ffsim-0.0.78 → ffsim-0.0.79}/.pre-commit-config.yaml +0 -0
  67. {ffsim-0.0.78 → ffsim-0.0.79}/Cargo.lock +0 -0
  68. {ffsim-0.0.78 → ffsim-0.0.79}/Cargo.toml +0 -0
  69. {ffsim-0.0.78 → ffsim-0.0.79}/Dockerfile +0 -0
  70. {ffsim-0.0.78 → ffsim-0.0.79}/LICENSE +0 -0
  71. {ffsim-0.0.78 → ffsim-0.0.79}/compose.yaml +0 -0
  72. {ffsim-0.0.78 → ffsim-0.0.79}/docs/_static/logo-dark.svg +0 -0
  73. {ffsim-0.0.78 → ffsim-0.0.79}/docs/_static/logo.svg +0 -0
  74. {ffsim-0.0.78 → ffsim-0.0.79}/docs/explanations/diag-coulomb-hamiltonian.ipynb +0 -0
  75. {ffsim-0.0.78 → ffsim-0.0.79}/docs/explanations/double-factorized.ipynb +0 -0
  76. {ffsim-0.0.78 → ffsim-0.0.79}/docs/explanations/fermion-operator.ipynb +0 -0
  77. {ffsim-0.0.78 → ffsim-0.0.79}/docs/explanations/hamiltonians.ipynb +0 -0
  78. {ffsim-0.0.78 → ffsim-0.0.79}/docs/explanations/index.md +0 -0
  79. {ffsim-0.0.78 → ffsim-0.0.79}/docs/explanations/lucj.ipynb +0 -0
  80. {ffsim-0.0.78 → ffsim-0.0.79}/docs/explanations/orbital-rotation.ipynb +0 -0
  81. {ffsim-0.0.78 → ffsim-0.0.79}/docs/explanations/protocols.ipynb +0 -0
  82. {ffsim-0.0.78 → ffsim-0.0.79}/docs/explanations/qubit-gate-decompositions.ipynb +0 -0
  83. {ffsim-0.0.78 → ffsim-0.0.79}/docs/explanations/state-vectors-and-gates.ipynb +0 -0
  84. {ffsim-0.0.78 → ffsim-0.0.79}/docs/explanations/variational-ansatzes.ipynb +0 -0
  85. {ffsim-0.0.78 → ffsim-0.0.79}/docs/generate_api_docs.py +0 -0
  86. {ffsim-0.0.78 → ffsim-0.0.79}/docs/how-to-guides/compute-expectation-values.ipynb +0 -0
  87. {ffsim-0.0.78 → ffsim-0.0.79}/docs/how-to-guides/entanglement-forging.ipynb +0 -0
  88. {ffsim-0.0.78 → ffsim-0.0.79}/docs/how-to-guides/index.md +0 -0
  89. {ffsim-0.0.78 → ffsim-0.0.79}/docs/how-to-guides/qiskit-circuits-sim.ipynb +0 -0
  90. {ffsim-0.0.78 → ffsim-0.0.79}/docs/how-to-guides/qiskit-lucj.ipynb +0 -0
  91. {ffsim-0.0.78 → ffsim-0.0.79}/docs/how-to-guides/qiskit-merge-orbital-rotations.ipynb +0 -0
  92. {ffsim-0.0.78 → ffsim-0.0.79}/docs/how-to-guides/qiskit-trotter.ipynb +0 -0
  93. {ffsim-0.0.78 → ffsim-0.0.79}/docs/how-to-guides/sample-from-state-vectors.ipynb +0 -0
  94. {ffsim-0.0.78 → ffsim-0.0.79}/docs/how-to-guides/sample-slater.ipynb +0 -0
  95. {ffsim-0.0.78 → ffsim-0.0.79}/docs/how-to-guides/simulate-trotter-hubbard.ipynb +0 -0
  96. {ffsim-0.0.78 → ffsim-0.0.79}/docs/how-to-guides/simulate-trotter-mol-ham.ipynb +0 -0
  97. {ffsim-0.0.78 → ffsim-0.0.79}/docs/how-to-guides/simulate-vqe.ipynb +0 -0
  98. {ffsim-0.0.78 → ffsim-0.0.79}/docs/index.md +0 -0
  99. {ffsim-0.0.78 → ffsim-0.0.79}/docs/tutorials/double-factorized-trotter.ipynb +0 -0
  100. {ffsim-0.0.78 → ffsim-0.0.79}/docs/tutorials/index.md +0 -0
  101. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/__init__.py +0 -0
  102. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/_cistring.py +0 -0
  103. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/_lib.pyi +0 -0
  104. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/_slow/__init__.py +0 -0
  105. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/_slow/contract/__init__.py +0 -0
  106. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/_slow/contract/diag_coulomb.py +0 -0
  107. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/_slow/contract/num_op_sum.py +0 -0
  108. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/_slow/fermion_operator.py +0 -0
  109. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/_slow/gates/__init__.py +0 -0
  110. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/_slow/gates/diag_coulomb.py +0 -0
  111. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/_slow/gates/num_op_sum.py +0 -0
  112. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/_slow/gates/orbital_rotation.py +0 -0
  113. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/contract/__init__.py +0 -0
  114. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/contract/diag_coulomb.py +0 -0
  115. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/contract/num_op_sum.py +0 -0
  116. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/contract/one_body.py +0 -0
  117. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/contract/two_body.py +0 -0
  118. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/gates/__init__.py +0 -0
  119. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/gates/basic_gates.py +0 -0
  120. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/gates/diag_coulomb.py +0 -0
  121. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/gates/num_op_sum.py +0 -0
  122. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/gates/orbital_rotation.py +0 -0
  123. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/gates/quad_ham.py +0 -0
  124. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/hamiltonians/__init__.py +0 -0
  125. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/hamiltonians/diagonal_coulomb_hamiltonian.py +0 -0
  126. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/hamiltonians/double_factorized_hamiltonian.py +0 -0
  127. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/hamiltonians/molecular_hamiltonian.py +0 -0
  128. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/hamiltonians/single_factorized_hamiltonian.py +0 -0
  129. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/init_cache.py +0 -0
  130. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/linalg/double_factorized_decomposition.py +0 -0
  131. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/linalg/linalg.py +0 -0
  132. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/linalg/predicates.py +0 -0
  133. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/linalg/util.py +0 -0
  134. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/molecular_data.py +0 -0
  135. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/operators/__init__.py +0 -0
  136. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/operators/common_operators.py +0 -0
  137. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/operators/coupled_cluster.py +0 -0
  138. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/operators/fermi_hubbard.py +0 -0
  139. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/operators/fermion_action.py +0 -0
  140. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/operators/fermion_operator.py +0 -0
  141. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/optimize/__init__.py +0 -0
  142. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/optimize/_util.py +0 -0
  143. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/optimize/linear_method.py +0 -0
  144. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/protocols/__init__.py +0 -0
  145. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/protocols/apply_unitary_protocol.py +0 -0
  146. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/protocols/approximate_equality_protocol.py +0 -0
  147. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/protocols/diagonal_protocol.py +0 -0
  148. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/protocols/fermion_operator_protocol.py +0 -0
  149. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/protocols/linear_operator_protocol.py +0 -0
  150. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/protocols/trace_protocol.py +0 -0
  151. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/py.typed +0 -0
  152. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/__init__.py +0 -0
  153. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/gates/__init__.py +0 -0
  154. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/gates/diag_coulomb.py +0 -0
  155. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/gates/givens_ansatz.py +0 -0
  156. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/gates/num_num_ansatz.py +0 -0
  157. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/gates/num_op_sum.py +0 -0
  158. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/gates/orbital_rotation.py +0 -0
  159. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/gates/slater_determinant.py +0 -0
  160. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/jordan_wigner.py +0 -0
  161. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/lucj_pass_manager.py +0 -0
  162. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/sampler.py +0 -0
  163. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/sim.py +0 -0
  164. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/transpiler_passes/__init__.py +0 -0
  165. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/transpiler_passes/drop_negligible.py +0 -0
  166. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/transpiler_passes/merge_orbital_rotations.py +0 -0
  167. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/transpiler_stages.py +0 -0
  168. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/qiskit/util.py +0 -0
  169. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/random/__init__.py +0 -0
  170. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/random/random.py +0 -0
  171. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/states/__init__.py +0 -0
  172. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/states/bitstring.py +0 -0
  173. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/states/dimensions.py +0 -0
  174. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/states/product_state_sum.py +0 -0
  175. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/states/rdm.py +0 -0
  176. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/states/sample_slater.py +0 -0
  177. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/states/slater.py +0 -0
  178. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/states/spin.py +0 -0
  179. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/states/states.py +0 -0
  180. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/states/wick.py +0 -0
  181. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/testing/__init__.py +0 -0
  182. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/testing/testing.py +0 -0
  183. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/trotter/__init__.py +0 -0
  184. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/trotter/_util.py +0 -0
  185. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/trotter/diagonal_coulomb_split_op.py +0 -0
  186. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/trotter/double_factorized.py +0 -0
  187. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/variational/__init__.py +0 -0
  188. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/variational/givens.py +0 -0
  189. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/variational/hopgate.py +0 -0
  190. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/variational/multireference.py +0 -0
  191. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/variational/num_num.py +0 -0
  192. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/variational/orbital_optimization.py +0 -0
  193. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/variational/uccsd.py +0 -0
  194. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/variational/ucj_angles_spin_balanced.py +0 -0
  195. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/variational/ucj_spin_balanced.py +0 -0
  196. {ffsim-0.0.78 → ffsim-0.0.79}/python/ffsim/variational/util.py +0 -0
  197. {ffsim-0.0.78 → ffsim-0.0.79}/src/contract/diag_coulomb.rs +0 -0
  198. {ffsim-0.0.78 → ffsim-0.0.79}/src/contract/mod.rs +0 -0
  199. {ffsim-0.0.78 → ffsim-0.0.79}/src/contract/num_op_sum.rs +0 -0
  200. {ffsim-0.0.78 → ffsim-0.0.79}/src/fermion_operator.rs +0 -0
  201. {ffsim-0.0.78 → ffsim-0.0.79}/src/gates/diag_coulomb.rs +0 -0
  202. {ffsim-0.0.78 → ffsim-0.0.79}/src/gates/mod.rs +0 -0
  203. {ffsim-0.0.78 → ffsim-0.0.79}/src/gates/num_op_sum.rs +0 -0
  204. {ffsim-0.0.78 → ffsim-0.0.79}/src/gates/orbital_rotation.rs +0 -0
  205. {ffsim-0.0.78 → ffsim-0.0.79}/src/gates/phase_shift.rs +0 -0
  206. {ffsim-0.0.78 → ffsim-0.0.79}/src/jordan_wigner.rs +0 -0
  207. {ffsim-0.0.78 → ffsim-0.0.79}/src/lib.rs +0 -0
  208. {ffsim-0.0.78 → ffsim-0.0.79}/src/linalg/givens.rs +0 -0
  209. {ffsim-0.0.78 → ffsim-0.0.79}/src/linalg/mod.rs +0 -0
  210. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/__init__.py +0 -0
  211. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/_slow/__init__.py +0 -0
  212. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/_slow/contract/__init__.py +0 -0
  213. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/_slow/gates/__init__.py +0 -0
  214. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/contract/__init__.py +0 -0
  215. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/contract/two_body_test.py +0 -0
  216. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/gates/__init__.py +0 -0
  217. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/gates/quad_ham_test.py +0 -0
  218. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/hamiltonians/__init__.py +0 -0
  219. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/hamiltonians/diagonal_coulomb_hamiltonian_test.py +0 -0
  220. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/hamiltonians/double_factorized_hamiltonian_test.py +0 -0
  221. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/hamiltonians/molecular_hamiltonian_test.py +0 -0
  222. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/linalg/__init__.py +0 -0
  223. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/linalg/double_factorized_decomposition_test.py +0 -0
  224. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/linalg/util_test.py +0 -0
  225. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/molecular_data_test.py +0 -0
  226. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/operators/__init__.py +0 -0
  227. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/operators/common_operators_test.py +0 -0
  228. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/operators/coupled_cluster_test.py +0 -0
  229. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/operators/fermi_hubbard_test.py +0 -0
  230. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/operators/fermion_action_test.py +0 -0
  231. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/optimize/__init__.py +0 -0
  232. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/protocols/__init__.py +0 -0
  233. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/protocols/trace_protocol_test.py +0 -0
  234. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/__init__.py +0 -0
  235. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/gates/__init__.py +0 -0
  236. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/lucj_pass_manager_test.py +0 -0
  237. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/transpiler_passes/__init__.py +0 -0
  238. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/qiskit/transpiler_passes/drop_negligible_test.py +0 -0
  239. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/states/__init__.py +0 -0
  240. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/states/rdm_test.py +0 -0
  241. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/states/states_test.py +0 -0
  242. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/test_data/orbital_rotation-0.npy +0 -0
  243. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/test_no_warnings.py +0 -0
  244. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/trotter/__init__.py +0 -0
  245. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/variational/__init__.py +0 -0
  246. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/variational/givens_test.py +0 -0
  247. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/variational/hopgate_test.py +0 -0
  248. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/variational/multireference_test.py +0 -0
  249. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/variational/num_num_test.py +0 -0
  250. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/variational/orbital_optimization_test.py +0 -0
  251. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/variational/uccsd_test.py +0 -0
  252. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/variational/ucj_angles_spin_balanced_test.py +0 -0
  253. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/variational/ucj_spin_balanced_test.py +0 -0
  254. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/variational/ucj_spin_unbalanced_test.py +0 -0
  255. {ffsim-0.0.78 → ffsim-0.0.79}/tests/python/variational/ucj_spinless_test.py +0 -0
@@ -7,7 +7,7 @@ The instructions on this page won't work natively on Windows. For ffsim developm
7
7
  To set up ffsim for development, install it from source in editable mode along with the development requirements:
8
8
 
9
9
  ```bash
10
- pip install -e ".[dev]"
10
+ pip install -e . --group dev
11
11
  ```
12
12
 
13
13
  To install the git pre-commit hooks, run
@@ -57,10 +57,7 @@ substituting `<environment name>` with the name of the tox environment for the c
57
57
 
58
58
  ## Run code checks directly
59
59
 
60
- Running the code checks directly using the corresponding software tool can be useful and allows you to:
61
-
62
- - Automatically fix lint and formatting errors.
63
- - Build the documentation without deleting cached files.
60
+ Running the code checks directly using the corresponding software tool gives you more flexibility and allows you to automatically fix lint and formatting errors.
64
61
 
65
62
  ### Run tests
66
63
 
@@ -101,6 +98,4 @@ sphinx-build -b html -W docs/ docs/_build/html
101
98
 
102
99
  ## View locally built documentation
103
100
 
104
- After building the docs using either the [tox command](#run-code-checks-using-tox) or the [sphinx command](#build-documentation), open the file `docs/_build/html/index.html` in your web browser. For rapid iterations, the sphinx command is preferred because it retains cached files.
105
- Building the documentation can consume significant CPU because the tutorial notebooks are executed.
106
- The tox command deletes cached files so it will execute all the notebooks every time, while the sphinx command only executes notebooks if they were modified from the previous run.
101
+ After building the documentation, open the file `docs/_build/html/index.html` in your web browser.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ffsim
3
- Version: 0.0.78
3
+ Version: 0.0.79
4
4
  Classifier: License :: OSI Approved :: Apache Software License
5
5
  Classifier: Programming Language :: Python
6
6
  Classifier: Programming Language :: Python :: 3 :: Only
@@ -44,13 +44,13 @@ Documentation is located at the [project website](https://qiskit-community.githu
44
44
 
45
45
  <!-- start installation -->
46
46
 
47
- We recommend installing ffsim using pip when possible:
47
+ On Linux and macOS, you can install ffsim using pip:
48
48
 
49
49
  ```bash
50
50
  pip install ffsim
51
51
  ```
52
52
 
53
- This method won't work natively on Windows, however. Refer to the [installation instructions](https://qiskit-community.github.io/ffsim/install.html) for information about using ffsim on Windows, as well as instructions for installing from source and running ffsim in a container.
53
+ See the [installation instructions](https://qiskit-community.github.io/ffsim/install.html) for information about using ffsim on Windows, as well as instructions for installing from source and running ffsim in a container.
54
54
 
55
55
  <!-- end installation -->
56
56
 
@@ -14,13 +14,13 @@ Documentation is located at the [project website](https://qiskit-community.githu
14
14
 
15
15
  <!-- start installation -->
16
16
 
17
- We recommend installing ffsim using pip when possible:
17
+ On Linux and macOS, you can install ffsim using pip:
18
18
 
19
19
  ```bash
20
20
  pip install ffsim
21
21
  ```
22
22
 
23
- This method won't work natively on Windows, however. Refer to the [installation instructions](https://qiskit-community.github.io/ffsim/install.html) for information about using ffsim on Windows, as well as instructions for installing from source and running ffsim in a container.
23
+ See the [installation instructions](https://qiskit-community.github.io/ffsim/install.html) for information about using ffsim on Windows, as well as instructions for installing from source and running ffsim in a container.
24
24
 
25
25
  <!-- end installation -->
26
26
 
@@ -26,6 +26,10 @@
26
26
  ~{{ name }}.{{ item }}
27
27
  {%- endfor %}
28
28
  {% endif %}
29
+ {%- for item in methods if item not in inherited_members and item != '__init__' %}
30
+
31
+ .. automethod:: {{ item }}
32
+ {%- endfor %}
29
33
  {%- for item in ns.protocol_methods %}
30
34
 
31
35
  .. automethod:: {{ item }}
@@ -24,7 +24,6 @@ extensions = [
24
24
  "sphinx.ext.intersphinx",
25
25
  "sphinx.ext.mathjax",
26
26
  "sphinx.ext.viewcode",
27
- "sphinx.ext.extlinks",
28
27
  "sphinx_autodoc_typehints",
29
28
  "myst_parser",
30
29
  "nbsphinx",
@@ -15,6 +15,8 @@ ffsim is available on [PyPI](https://pypi.org/project/ffsim/). You can install i
15
15
  pip install ffsim
16
16
  ```
17
17
 
18
+ For improved performance on [x86](https://en.wikipedia.org/wiki/X86) systems, considering [installing from source](#install-from-source).
19
+
18
20
  ## Install from source
19
21
 
20
22
  You can use pip to install ffsim from source. For example:
@@ -25,6 +27,10 @@ cd ffsim
25
27
  pip install .
26
28
  ```
27
29
 
30
+ Installing from source may improve performance on x86 systems because the Rust extensions in the PyPI wheels are compiled with `-C target-cpu=x86-64`, which targets the baseline x86-64 instruction set for broad compatibility. When you build from source, ffsim is configured to compile its Rust extensions with `-C target-cpu=native`, so the Rust compiler can emit optimized instructions (e.g., AVX2, AVX-512) for your specific CPU.
31
+
32
+ Similarly, you can install [PySCF](https://pyscf.org/) from source with `-DBUILD_MARCH_NATIVE=ON` to enable CPU-specific optimizations in PySCF's C extensions. See [PySCF's installation instructions](https://pyscf.org/user/install.html#build-from-source) for details.
33
+
28
34
  ## Use within Docker
29
35
 
30
36
  We provide a [Dockerfile](https://github.com/qiskit-community/ffsim/blob/main/Dockerfile) and a [compose.yaml](https://github.com/qiskit-community/ffsim/blob/main/compose.yaml) file, which you can use to build a [Docker](https://www.docker.com/) image with just a few simple commands:
@@ -5,7 +5,7 @@ build-backend = "maturin"
5
5
  [project]
6
6
  name = "ffsim"
7
7
  requires-python = ">=3.10"
8
- version = "0.0.78"
8
+ version = "0.0.79"
9
9
  description = "Faster simulation of fermionic quantum circuits."
10
10
  readme = "README.md"
11
11
  license = { file = "LICENSE" }
@@ -45,17 +45,19 @@ dev = [
45
45
  "coverage",
46
46
  "maturin",
47
47
  "mypy",
48
+ "pre-commit",
49
+ "pytest",
50
+ "ruff",
51
+ "typos",
52
+ ]
53
+ docs = [
48
54
  "myst-parser",
49
55
  "nbmake",
50
56
  "nbsphinx",
51
- "pre-commit",
52
- "pytest",
53
57
  "qiskit[visualization]",
54
58
  "qiskit-sphinx-theme",
55
- "ruff",
56
59
  "sphinx",
57
60
  "sphinx-autodoc-typehints",
58
- "typos",
59
61
  ]
60
62
 
61
63
  [tool.maturin]
@@ -140,7 +142,7 @@ commands = [["pytest"]]
140
142
 
141
143
  [tool.tox.env.coverage]
142
144
  description = "check test coverage"
143
- dependency_groups = ["dev"]
145
+ deps = ["coverage", "pytest"]
144
146
  commands = [
145
147
  [
146
148
  "coverage",
@@ -157,48 +159,43 @@ commands = [
157
159
  ],
158
160
  ]
159
161
 
160
- [tool.tox.env.type]
161
- description = "run type check"
162
- dependency_groups = ["dev"]
163
- commands = [["mypy"]]
164
-
165
- [tool.tox.env.lint]
166
- description = "check for lint"
162
+ [tool.tox.env.format]
163
+ description = "check formatting"
167
164
  skip_install = true
168
165
  deps = ["ruff"]
169
166
  allowlist_externals = ["cargo"]
170
167
  commands = [
171
168
  [
172
169
  "ruff",
173
- "check",
170
+ "format",
171
+ "--check",
174
172
  ],
175
173
  [
176
174
  "cargo",
177
- "clippy",
175
+ "fmt",
178
176
  "--all",
179
177
  "--",
180
- "-D",
181
- "warnings",
178
+ "--check",
182
179
  ],
183
180
  ]
184
181
 
185
- [tool.tox.env.format]
186
- description = "check formatting"
182
+ [tool.tox.env.lint]
183
+ description = "check for lint"
187
184
  skip_install = true
188
185
  deps = ["ruff"]
189
186
  allowlist_externals = ["cargo"]
190
187
  commands = [
191
188
  [
192
189
  "ruff",
193
- "format",
194
- "--check",
190
+ "check",
195
191
  ],
196
192
  [
197
193
  "cargo",
198
- "fmt",
194
+ "clippy",
199
195
  "--all",
200
196
  "--",
201
- "--check",
197
+ "-D",
198
+ "warnings",
202
199
  ],
203
200
  ]
204
201
 
@@ -208,10 +205,14 @@ skip_install = true
208
205
  deps = ["typos"]
209
206
  commands = [["typos"]]
210
207
 
208
+ [tool.tox.env.type]
209
+ description = "run type check"
210
+ deps = ["mypy"]
211
+ commands = [["mypy"]]
212
+
211
213
  [tool.tox.env.docs]
212
214
  description = "build docs"
213
- dependency_groups = ["dev"]
214
- set_env = { SPHINX_APIDOC_OPTIONS = "members,show-inheritance" }
215
+ dependency_groups = ["docs"]
215
216
  commands = [
216
217
  [
217
218
  "python",
@@ -16,11 +16,7 @@ from ffsim.linalg.double_factorized_decomposition import (
16
16
  double_factorized_t2_alpha_beta,
17
17
  modified_cholesky,
18
18
  )
19
- from ffsim.linalg.givens import (
20
- GivensRotation,
21
- apply_matrix_to_slices,
22
- givens_decomposition,
23
- )
19
+ from ffsim.linalg.givens import apply_matrix_to_slices, givens_decomposition
24
20
  from ffsim.linalg.linalg import (
25
21
  expm_multiply_taylor,
26
22
  lup,
@@ -38,7 +34,6 @@ from ffsim.linalg.predicates import (
38
34
  )
39
35
 
40
36
  __all__ = [
41
- "GivensRotation",
42
37
  "apply_matrix_to_slices",
43
38
  "double_factorized",
44
39
  "double_factorized_t2",
@@ -12,37 +12,11 @@
12
12
 
13
13
  from __future__ import annotations
14
14
 
15
- import cmath
16
- from typing import NamedTuple
17
-
18
15
  import numpy as np
19
- from scipy.linalg.blas import zrotg as zrotg_
20
16
 
21
17
  from ffsim import _lib
22
18
 
23
19
 
24
- class GivensRotation(NamedTuple):
25
- r"""A Givens rotation.
26
-
27
- A Givens rotation acts on the two-dimensional subspace spanned by the :math:`i`-th
28
- and :math:`j`-th basis vectors as
29
-
30
- .. math::
31
-
32
- \begin{pmatrix}
33
- c & s \\
34
- -s^* & c \\
35
- \end{pmatrix}
36
-
37
- where :math:`c` is a real number and :math:`s` is a complex number.
38
- """
39
-
40
- c: float
41
- s: complex
42
- i: int
43
- j: int
44
-
45
-
46
20
  def apply_matrix_to_slices(
47
21
  target: np.ndarray,
48
22
  mat: np.ndarray,
@@ -73,24 +47,6 @@ def apply_matrix_to_slices(
73
47
  return out
74
48
 
75
49
 
76
- def zrotg(a: complex, b: complex, tol=1e-12) -> tuple[float, complex]:
77
- r"""Safe version of the zrotg BLAS function.
78
-
79
- The BLAS implementation of zrotg can return NaN values if either a or b is very
80
- close to zero. This function detects if either a or b is close to zero up to the
81
- specified tolerance, in which case it behaves as if it were exactly zero.
82
-
83
- Note that in contrast to ``scipy.linalg.blas.zrotg``, this function returns c as a
84
- float rather than a complex.
85
- """
86
- if cmath.isclose(b, 0.0, abs_tol=tol):
87
- return 1.0, 0j
88
- if cmath.isclose(a, 0.0, abs_tol=tol):
89
- return 0.0, 1 + 0j
90
- c, s = zrotg_(a, b)
91
- return c.real, s
92
-
93
-
94
50
  def givens_decomposition(
95
51
  mat: np.ndarray, tol: float = 1e-12
96
52
  ) -> tuple[list[tuple[float, complex, int, int]], np.ndarray]:
@@ -46,6 +46,7 @@ class SimulateTrotterDiagCoulombSplitOpJW(Gate):
46
46
  *,
47
47
  n_steps: int = 1,
48
48
  order: int = 0,
49
+ tol: float = 1e-12,
49
50
  label: str | None = None,
50
51
  ):
51
52
  r"""Create diagonal Coulomb split-operator Trotter evolution gate.
@@ -56,6 +57,8 @@ class SimulateTrotterDiagCoulombSplitOpJW(Gate):
56
57
  time: The evolution time.
57
58
  n_steps: The number of Trotter steps.
58
59
  order: The order of the Trotter decomposition.
60
+ tol: Tolerance for the Givens decomposition of the orbital rotations.
61
+ Matrix entries smaller than this value will be treated as equal to zero.
59
62
  label: The label of the gate.
60
63
  """
61
64
  if order < 0:
@@ -66,6 +69,7 @@ class SimulateTrotterDiagCoulombSplitOpJW(Gate):
66
69
  self.time = time
67
70
  self.n_steps = n_steps
68
71
  self.order = order
72
+ self.tol = tol
69
73
  super().__init__(
70
74
  "dc_trotter_split_op_jw", 2 * self.hamiltonian.norb, [], label=label
71
75
  )
@@ -80,6 +84,7 @@ class SimulateTrotterDiagCoulombSplitOpJW(Gate):
80
84
  time=self.time,
81
85
  n_steps=self.n_steps,
82
86
  order=self.order,
87
+ tol=self.tol,
83
88
  ),
84
89
  qubits=qubits,
85
90
  )
@@ -91,6 +96,7 @@ def _simulate_trotter_diag_coulomb_split_op(
91
96
  time: float,
92
97
  n_steps: int = 1,
93
98
  order: int = 0,
99
+ tol: float = 1e-12,
94
100
  ) -> Iterator[CircuitInstruction]:
95
101
  if n_steps == 0:
96
102
  return
@@ -106,10 +112,11 @@ def _simulate_trotter_diag_coulomb_split_op(
106
112
  step_time,
107
113
  norb=hamiltonian.norb,
108
114
  order=order,
115
+ tol=tol,
109
116
  )
110
117
  if not np.all(np.diagonal(current_basis) == 1):
111
118
  yield CircuitInstruction(
112
- OrbitalRotationJW(hamiltonian.norb, current_basis), qubits
119
+ OrbitalRotationJW(hamiltonian.norb, current_basis, tol=tol), qubits
113
120
  )
114
121
  yield CircuitInstruction(GlobalPhaseGate(-time * hamiltonian.constant), [])
115
122
 
@@ -122,6 +129,7 @@ def _simulate_trotter_step_diag_coulomb_split_op(
122
129
  time: float,
123
130
  norb: int,
124
131
  order: int,
132
+ tol: float = 1e-12,
125
133
  ) -> Generator[CircuitInstruction, None, np.ndarray]:
126
134
  diag_coulomb_aa, diag_coulomb_ab = diag_coulomb_mats
127
135
  eye = np.eye(norb)
@@ -131,7 +139,9 @@ def _simulate_trotter_step_diag_coulomb_split_op(
131
139
  scipy.linalg.expm(-1j * time * one_body_tensor) @ current_basis
132
140
  )
133
141
  else:
134
- yield CircuitInstruction(OrbitalRotationJW(norb, current_basis), qubits)
142
+ yield CircuitInstruction(
143
+ OrbitalRotationJW(norb, current_basis, tol=tol), qubits
144
+ )
135
145
  yield CircuitInstruction(
136
146
  DiagCoulombEvolutionJW(
137
147
  norb,
@@ -46,6 +46,7 @@ class SimulateTrotterDoubleFactorizedJW(Gate):
46
46
  *,
47
47
  n_steps: int = 1,
48
48
  order: int = 0,
49
+ tol: float = 1e-12,
49
50
  label: str | None = None,
50
51
  ):
51
52
  r"""Create double-factorized Trotter evolution gate.
@@ -56,6 +57,8 @@ class SimulateTrotterDoubleFactorizedJW(Gate):
56
57
  time: The evolution time.
57
58
  n_steps: The number of Trotter steps.
58
59
  order: The order of the Trotter decomposition.
60
+ tol: Tolerance for the Givens decomposition of the orbital rotations.
61
+ Matrix entries smaller than this value will be treated as equal to zero.
59
62
  label: The label of the gate.
60
63
  """
61
64
  if order < 0:
@@ -66,6 +69,7 @@ class SimulateTrotterDoubleFactorizedJW(Gate):
66
69
  self.time = time
67
70
  self.n_steps = n_steps
68
71
  self.order = order
72
+ self.tol = tol
69
73
  super().__init__("df_trotter_jw", 2 * self.hamiltonian.norb, [], label=label)
70
74
 
71
75
  def _define(self):
@@ -78,6 +82,7 @@ class SimulateTrotterDoubleFactorizedJW(Gate):
78
82
  time=self.time,
79
83
  n_steps=self.n_steps,
80
84
  order=self.order,
85
+ tol=self.tol,
81
86
  ),
82
87
  qubits=qubits,
83
88
  )
@@ -89,6 +94,7 @@ def _simulate_trotter_double_factorized(
89
94
  time: float,
90
95
  n_steps: int = 1,
91
96
  order: int = 0,
97
+ tol: float = 1e-12,
92
98
  ) -> Iterator[CircuitInstruction]:
93
99
  if n_steps == 0:
94
100
  return
@@ -106,10 +112,11 @@ def _simulate_trotter_double_factorized(
106
112
  norb=hamiltonian.norb,
107
113
  order=order,
108
114
  z_representation=hamiltonian.z_representation,
115
+ tol=tol,
109
116
  )
110
117
  if not np.all(np.diagonal(current_basis) == 1):
111
118
  yield CircuitInstruction(
112
- OrbitalRotationJW(hamiltonian.norb, current_basis), qubits
119
+ OrbitalRotationJW(hamiltonian.norb, current_basis, tol=tol), qubits
113
120
  )
114
121
  yield CircuitInstruction(GlobalPhaseGate(-time * hamiltonian.constant), [])
115
122
 
@@ -124,6 +131,7 @@ def _simulate_trotter_step_double_factorized(
124
131
  norb: int,
125
132
  order: int,
126
133
  z_representation: bool,
134
+ tol: float = 1e-12,
127
135
  ) -> Generator[CircuitInstruction, None, np.ndarray]:
128
136
  for term_index, time in simulate_trotter_step_iterator(
129
137
  1 + len(diag_coulomb_mats), time, order
@@ -135,7 +143,9 @@ def _simulate_trotter_step_double_factorized(
135
143
  else:
136
144
  orbital_rotation = orbital_rotations[term_index - 1]
137
145
  yield CircuitInstruction(
138
- OrbitalRotationJW(norb, orbital_rotation.T.conj() @ current_basis),
146
+ OrbitalRotationJW(
147
+ norb, orbital_rotation.T.conj() @ current_basis, tol=tol
148
+ ),
139
149
  qubits,
140
150
  )
141
151
  yield CircuitInstruction(
@@ -44,35 +44,42 @@ class UCJOpSpinBalancedJW(Gate):
44
44
  """
45
45
 
46
46
  def __init__(
47
- self, ucj_op: variational.UCJOpSpinBalanced, *, label: str | None = None
47
+ self,
48
+ ucj_op: variational.UCJOpSpinBalanced,
49
+ *,
50
+ tol: float = 1e-12,
51
+ label: str | None = None,
48
52
  ):
49
53
  """Create a new spin-balanced unitary cluster Jastrow (UCJ) gate.
50
54
 
51
55
  Args:
52
56
  ucj_op: The UCJ operator.
57
+ tol: Tolerance for the Givens decomposition of the orbital rotations.
58
+ Matrix entries smaller than this value will be treated as equal to zero.
53
59
  label: The label of the gate.
54
60
  """
55
61
  self.ucj_op = ucj_op
62
+ self.tol = tol
56
63
  super().__init__("ucj_balanced_jw", 2 * ucj_op.norb, [], label=label)
57
64
 
58
65
  def _define(self):
59
66
  """Gate decomposition."""
60
67
  qubits = QuantumRegister(self.num_qubits)
61
68
  self.definition = QuantumCircuit.from_instructions(
62
- _ucj_op_spin_balanced_jw(qubits, self.ucj_op),
69
+ _ucj_op_spin_balanced_jw(qubits, self.ucj_op, tol=self.tol),
63
70
  qubits=qubits,
64
71
  name=self.name,
65
72
  )
66
73
 
67
74
 
68
75
  def _ucj_op_spin_balanced_jw(
69
- qubits: Sequence[Qubit], ucj_op: variational.UCJOpSpinBalanced
76
+ qubits: Sequence[Qubit], ucj_op: variational.UCJOpSpinBalanced, tol: float = 1e-12
70
77
  ) -> Iterator[CircuitInstruction]:
71
78
  for (diag_coulomb_mat_aa, diag_coulomb_mat_ab), orbital_rotation in zip(
72
79
  ucj_op.diag_coulomb_mats, ucj_op.orbital_rotations
73
80
  ):
74
81
  yield CircuitInstruction(
75
- OrbitalRotationJW(ucj_op.norb, orbital_rotation.T.conj()),
82
+ OrbitalRotationJW(ucj_op.norb, orbital_rotation.T.conj(), tol=tol),
76
83
  qubits,
77
84
  )
78
85
  yield CircuitInstruction(
@@ -84,11 +91,12 @@ def _ucj_op_spin_balanced_jw(
84
91
  qubits,
85
92
  )
86
93
  yield CircuitInstruction(
87
- OrbitalRotationJW(ucj_op.norb, orbital_rotation), qubits
94
+ OrbitalRotationJW(ucj_op.norb, orbital_rotation, tol=tol), qubits
88
95
  )
89
96
  if ucj_op.final_orbital_rotation is not None:
90
97
  yield CircuitInstruction(
91
- OrbitalRotationJW(ucj_op.norb, ucj_op.final_orbital_rotation), qubits
98
+ OrbitalRotationJW(ucj_op.norb, ucj_op.final_orbital_rotation, tol=tol),
99
+ qubits,
92
100
  )
93
101
 
94
102
 
@@ -103,35 +111,44 @@ class UCJOpSpinUnbalancedJW(Gate):
103
111
  """
104
112
 
105
113
  def __init__(
106
- self, ucj_op: variational.UCJOpSpinUnbalanced, *, label: str | None = None
114
+ self,
115
+ ucj_op: variational.UCJOpSpinUnbalanced,
116
+ *,
117
+ tol: float = 1e-12,
118
+ label: str | None = None,
107
119
  ):
108
120
  """Create a new spin-unbalanced unitary cluster Jastrow (UCJ) gate.
109
121
 
110
122
  Args:
111
123
  ucj_op: The UCJ operator.
124
+ tol: Tolerance for the Givens decomposition of the orbital rotations.
125
+ Matrix entries smaller than this value will be treated as equal to zero.
112
126
  label: The label of the gate.
113
127
  """
114
128
  self.ucj_op = ucj_op
129
+ self.tol = tol
115
130
  super().__init__("ucj_unbalanced_jw", 2 * ucj_op.norb, [], label=label)
116
131
 
117
132
  def _define(self):
118
133
  """Gate decomposition."""
119
134
  qubits = QuantumRegister(self.num_qubits)
120
135
  self.definition = QuantumCircuit.from_instructions(
121
- _ucj_op_spin_unbalanced_jw(qubits, self.ucj_op),
136
+ _ucj_op_spin_unbalanced_jw(qubits, self.ucj_op, tol=self.tol),
122
137
  qubits=qubits,
123
138
  name=self.name,
124
139
  )
125
140
 
126
141
 
127
142
  def _ucj_op_spin_unbalanced_jw(
128
- qubits: Sequence[Qubit], ucj_op: variational.UCJOpSpinUnbalanced
143
+ qubits: Sequence[Qubit], ucj_op: variational.UCJOpSpinUnbalanced, tol: float = 1e-12
129
144
  ) -> Iterator[CircuitInstruction]:
130
145
  for diag_colomb_mat, orbital_rotation in zip(
131
146
  ucj_op.diag_coulomb_mats, ucj_op.orbital_rotations
132
147
  ):
133
148
  yield CircuitInstruction(
134
- OrbitalRotationJW(ucj_op.norb, orbital_rotation.transpose(0, 2, 1).conj()),
149
+ OrbitalRotationJW(
150
+ ucj_op.norb, orbital_rotation.transpose(0, 2, 1).conj(), tol=tol
151
+ ),
135
152
  qubits,
136
153
  )
137
154
  yield CircuitInstruction(
@@ -139,11 +156,12 @@ def _ucj_op_spin_unbalanced_jw(
139
156
  qubits,
140
157
  )
141
158
  yield CircuitInstruction(
142
- OrbitalRotationJW(ucj_op.norb, orbital_rotation), qubits
159
+ OrbitalRotationJW(ucj_op.norb, orbital_rotation, tol=tol), qubits
143
160
  )
144
161
  if ucj_op.final_orbital_rotation is not None:
145
162
  yield CircuitInstruction(
146
- OrbitalRotationJW(ucj_op.norb, ucj_op.final_orbital_rotation), qubits
163
+ OrbitalRotationJW(ucj_op.norb, ucj_op.final_orbital_rotation, tol=tol),
164
+ qubits,
147
165
  )
148
166
 
149
167
 
@@ -153,34 +171,43 @@ class UCJOpSpinlessJW(Gate):
153
171
  See :class:`ffsim.UCJOpSpinless` for a description of this gate's unitary.
154
172
  """
155
173
 
156
- def __init__(self, ucj_op: variational.UCJOpSpinless, *, label: str | None = None):
174
+ def __init__(
175
+ self,
176
+ ucj_op: variational.UCJOpSpinless,
177
+ *,
178
+ tol: float = 1e-12,
179
+ label: str | None = None,
180
+ ):
157
181
  """Create a new spinless unitary cluster Jastrow (UCJ) gate.
158
182
 
159
183
  Args:
160
184
  ucj_op: The UCJ operator.
185
+ tol: Tolerance for the Givens decomposition of the orbital rotations.
186
+ Matrix entries smaller than this value will be treated as equal to zero.
161
187
  label: The label of the gate.
162
188
  """
163
189
  self.ucj_op = ucj_op
190
+ self.tol = tol
164
191
  super().__init__("ucj_spinless_jw", ucj_op.norb, [], label=label)
165
192
 
166
193
  def _define(self):
167
194
  """Gate decomposition."""
168
195
  qubits = QuantumRegister(self.num_qubits)
169
196
  self.definition = QuantumCircuit.from_instructions(
170
- _ucj_op_spinless_jw(qubits, self.ucj_op),
197
+ _ucj_op_spinless_jw(qubits, self.ucj_op, tol=self.tol),
171
198
  qubits=qubits,
172
199
  name=self.name,
173
200
  )
174
201
 
175
202
 
176
203
  def _ucj_op_spinless_jw(
177
- qubits: Sequence[Qubit], ucj_op: variational.UCJOpSpinless
204
+ qubits: Sequence[Qubit], ucj_op: variational.UCJOpSpinless, tol: float = 1e-12
178
205
  ) -> Iterator[CircuitInstruction]:
179
206
  for diag_coulomb_mat, orbital_rotation in zip(
180
207
  ucj_op.diag_coulomb_mats, ucj_op.orbital_rotations
181
208
  ):
182
209
  yield CircuitInstruction(
183
- OrbitalRotationSpinlessJW(ucj_op.norb, orbital_rotation.T.conj()),
210
+ OrbitalRotationSpinlessJW(ucj_op.norb, orbital_rotation.T.conj(), tol=tol),
184
211
  qubits,
185
212
  )
186
213
  yield CircuitInstruction(
@@ -188,10 +215,12 @@ def _ucj_op_spinless_jw(
188
215
  qubits,
189
216
  )
190
217
  yield CircuitInstruction(
191
- OrbitalRotationSpinlessJW(ucj_op.norb, orbital_rotation), qubits
218
+ OrbitalRotationSpinlessJW(ucj_op.norb, orbital_rotation, tol=tol), qubits
192
219
  )
193
220
  if ucj_op.final_orbital_rotation is not None:
194
221
  yield CircuitInstruction(
195
- OrbitalRotationSpinlessJW(ucj_op.norb, ucj_op.final_orbital_rotation),
222
+ OrbitalRotationSpinlessJW(
223
+ ucj_op.norb, ucj_op.final_orbital_rotation, tol=tol
224
+ ),
196
225
  qubits,
197
226
  )
@@ -405,18 +405,16 @@ def spectral_norm_diag_coulomb(
405
405
  # decompose the diag Coulomb mat as a sum of squared one-body operators
406
406
  one_body_tensors = one_body_square_decomposition(diag_coulomb_mat)
407
407
 
408
- # for a rank-1 diag Coulomb mat, we can compute the exact spectral norm
408
+ # for a rank-1 diag Coulomb mat, we can compute the exact spectral norm in the
409
+ # number representation. In the Z representation we return an upper bound instead.
409
410
  if len(one_body_tensors) == 1:
410
411
  one_body_tensor = one_body_tensors[0]
411
412
  if z_representation:
412
- # TODO this abs is probably not necessary
413
- return abs(
414
- spectral_norm_one_body_tensor(
415
- one_body_tensor, nelec=nelec, z_representation=True
416
- )
417
- ** 2
418
- - 0.25 * np.trace(diag_coulomb_mat)
413
+ norm_bound = spectral_norm_one_body_tensor(
414
+ one_body_tensor, nelec=nelec, z_representation=True
419
415
  )
416
+ quarter_trace = 0.25 * np.trace(diag_coulomb_mat)
417
+ return max(quarter_trace, norm_bound**2 - quarter_trace)
420
418
  return spectral_norm_one_body_tensor(one_body_tensor, nelec=nelec) ** 2
421
419
 
422
420
  # when the rank is greater than one, we only know how to return an upper bound
@@ -527,6 +527,8 @@ class UCJOpSpinUnbalanced(
527
527
  ValueError: Interaction pairs list contained duplicate interactions.
528
528
  ValueError: Interaction pairs list for alpha-alpha or beta-beta interactions
529
529
  contained lower triangular pairs.
530
+
531
+ .. _scipy.optimize.minimize: https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.minimize.html
530
532
  """
531
533
  if isinstance(n_reps, int) and n_reps <= 0:
532
534
  raise ValueError(f"n_reps must be at least 1. Got {n_reps}.")