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

Force hamming_left and hamming_right to be kwargs throughout API #16

Merged
merged 3 commits into from
Sep 13, 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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ This package contains the Qiskit addon for sample-based quantum diagonalization

Classical distributed computing is used to process samples obtained from a quantum processor and to project and diagonalize a target Hamiltonian in a subspace spanned by them. This allows SQD to be robust to samples corrupted by quantum noise and deal with large Hamiltonians, such as chemistry Hamiltonians with millions of interaction terms, beyond the reach of any exact diagonalization methods.

The SQD tool can target Hamiltonians expressed as linear combination of Pauli operators, or second-quantized fermionic operators. The input samples are obtained by quantum circuits defined by the user, which are believed to be good representations of eigenstates (e.g. the ground state) of a target operator. The convergence rate of SQD as a function of the number of samples improves with the sparseness of the target eigenstate.
The SQD tool can target Hamiltonians expressed as linear combinations of Pauli operators, or second-quantized fermionic operators. The input samples are obtained by quantum circuits defined by the user, which are believed to be good representations of eigenstates (e.g. the ground state) of a target operator. The convergence rate of SQD as a function of the number of samples improves with the sparseness of the target eigenstate.

The projection and diagonalization steps are performed by a classical solver. We provide here two generic solvers, one for fermionic systems and another for qubit systems. Other solvers that might be more efficient for specific systems can be interfaced by the users.

Expand Down
12 changes: 5 additions & 7 deletions docs/how_tos/choose_subspace_dimension.ipynb

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions docs/how_tos/integrate_dice_solver.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -83,18 +83,18 @@
" occupancies_bitwise,\n",
" num_elec_a,\n",
" num_elec_b,\n",
" # rand_seed=rand_seed,\n",
" rand_seed=rand_seed,\n",
" )\n",
"\n",
" # Throw out samples with incorrect hamming weight and create batches of subsamples.\n",
" batches = postselect_and_subsample(\n",
" bs_mat_tmp,\n",
" probs_arr_tmp,\n",
" num_elec_a,\n",
" num_elec_b,\n",
" samples_per_batch,\n",
" n_batches,\n",
" # rand_seed=rand_seed,\n",
" hamming_right=num_elec_a,\n",
" hamming_left=num_elec_b,\n",
" samples_per_batch=samples_per_batch,\n",
" num_batches=n_batches,\n",
" rand_seed=rand_seed,\n",
" )\n",
" # Run eigenstate solvers in a loop. This loop should be parallelized for larger problems.\n",
" int_e = np.zeros(n_batches)\n",
Expand Down
16 changes: 8 additions & 8 deletions docs/how_tos/select_open_closed_shell.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,10 @@
"batches = postselect_and_subsample(\n",
" bitstring_matrix_full,\n",
" probs_arr_full,\n",
" num_elec_a,\n",
" num_elec_b,\n",
" samples_per_batch,\n",
" n_batches,\n",
" hamming_right=num_elec_a,\n",
" hamming_left=num_elec_b,\n",
" samples_per_batch=samples_per_batch,\n",
" num_batches=n_batches,\n",
" rand_seed=rand_seed,\n",
")\n",
"\n",
Expand Down Expand Up @@ -406,10 +406,10 @@
"batches = postselect_and_subsample(\n",
" bitstring_matrix_full,\n",
" probs_arr_full,\n",
" num_elec_a,\n",
" num_elec_b,\n",
" samples_per_batch,\n",
" n_batches,\n",
" hamming_right=num_elec_a,\n",
" hamming_left=num_elec_b,\n",
" samples_per_batch=samples_per_batch,\n",
" num_batches=n_batches,\n",
" rand_seed=rand_seed,\n",
")\n",
"\n",
Expand Down
8 changes: 4 additions & 4 deletions docs/how_tos/use_oo_to_optimize_hamiltonian_basis.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,10 @@
" batches = postselect_and_subsample(\n",
" bs_mat_tmp,\n",
" probs_arr_tmp,\n",
" num_elec_a,\n",
" num_elec_b,\n",
" samples_per_batch,\n",
" n_batches,\n",
" hamming_right=num_elec_a,\n",
" hamming_left=num_elec_b,\n",
" samples_per_batch=samples_per_batch,\n",
" num_batches=n_batches,\n",
" rand_seed=rand_seed,\n",
" )\n",
"\n",
Expand Down
2 changes: 1 addition & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This package contains the Qiskit addon for sample-based quantum diagonalization

