Skip to content

Commit

Permalink
Force hamming_left and hamming_right to be kwargs throughout API (#16)
Browse files Browse the repository at this point in the history
* Force hamming_left and hamming_right to be kwargs throughout API

* Don't change order of args in postselect_and_subsample

* typo
  • Loading branch information
caleb-johnson authored Sep 13, 2024
1 parent 10a7332 commit 147405a
Show file tree
Hide file tree
Showing 13 changed files with 130 additions and 74 deletions.
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,
*,
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])

0 comments on commit 147405a

Please sign in to comment.