Skip to content

Commit

Permalink
Deprecate determinant addresses as input to fermionic solver functions (
Browse files Browse the repository at this point in the history
#29)

* Fermionic solver functions should take the bitstring matrix as input, same as qubit solver

* Add `addresses` as kwarg (#31)

* Small tutorial fix

* Update docstring

* Remove addresses as kwarg

* Clean up docstrings

---------

Co-authored-by: Jim Garrison <[email protected]>
  • Loading branch information
caleb-johnson and garrison authored Sep 18, 2024
1 parent 256e5e1 commit c115ea2
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 47 deletions.
3 changes: 2 additions & 1 deletion docs/how_tos/choose_subspace_dimension.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -179,9 +179,10 @@
" addresses = bitstring_matrix_to_sorted_addresses(batches[j], open_shell=open_shell)\n",
" int_d[j] = len(addresses[0]) * len(addresses[1])\n",
" energy_sci, coeffs_sci, avg_occs, spin = solve_fermion(\n",
" addresses,\n",
" batches[j],\n",
" hcore,\n",
" eri,\n",
" open_shell=open_shell,\n",
" spin_sq=spin_sq,\n",
" max_davidson=max_davidson_cycles,\n",
" )\n",
Expand Down
2 changes: 1 addition & 1 deletion docs/how_tos/select_open_closed_shell.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"source": [
"# Understand open-shell vs closed-shell options and its effect in the subspace construction\n",
"\n",
"In this \"how-to\", we will show how to choose subpace dimensions in the `sqd` package to post-process quantum samples using the [self-consistent configuration recovery technique](https://arxiv.org/abs/2405.05068). \n",
"In this \"how-to\", we will show how to choose subpace dimensions in the `qiskit_addon_sqd` package to post-process quantum samples using the [self-consistent configuration recovery technique](https://arxiv.org/abs/2405.05068). \n",
"\n",
"More importantly, this \"how-to\" also highlights some differences in the behaviour in the susbapce construction when run in `open_shell = False` or `open_shell = True` modes:\n",
"\n",
Expand Down
32 changes: 13 additions & 19 deletions docs/how_tos/use_oo_to_optimize_hamiltonian_basis.ipynb

Large diffs are not rendered by default.

13 changes: 6 additions & 7 deletions docs/tutorials/01_chemistry_hamiltonian.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -236,8 +236,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Gate counts (w/o pre-init passes): OrderedDict({'rz': 7402, 'sx': 6009, 'ecr': 2232, 'x': 317, 'measure': 32, 'barrier': 1})\n",
"Gate counts (w/ pre-init passes): OrderedDict({'rz': 4158, 'sx': 3186, 'ecr': 1262, 'x': 210, 'measure': 32, 'barrier': 1})\n"
"Gate counts (w/o pre-init passes): OrderedDict({'rz': 7405, 'sx': 6014, 'ecr': 2232, 'x': 315, 'measure': 32, 'barrier': 1})\n",
"Gate counts (w/ pre-init passes): OrderedDict({'rz': 4156, 'sx': 3185, 'ecr': 1262, 'x': 209, 'measure': 32, 'barrier': 1})\n"
]
}
],
Expand Down Expand Up @@ -317,9 +317,9 @@
"source": [
"First, we will transform the counts into a bitstring matrix and probability array for post-processing.\n",
"\n",
"Each row in the matrix represents one unique bitstring. Since qubits are normally indexed from the right of a bitstring, column index ``0`` represents qubit ``N-1``, and column index ``N-1`` represents qubit ``0``, where ``N`` is the number of qubits.\n",
"Each row in the matrix represents one unique bitstring. Since qubits are indexed from the right of a bitstring in Qiskit, column ``0`` represents qubit ``N-1``, and column ``N-1`` represents qubit ``0``, where ``N`` is the number of qubits.\n",
"\n",
"The alpha particles are represented in the column range ``(N, N/2]``, and the beta particles are represented in the column range ``(N/2, 0]``."
"The alpha particles are represented in the column index range ``(N, N/2]``, and the beta particles are represented in the column range ``(N/2, 0]``."
]
},
{
Expand Down Expand Up @@ -372,7 +372,6 @@
"source": [
"from qiskit_addon_sqd.configuration_recovery import recover_configurations\n",
"from qiskit_addon_sqd.fermion import (\n",
" bitstring_matrix_to_sorted_addresses,\n",
" flip_orbital_occupancies,\n",
" solve_fermion,\n",
")\n",
Expand Down Expand Up @@ -427,11 +426,11 @@
" occs_tmp = np.zeros((n_batches, 2 * num_orbitals))\n",
" coeffs = []\n",
" for j in range(n_batches):\n",
" addresses = bitstring_matrix_to_sorted_addresses(batches[j], open_shell=open_shell)\n",
" energy_sci, coeffs_sci, avg_occs, spin = solve_fermion(\n",
" addresses,\n",
" batches[j],\n",
" hcore,\n",
" eri,\n",
" open_shell=open_shell,\n",
" spin_sq=spin_sq,\n",
" max_davidson=max_davidson_cycles,\n",
" )\n",
Expand Down
79 changes: 60 additions & 19 deletions qiskit_addon_sqd/fermion.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

from __future__ import annotations

import warnings

import numpy as np
from jax import Array, config, grad, jit, vmap
from jax import numpy as jnp
Expand All @@ -39,28 +41,36 @@


def solve_fermion(
addresses: tuple[np.ndarray, np.ndarray],
bitstring_matrix: tuple[np.ndarray, np.ndarray] | np.ndarray,
/,
hcore: np.ndarray,
eri: np.ndarray,
*,
open_shell: bool = False,
spin_sq: int | None = None,
max_davidson: int = 100,
verbose: int | None = None,
) -> tuple[float, np.ndarray, list[np.ndarray], float]:
"""
Approximate the ground state given molecular integrals and Slater determinant addresses.
.. note::
The ``addresses`` are expected to be unique and sorted. While this will be handled
for the user automatically, this function could become slower if the input
addresses are not sorted or nearly-sorted.
Approximate the ground state given molecular integrals and a set of electronic configurations.
Args:
addresses: A length-2 tuple of 1D arrays containing sorted, base-10
representations of bitstrings. The first array represents configurations of the
alpha particles, and the second array represents that of the beta particles.
bitstring_matrix: A set of configurations defining the subspace onto which the Hamiltonian
will be projected and diagonalized. This is a 2D array of ``bool`` representations of bit
values such that each row represents a single bitstring. The spin-up configurations
should be specified by column indices in range ``(N, N/2]``, and the spin-down
configurations should be specified by column indices in range ``(N/2, 0]``, where ``N``
is the number of qubits.
(DEPRECATED) The configurations may also be specified by a length-2 tuple of sorted 1D
arrays containing base-10, unsigned integer representations of the determinants. The
two lists should represent the spin-up and spin-down orbitals, respectively.
hcore: Core Hamiltonian matrix representing single-electron integrals
eri: Electronic repulsion integrals representing two-electron integrals
open_shell: A flag specifying whether configurations from the left and right
halves of the bitstrings should be kept separate. If ``False``, addresses
from the left and right halves of the bitstrings are combined into a single
set of unique configurations and used for both the alpha and beta subspaces.
spin_sq: Target value for the total spin squared for the ground state.
If ``None``, no spin will be imposed.
max_davidson: The maximum number of cycles of Davidson's algorithm
Expand All @@ -76,6 +86,17 @@ def solve_fermion(
Raises:
ValueError: The input determinant ``addresses`` must be non-empty, sorted arrays of integers.
"""
if isinstance(bitstring_matrix, tuple):
warnings.warn(
"Passing the input determinants as integers is deprecated. Users should instead pass a bitstring matrix defining the subspace.",
DeprecationWarning,
stacklevel=2,
)
addresses = bitstring_matrix
else:
# This will become the default code path after the deprecation period.
addresses = bitstring_matrix_to_sorted_addresses(bitstring_matrix, open_shell=open_shell)
addresses = addresses[::-1]
addresses = _check_addresses(addresses)

num_up = format(addresses[0][0], "b").count("1")
Expand Down Expand Up @@ -108,11 +129,13 @@ def solve_fermion(


def optimize_orbitals(
addresses: tuple[np.ndarray, np.ndarray],
bitstring_matrix: tuple[np.ndarray, np.ndarray] | np.ndarray,
/,
hcore: np.ndarray,
eri: np.ndarray,
k_flat: np.ndarray,
*,
open_shell: bool = False,
spin_sq: float = 0.0,
num_iters: int = 10,
num_steps_grad: int = 10_000,
Expand All @@ -133,20 +156,26 @@ def optimize_orbitals(
Refer to `Sec. II A 4 <https://arxiv.org/pdf/2405.05068>`_ for more detailed
discussion on this orbital optimization technique.
.. note::
The input ``addresses`` are expected to be unique and sorted. While this will be
handled for the user automatically, this function may become slower if the input
addresses are not sorted or nearly-sorted.
Args:
addresses: A length-2 tuple of 1D arrays containing sorted, base-10
representations of bitstrings. The first array represents configurations of the
alpha particles, and the second array represents that of the beta particles.
bitstring_matrix: A set of configurations defining the subspace onto which the Hamiltonian
will be projected and diagonalized. This is a 2D array of ``bool`` representations of bit
values such that each row represents a single bitstring. The spin-up configurations
should be specified by column indices in range ``(N, N/2]``, and the spin-down
configurations should be specified by column indices in range ``(N/2, 0]``, where ``N``
is the number of qubits.
(DEPRECATED) The configurations may also be specified by a length-2 tuple of sorted 1D
arrays containing base-10, unsigned integer representations of the determinants. The
two lists should represent the spin-up and spin-down orbitals, respectively.
hcore: Core Hamiltonian matrix representing single-electron integrals
eri: Electronic repulsion integrals representing two-electron integrals
k_flat: 1D array defining the orbital transform. This array will be reshaped
to be of shape (# orbitals, # orbitals) before being used as a
similarity transform operator on the orbitals. Thus ``len(k_flat)=# orbitals**2``.
open_shell: A flag specifying whether configurations from the left and right
halves of the bitstrings should be kept separate. If ``False``, addresses
from the left and right halves of the bitstrings are combined into a single
set of unique configurations and used for both the alpha and beta subspaces.
spin_sq: Target value for the total spin squared for the ground state
num_iters: The number of iterations of orbital optimization to perform
max_davidson: The maximum number of cycles of Davidson's algorithm to
Expand All @@ -161,6 +190,18 @@ def optimize_orbitals(
- An optimized 1D array defining the orbital transform
- Average orbital occupancy
"""
if isinstance(bitstring_matrix, tuple):
warnings.warn(
"Passing a length-2 tuple of sorted addresses to define the subspace is deprecated. Users "
"should instead pass in the bitstring matrix defining the subspace.",
DeprecationWarning,
stacklevel=2,
)
addresses = bitstring_matrix
else:
# Flip the output so the alpha addresses are on the left with [::-1]
addresses = bitstring_matrix_to_sorted_addresses(bitstring_matrix, open_shell=open_shell)
addresses = addresses[::-1]
addresses = _check_addresses(addresses)

num_up = format(addresses[0][0], "b").count("1")
Expand Down
47 changes: 47 additions & 0 deletions releasenotes/notes/deprecate-addr-526f0c5bf681f739.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
deprecations:
- |
The ``addressess`` argument to :func:`qiskit_addon_sqd.fermion.solve_fermion` and :func:`qiskit_addon_sqd.fermion.optimize_orbitals` has been deprecated in favor of ``bitstring_matrix``. Users are no longer required to convert their configurations to base-10 determinants; instead, they should now pass in the bitstring matrix specifying the subspace onto which to project and diagonalize the Hamiltonian. The conversion to determinant addresses will be done internally.
upgrade:
- |
Specifying ``addresses`` as a keyword argument to :func:`qiskit_addon_sqd.fermion.solve_fermion` and :func:`qiskit_addon_sqd.fermion.optimize_orbitals` is no longer supported. Users may still pass ``addresses`` as the first positional argument; however, this usage is deprecated. Users are encouraged to pass the bitstring matrix defining the subspace as the first positional arguments to these functions, as shown below.
To upgrade, change this code
.. code-block:: python
from qiskit_addon_sqd.fermion import bitstring_matrix_to_sorted_addresses
from qiskit_addon_sqd.fermion import solve_fermion, optimize_orbitals
bitstring_matrix = ...
addresses = bitstring_matrix_to_sorted_addresses(bitstring_matrix, open_shell=open_shell)
energy, coeffs, occs, spin = solve_fermion(
addresses=addresses,
hcore=hcore,
eri=eri,
)
...
e_oo, rotation, occs_oo = optimize_orbitals(
addresses=addresses,
hcore=hcore,
eri=eri,
)
...to this code
.. code-block:: python
from qiskit_addon_sqd.fermion import solve_fermion, optimize_orbitals
bitstring_matrix = ...
energy, coeffs, occs, spin = solve_fermion(
bitstring_matrix,
hcore=hcore,
eri=eri,
)
...
e_oo, rotation, occs_oo = optimize_orbitals(
bitstring_matrix,
hcore=hcore,
eri=eri,
)

0 comments on commit c115ea2

Please sign in to comment.