Classical distributed computing is used to process samples obtained from a quantum processor, and to project and diagonalize a target Hamiltonian in a subspace spanned by them. This allows SQD to be robust to samples corrupted by quantum noise and deal with large Hamiltonians, such as chemistry Hamiltonians with millions of interaction terms, beyond the reach of any exact diagonalization methods.

The SQD tool can target Hamiltonians expressed as linear combination of Pauli operators, or second-quantized fermionic operators. The input samples are obtained by quantum circuits defined by the user, which are believed to be good representations of eigenstates (e.g. the ground state) of a target operator. The convergence rate of SQD as a function of the number of samples improves with the sparseness of the target eigenstate.
The SQD tool can target Hamiltonians expressed as linear combinations of Pauli operators, or second-quantized fermionic operators. The input samples are obtained by quantum circuits defined by the user, which are believed to be good representations of eigenstates (e.g. the ground state) of a target operator. The convergence rate of SQD as a function of the number of samples improves with the sparseness of the target eigenstate.

The projection and diagonalization steps are performed by a classical solver. We provide here two generic solvers, one for fermionic systems and another for qubit systems. Other solvers that might be more efficient for specific systems can be interfaced by the users.

Expand Down
14 changes: 7 additions & 7 deletions docs/tutorials/01_chemistry_hamiltonian.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@
"name": "stderr",
"output_type": "stream",
"text": [
"Overwritten attributes get_hcore get_ovlp of <class 'pyscf.scf.hf_symm.SymAdaptedRHF'>\n"
"Overwritten attributes get_ovlp get_hcore of <class 'pyscf.scf.hf_symm.SymAdaptedRHF'>\n"
]
}
],
Expand Down Expand Up @@ -236,8 +236,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Gate counts (w/o pre-init passes): OrderedDict({'rz': 7403, 'sx': 6014, 'ecr': 2232, 'x': 315, 'measure': 32, 'barrier': 1})\n",
"Gate counts (w/ pre-init passes): OrderedDict({'rz': 4163, 'sx': 3189, 'ecr': 1262, 'x': 209, 'measure': 32, 'barrier': 1})\n"
"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"
]
}
],
Expand Down Expand Up @@ -414,10 +414,10 @@
" batches = postselect_and_subsample(\n",
" bs_mat_tmp,\n",
" probs_arr_tmp,\n",
" num_elec_a,\n",
" num_elec_b,\n",
" samples_per_batch,\n",
" n_batches,\n",
" hamming_right=num_elec_a,\n",
" hamming_left=num_elec_b,\n",
" samples_per_batch=samples_per_batch,\n",
" num_batches=n_batches,\n",
" rand_seed=rand_seed,\n",
" )\n",
"\n",
Expand Down
3 changes: 1 addition & 2 deletions qiskit_addon_sqd/configuration_recovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@


