Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

deprecate indices_to_strings and strings_to_indices in favor of addresses_to_strings and strings_to_addresses #283

Merged
merged 1 commit into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions docs/explanations/state-vectors-and-gates.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
"Each entry of the state vector is associated with an electronic configuration, which can be labeled by the concatenation of two bitstrings, $\\lvert s_\\beta s_\\alpha \\rangle$, where $s_\\alpha$ is a bitstring of length $N$ with Hamming weight $N_\\alpha$, and $s_\\beta$ is a bitstring of length $N$ with Hamming weight $N_\\beta$. A full specification of the state vector representation requires a choice of ordering for the bitstrings. ffsim uses the same ordering as [PySCF](https://pyscf.org/)'s FCI module, `pyscf.fci`. You can use the `indices_to_strings` function in ffsim to convert a list of state vector indices to the corresponding bitstrings."
"Each entry of the state vector is associated with an electronic configuration, which can be labeled by the concatenation of two bitstrings, $\\lvert s_\\beta s_\\alpha \\rangle$, where $s_\\alpha$ is a bitstring of length $N$ with Hamming weight $N_\\alpha$, and $s_\\beta$ is a bitstring of length $N$ with Hamming weight $N_\\beta$. A full specification of the state vector representation requires a choice of ordering for the bitstrings. ffsim uses the same ordering as [PySCF](https://pyscf.org/)'s FCI module, `pyscf.fci`. You can use the `addresses_to_strings` function in ffsim to convert a list of state vector indices to the corresponding bitstrings."
]
},
{
Expand Down Expand Up @@ -87,7 +87,9 @@
}
],
"source": [
"strings = ffsim.indices_to_strings(range(dim), norb=norb, nelec=nelec)\n",
"strings = ffsim.addresses_to_strings(\n",
" range(dim), norb=norb, nelec=nelec, bitstring_type=ffsim.BitstringType.STRING\n",
")\n",
"\n",
"strings"
]
Expand Down
71 changes: 55 additions & 16 deletions python/ffsim/states/bitstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import numpy as np
from pyscf.fci import cistring
from typing_extensions import deprecated


class BitstringType(Enum):
Expand All @@ -45,6 +46,10 @@ class BitstringType(Enum):
"""Bit array."""


