Skip to content

Commit

Permalink
Dont batch unless separate samplers are used for each subcircuit (#333)
Browse files Browse the repository at this point in the history
* Don't batch unnecessarily

* mypy

* black

* Missing varname

* fix bug

* Clean up code

* Improve comments

* release note

* release note

* Update batch-by-sampler-c4ae836df9997b1d.yaml

* Update batch-by-sampler-c4ae836df9997b1d.yaml

* Update batch-by-sampler-c4ae836df9997b1d.yaml

* Bump Python version in Dockerfile to 3.11 (#331)

Now that CKT supports Python 3.11, we might as well use the latest version in the Dockerfile.

* Fix formatting of example in `reduce_bitstrings` docstring (#332)

* Implement cutting of general 2-qubit unitaries (#302)

* Add support for `SwapGate`

* Reorder terms

[ci skip]

* Add missing terms

* DRY the coefficients

* Fix coverage

* Add support for `iSwapGate`

* Fix black

* Add to release note

* Fix type hint

* Gates without parameters are nicer to work with

and can be singletons, one day!

* Remove a line

* Add comments describing channels

* `_copy_unique_sublists`

* Add `DCXGate`

* Tweak

* Implement cutting of general 2-qubit unitaries

Builds on #294.  Closes #186.

* Add tests of additional gates

* Fix type annotation

* Add explanatory comments

* `supported_gates()` -> `explicitly_supported_gates()`

* Add to references

* Improved error message and test about `to_matrix` conversion failing

* Add xref to `QPDBasis` in docstrings

* Add `qpdbasis_from_gate` to Sphinx build

* Make `explicitly_supported_gates` private and remove its release note

It's not clear that this function remains useful now that we
support essentially all 2-qubit gates.  If we find a use for it
in the future, we can re-introduce it (or something like it) as a
public interface.

* Fix intersphinx link

* Release note

* Update qpd.py: remove extraneous `from None`

* Improve the instructions regarding pandoc (#336)

I've also tried to make the developer documentation easier to discover

* Make the repository link more obvious from the Sphinx build (#338)

* Make the repository link more obvious from the Sphinx build

* Enable "edit" link in the header

* Add comment

* Add README badge linking to stable documentation (#339)

* Add docs badge to link to stable docs

* Add ruff badge

* Remove ruff

* Update README.md (#340)

Making directions on opening the docs a little more succinct.

* Change var name

* Add SECURITY.md (#337)

* Implement wire cutting as a two-qubit instruction (#174)

* Implement wire cutting as a two-qubit instruction

* Update type annotation

* s/gate/instruction/

* Add overhead test for `Move` instruction

* Add wire cutting tutorial

* Add `Move` to Sphinx build

* Doc updates suggested by Caleb

* Add release note and link to new tutorial

* Clarify wording

following #174 (comment)

* Improvements to `Move` docstring

* Use svg as the plot format

This avoids pixelation on high-dpi displays

* Remove unnecessary uses of `CircuitInstruction`

https://github.com/Qiskit-Extensions/circuit-knitting-toolbox/pull/174/files#r1278067109

* The notebook tests should ignore any files that crop up in `docs/_build`

`matplotlib.sphinxext.plot_directive` likes to leave python files there

---------

Co-authored-by: Jim Garrison <garrison@ibm.com>
Co-authored-by: Ibrahim Shehzad <75153717+IbrahimShehzad@users.noreply.github.com>
  • Loading branch information
3 people authored Aug 14, 2023
1 parent e8df4dd commit 4e0cf63
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 27 deletions.
67 changes: 40 additions & 27 deletions circuit_knitting/cutting/cutting_evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,30 +128,47 @@ def execute_experiments(
num_samples,
)

# Create a list of samplers -- one for each qubit partition
num_partitions = len(subexperiments[0])
# Create a list of samplers to use -- one for each batch
if isinstance(samplers, BaseSampler):
samplers_by_partition = [samplers] * num_partitions
samplers_by_batch = [samplers]
batches = [
[
sample[i]
for sample in subexperiments
for i in range(len(subexperiments[0]))
]
]
else:
samplers_by_partition = [samplers[key] for key in sorted(samplers.keys())]
samplers_by_batch = [samplers[key] for key in sorted(samplers.keys())]
batches = [
[sample[i] for sample in subexperiments]
for i in range(len(subexperiments[0]))
]

# Run each partition's sub-experiments
quasi_dists_by_partition = [
# There should be one batch per input sampler
assert len(samplers_by_batch) == len(batches)

# Run each batch of sub-experiments
quasi_dists_by_batch = [
_run_experiments_batch(
[sample[i] for sample in subexperiments],
samplers_by_partition[i],
batches[i],
samplers_by_batch[i],
)
for i in range(num_partitions)
for i in range(len(samplers_by_batch))
]

# Reformat the counts to match the shape of the input before returning
num_unique_samples = len(subexperiments)
# Build the output data structure to match the shape of input subexperiments
quasi_dists: list[list[list[tuple[dict[str, int], int]]]] = [
[] for _ in range(num_unique_samples)
[] for _ in range(len(subexperiments))
]
for i in range(num_unique_samples):
for partition in quasi_dists_by_partition:
quasi_dists[i].append(partition[i])
count = 0
for i in range(len(subexperiments)):
for j in range(len(subexperiments[0])):
if len(samplers_by_batch) == 1:
quasi_dists[i].append(quasi_dists_by_batch[0][count])
count += 1
else:
quasi_dists[i].append(quasi_dists_by_batch[j][i])

return CuttingExperimentResults(quasi_dists, coefficients)

Expand Down Expand Up @@ -327,18 +344,14 @@ def _run_experiments_batch(
quasi_dists_flat = sampler.run(experiments_flat).result().quasi_dists

# Reshape the output data to match the input
if len(subexperiments) == 1:
quasi_dists_reshaped = np.array([quasi_dists_flat])
num_qpd_bits = np.array([num_qpd_bits_flat])
else:
# We manually build the shape tuple in second arg because it behaves strangely
# with QuantumCircuits in some versions. (e.g. passes local pytest but fails in tox env)
quasi_dists_reshaped = np.reshape(
quasi_dists_flat, (len(subexperiments), len(subexperiments[0]))
)
num_qpd_bits = np.reshape(
num_qpd_bits_flat, (len(subexperiments), len(subexperiments[0]))
)
quasi_dists_reshaped: list[list[QuasiDistribution]] = [[] for _ in subexperiments]
num_qpd_bits: list[list[int]] = [[] for _ in subexperiments]
count = 0
for i, subcirc in enumerate(subexperiments):
for j in range(len(subcirc)):
quasi_dists_reshaped[i].append(quasi_dists_flat[count])
num_qpd_bits[i].append(num_qpd_bits_flat[count])
count += 1

# Create the counts tuples, which include the number of QPD measurement bits
quasi_dists: list[list[tuple[dict[str, float], int]]] = [
Expand Down
5 changes: 5 additions & 0 deletions releasenotes/notes/batch-by-sampler-c4ae836df9997b1d.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
upgrade:
- |
:func:`~circuit_knitting.cutting.execute_experiments` no longer creates separate jobs for each subcircuit by default.
Now, separate jobs are only created if separate :class:`~qiskit.primitives.BaseSampler` instances are provided for each circuit partition.

0 comments on commit 4e0cf63

Please sign in to comment.