def post_select_by_hamming_weight(
bitstring_matrix: np.ndarray, hamming_right: int, hamming_left: int
bitstring_matrix: np.ndarray, *, hamming_right: int, hamming_left: int
) -> np.ndarray:
"""
Post-select bitstrings based on the hamming weight of each half.
Expand Down Expand Up @@ -62,7 +62,6 @@ def recover_configurations(
avg_occupancies: np.ndarray,
num_elec_a: int,
num_elec_b: int,
*,
rand_seed: int | None = None,
) -> tuple[np.ndarray, np.ndarray]:
"""
Expand Down
1 change: 1 addition & 0 deletions qiskit_addon_sqd/counts.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ def generate_counts_uniform(
def generate_counts_bipartite_hamming(
num_samples: int,
num_bits: int,
*,
hamming_right: int,
hamming_left: int,
rand_seed: None | int = None,
Expand Down
5 changes: 4 additions & 1 deletion qiskit_addon_sqd/subsampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
def postselect_and_subsample(
bitstring_matrix: np.ndarray,
probabilities: np.ndarray,
*,
caleb-johnson marked this conversation as resolved.
Show resolved Hide resolved
hamming_right: int,
hamming_left: int,
samples_per_batch: int,
Expand Down Expand Up @@ -76,7 +77,9 @@ def postselect_and_subsample(
raise ValueError("Hamming weight must be specified with a non-negative integer.")

# Post-select only bitstrings with correct hamming weight
mask_postsel = post_select_by_hamming_weight(bitstring_matrix, hamming_left, hamming_right)
mask_postsel = post_select_by_hamming_weight(
bitstring_matrix, hamming_right=hamming_right, hamming_left=hamming_left
)
bs_mat_postsel = bitstring_matrix[mask_postsel]
probs_postsel = probabilities[mask_postsel]
probs_postsel = np.abs(probs_postsel) / np.sum(np.abs(probs_postsel))
Expand Down
55 changes: 55 additions & 0 deletions releasenotes/notes/hamming-kwarg-c73098a4c756453e.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
---
upgrade:
- |

The :func:`qiskit_addon_sqd.counts.generate_counts_bipartite_hamming`, :func:`qiskit_addon_sqd.subsampling.postselect_and_subsample`, and :func:`qiskit_addon_sqd.configuration_recovery.post_select_by_hamming_weight` now require the ``hamming_right`` and ``hamming_left`` arguments to be specified as keyword arguments. Additionally, the ``samples_per_batch`` and ``n_batches`` arguments to :func:`qiskit_addon_sqd.subsampling.post_select_by_hamming_weight` should now be passed as keyword arguments.

To upgrade

.. code-block:: python

from qiskit_addon_sqd.configuration_recovery import post_select_by_hamming_weight
from qiskit_addon_sqd.subsampling import postselect_and_subsample
from qiskit_addon_sqd.counts import generate_counts_bipartite_hamming

counts = generate_counts_bipartite_hamming(num_samples, num_bits, num_elec_a, num_elec_b)

...

bs_mat = post_select_by_hamming_weight(bs_mat_full, num_elec_a, num_elec_b)

...

batches = postselect_and_subsample(
bs_mat,
probs_arr,
num_elec_a,
num_elec_b,
samples_per_batch,
n_batches,
)

should be changed to

.. code-block:: python

from qiskit_addon_sqd.configuration_recovery import post_select_by_hamming_weight
from qiskit_addon_sqd.subsampling import postselect_and_subsample
from qiskit_addon_sqd.counts import generate_counts_bipartite_hamming

counts = generate_counts_bipartite_hamming(num_samples, num_bits, hamming_right=num_elec_a, hamming_left=num_elec_b)

...

bs_mat = post_select_by_hamming_weight(bs_mat_full, hamming_right=num_elec_a, hamming_left=num_elec_b)

...

batches = postselect_and_subsample(
bs_mat,
probs_arr,
hamming_right=num_elec_a,
hamming_left=num_elec_b,
samples_per_batch=samples_per_batch,
n_batches=n_batches,
)
10 changes: 5 additions & 5 deletions test/test_counts.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def test_generate_counts_bipartite_hamming(self):
hamming_left = 3
hamming_right = 2
counts = generate_counts_bipartite_hamming(
num_samples, num_bits, hamming_right, hamming_left
num_samples, num_bits, hamming_right=hamming_right, hamming_left=hamming_left
)
self.assertLessEqual(len(counts), num_samples)
for bs in counts:
Expand All @@ -92,7 +92,7 @@ def test_generate_counts_bipartite_hamming(self):
hamming_right = 2
with pytest.raises(ValueError) as e_info:
generate_counts_bipartite_hamming(
num_samples, num_bits, hamming_right, hamming_left
num_samples, num_bits, hamming_right=hamming_right, hamming_left=hamming_left
)
self.assertEqual(
"The number of bits must be specified with an even integer.", e_info.value.args[0]
Expand All @@ -104,7 +104,7 @@ def test_generate_counts_bipartite_hamming(self):
hamming_right = 2
with pytest.raises(ValueError) as e_info:
generate_counts_bipartite_hamming(
num_samples, num_bits, hamming_right, hamming_left
num_samples, num_bits, hamming_right=hamming_right, hamming_left=hamming_left
)
self.assertEqual(
"The number of samples must be specified with a positive integer.",
Expand All @@ -117,7 +117,7 @@ def test_generate_counts_bipartite_hamming(self):
hamming_right = 2
with pytest.raises(ValueError) as e_info:
generate_counts_bipartite_hamming(
num_samples, num_bits, hamming_right, hamming_left
num_samples, num_bits, hamming_right=hamming_right, hamming_left=hamming_left
)
self.assertEqual(
"The number of bits must be specified with a positive integer.",
Expand All @@ -130,7 +130,7 @@ def test_generate_counts_bipartite_hamming(self):
hamming_right = -1
with pytest.raises(ValueError) as e_info:
generate_counts_bipartite_hamming(
num_samples, num_bits, hamming_right, hamming_left
num_samples, num_bits, hamming_right=hamming_right, hamming_left=hamming_left
)
self.assertEqual(
"Hamming weights must be specified as non-negative integers.", e_info.value.args[0]
Expand Down
64 changes: 32 additions & 32 deletions test/test_subsampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,10 @@ def test_postselect_and_subsample(self):
batches = postselect_and_subsample(
self.bitstring_matrix,
self.uniform_probs,
hamming_right,
hamming_left,
samples_per_batch,
num_batches,
hamming_right=hamming_right,
hamming_left=hamming_left,
samples_per_batch=samples_per_batch,
num_batches=num_batches,
)
self.assertEqual(num_batches, len(batches))
for batch in batches:
Expand All @@ -158,10 +158,10 @@ def test_postselect_and_subsample(self):
batches = postselect_and_subsample(
self.bitstring_matrix,
self.uniform_probs,
hamming_right,
hamming_left,
samples_per_batch,
num_batches,
hamming_right=hamming_right,
hamming_left=hamming_left,
samples_per_batch=samples_per_batch,
num_batches=num_batches,
)
self.assertEqual(num_batches, len(batches))
for batch in batches:
Expand All @@ -178,10 +178,10 @@ def test_postselect_and_subsample(self):
batches = postselect_and_subsample(
self.bitstring_matrix[1:],
self.uniform_probs[1:],
hamming_right,
hamming_left,
samples_per_batch,
num_batches,
hamming_right=hamming_right,
hamming_left=hamming_left,
samples_per_batch=samples_per_batch,
num_batches=num_batches,
)
self.assertEqual(num_batches, len(batches))
for batch in batches:
Expand All @@ -195,10 +195,10 @@ def test_postselect_and_subsample(self):
postselect_and_subsample(
self.bitstring_matrix,
self.uniform_probs,
hamming_right,
hamming_left,
samples_per_batch,
num_batches,
hamming_right=hamming_right,
hamming_left=hamming_left,
samples_per_batch=samples_per_batch,
num_batches=num_batches,
)
assert (
e_info.value.args[0]
Expand All @@ -213,10 +213,10 @@ def test_postselect_and_subsample(self):
postselect_and_subsample(
self.bitstring_matrix,
self.uniform_probs,
hamming_right,
hamming_left,
samples_per_batch,
num_batches,
hamming_right=hamming_right,
hamming_left=hamming_left,
samples_per_batch=samples_per_batch,
num_batches=num_batches,
)
assert (
e_info.value.args[0]
Expand All @@ -231,10 +231,10 @@ def test_postselect_and_subsample(self):
postselect_and_subsample(
self.bitstring_matrix,
self.uniform_probs,
hamming_right,
hamming_left,
samples_per_batch,
num_batches,
hamming_right=hamming_right,
hamming_left=hamming_left,
samples_per_batch=samples_per_batch,
num_batches=num_batches,
)
assert (
e_info.value.args[0]
Expand All @@ -249,10 +249,10 @@ def test_postselect_and_subsample(self):
postselect_and_subsample(
self.bitstring_matrix,
np.array([]),
hamming_right,
hamming_left,
samples_per_batch,
num_batches,
hamming_right=hamming_right,
hamming_left=hamming_left,
samples_per_batch=samples_per_batch,
num_batches=num_batches,
)
assert (
e_info.value.args[0]
Expand All @@ -266,10 +266,10 @@ def test_postselect_and_subsample(self):
batches = postselect_and_subsample(
np.array([]),
np.array([]),
hamming_left,
hamming_right,
samples_per_batch,
num_batches,
hamming_right=hamming_right,
hamming_left=hamming_left,
samples_per_batch=samples_per_batch,
num_batches=num_batches,
)
self.assertEqual(num_batches, len(batches))
self.assertEqual(0, batches[0].shape[0])