@deprecated(
"ffsim.indices_to_strings is deprecated. "
"Instead, use ffsim.addresses_to_strings."
)
def indices_to_strings(
indices: Sequence[int] | np.ndarray,
norb: int,
Expand Down Expand Up @@ -93,7 +98,6 @@ def indices_to_strings(
if isinstance(nelec, int):
# Spinless case
return convert_bitstring_type(
# TODO convert to python int
list(cistring.addrs2str(norb=norb, nelec=nelec, addrs=indices)),
input_type=BitstringType.INT,
output_type=bitstring_type,
Expand All @@ -105,14 +109,12 @@ def indices_to_strings(
dim_b = math.comb(norb, n_beta)
indices_a, indices_b = np.divmod(indices, dim_b)
strings_a = convert_bitstring_type(
# TODO convert to python int
list(cistring.addrs2str(norb=norb, nelec=n_alpha, addrs=indices_a)),
input_type=BitstringType.INT,
output_type=bitstring_type,
length=norb,
)
strings_b = convert_bitstring_type(
# TODO convert to python int
list(cistring.addrs2str(norb=norb, nelec=n_beta, addrs=indices_b)),
input_type=BitstringType.INT,
output_type=bitstring_type,
Expand All @@ -126,7 +128,7 @@ def indices_to_strings(


def convert_bitstring_type(
strings: list[str] | list[int] | np.ndarray,
strings: Sequence[str] | Sequence[int] | np.ndarray,
input_type: BitstringType,
output_type: BitstringType,
length: int,
Expand Down Expand Up @@ -154,7 +156,7 @@ def convert_bitstring_type(
return np.array([[b == "1" for b in s] for s in strings])

if input_type is BitstringType.INT:
strings = cast(list[int], strings)
strings = cast(Sequence[int], strings)
if output_type is BitstringType.STRING:
return [f"{string:0{length}b}" for string in strings]

Expand Down Expand Up @@ -222,6 +224,10 @@ def concatenate_bitstrings(
return np.concatenate([strings_b, strings_a], axis=1)


@deprecated(
"ffsim.strings_to_indices is deprecated. "
"Instead, use ffsim.strings_to_addresses."
)
def strings_to_indices(
strings: Sequence[str], norb: int, nelec: int | tuple[int, int]
) -> np.ndarray:
Expand Down Expand Up @@ -273,7 +279,8 @@ def addresses_to_strings(
norb: int,
nelec: int | tuple[int, int],
concatenate: bool = True,
) -> np.ndarray | tuple[np.ndarray, np.ndarray]:
bitstring_type: BitstringType = BitstringType.INT,
):
"""Convert state vector addresses to bitstrings.

Example:
Expand Down Expand Up @@ -304,6 +311,7 @@ def addresses_to_strings(
nelec: Either a single integer representing the number of fermions for a
spinless system, or a pair of integers storing the numbers of spin alpha
and spin beta fermions.
bitstring_type: The desired type of bitstring output.
concatenate: Whether to concatenate the spin-alpha and spin-beta parts of the
bitstrings. If True, then a single list of concatenated bitstrings is
returned. The strings are concatenated in the order :math:`s_b s_a`,
Expand All @@ -313,23 +321,44 @@ def addresses_to_strings(
In the spinless case (when `nelec` is an integer), this argument is ignored.
"""
if isinstance(nelec, int):
return cistring.addrs2str(norb=norb, nelec=nelec, addrs=addresses)
if norb >= 32:
raise NotImplementedError(
"addresses_to_strings currently does not support norb >= 32."
# Spinless case
return convert_bitstring_type(
[
int(s)
for s in cistring.addrs2str(norb=norb, nelec=nelec, addrs=addresses)
],
input_type=BitstringType.INT,
output_type=bitstring_type,
length=norb,
)

# Spinful case
n_alpha, n_beta = nelec
dim_b = math.comb(norb, n_beta)
addresses_a, addresses_b = np.divmod(addresses, dim_b)
strings_a = cistring.addrs2str(norb=norb, nelec=n_alpha, addrs=addresses_a)
strings_b = cistring.addrs2str(norb=norb, nelec=n_beta, addrs=addresses_b)
indices_a, indices_b = np.divmod(addresses, dim_b)
strings_a = convert_bitstring_type(
[int(s) for s in cistring.addrs2str(norb=norb, nelec=n_alpha, addrs=indices_a)],
input_type=BitstringType.INT,
output_type=bitstring_type,
length=norb,
)
strings_b = convert_bitstring_type(
[int(s) for s in cistring.addrs2str(norb=norb, nelec=n_beta, addrs=indices_b)],
input_type=BitstringType.INT,
output_type=bitstring_type,
length=norb,
)
if concatenate:
return (strings_b << norb) + strings_a
return concatenate_bitstrings(
strings_a, strings_b, bitstring_type=bitstring_type, length=norb
)
return strings_a, strings_b


def strings_to_addresses(
strings: Sequence[int] | np.ndarray, norb: int, nelec: int | tuple[int, int]
strings: Sequence[int] | Sequence[str] | np.ndarray,
norb: int,
nelec: int | tuple[int, int],
) -> np.ndarray:
"""Convert bitstrings to state vector addresses.

Expand Down Expand Up @@ -360,9 +389,19 @@ def strings_to_addresses(
# output:
# array([0, 1, 2, 3, 4, 5, 6, 7, 8], dtype=int32)
"""
if not len(strings):
return np.array([])
if isinstance(strings, np.ndarray):
bitstring_type = BitstringType.BIT_ARRAY
elif isinstance(strings[0], str):
bitstring_type = BitstringType.STRING
else:
bitstring_type = BitstringType.INT
strings = convert_bitstring_type(
strings, input_type=bitstring_type, output_type=BitstringType.INT, length=norb
)
if isinstance(nelec, int):
return cistring.strs2addr(norb=norb, nelec=nelec, strings=strings)

n_alpha, n_beta = nelec
strings = np.asarray(strings)
strings_a = strings & ((1 << norb) - 1)
Expand Down
10 changes: 5 additions & 5 deletions python/ffsim/states/states.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
from ffsim.gates.orbital_rotation import apply_orbital_rotation
from ffsim.states.bitstring import (
BitstringType,
addresses_to_strings,
concatenate_bitstrings,
indices_to_strings,
restrict_bitstrings,
)

Expand Down Expand Up @@ -494,7 +494,7 @@ def _sample_state_vector_spinless(
rng = np.random.default_rng(seed)
probabilities = np.abs(vec) ** 2
samples = rng.choice(len(vec), size=shots, p=probabilities)
strings = indices_to_strings(samples, norb, nelec, bitstring_type=bitstring_type)
strings = addresses_to_strings(samples, norb, nelec, bitstring_type=bitstring_type)
if list(orbs) == list(range(norb)):
return strings
return restrict_bitstrings(strings, orbs, bitstring_type=bitstring_type)
Expand All @@ -519,12 +519,12 @@ def _sample_state_vector_spinful(
orbs_a, orbs_b = orbs

if list(orbs_a) == list(orbs_b) == list(range(norb)):
# All orbitals are sampled, so we can simply call indices_to_strings
return indices_to_strings(
# All orbitals are sampled, so we can simply call addresses_to_strings
return addresses_to_strings(
samples, norb, nelec, concatenate=concatenate, bitstring_type=bitstring_type
)

strings_a, strings_b = indices_to_strings(
strings_a, strings_b = addresses_to_strings(
samples, norb, nelec, concatenate=False, bitstring_type=bitstring_type
)
strings_a = restrict_bitstrings(strings_a, orbs_a, bitstring_type=bitstring_type)
Expand Down
Loading
Loading