Skip to content

Commit

Permalink
Merge branch 'main' into reconstruct-validation
Browse files Browse the repository at this point in the history
  • Loading branch information
garrison committed May 16, 2024
2 parents 5758291 + c468735 commit 73fe385
Show file tree
Hide file tree
Showing 17 changed files with 192 additions and 105 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/test_latest_versions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ jobs:
max-parallel: 4
matrix:
os: [ubuntu-latest]
python-version: ["3.9", "3.12"]
python-version: ["3.8", "3.12"]
include:
- os: macos-13
python-version: "3.8"
- os: macos-latest
python-version: "3.12"
- os: windows-latest
python-version: "3.10"
steps:
Expand Down
20 changes: 19 additions & 1 deletion CITATION.bib
Original file line number Diff line number Diff line change
@@ -1,5 +1,23 @@
@misc{circuit-knitting-toolbox,
author = {Luciano Bello and Agata M. Bra\'{n}czyk and Sergey Bravyi and Almudena {Carrera Vazquez} and Andrew Eddins and Daniel J. Egger and Bryce Fuller and Julien Gacon and James R. Garrison and Jennifer R. Glick and Tanvi P. Gujarati and Ikko Hamamura and Areeq I. Hasan and Takashi Imamichi and Caleb Johnson and Ieva Liepuoniute and Owen Lockwood and Mario Motta and C. D. Pemmaraju and Pedro Rivero and Max Rossmannek and Travis L. Scholten and Seetharami Seelam and Iskandar Sitdikov and Dharmashankar Subramanian and Wei Tang and Stefan Woerner},
author = {
Agata M. Bra\'{n}czyk
and Almudena {Carrera Vazquez}
and Daniel J. Egger
and Bryce Fuller
and Julien Gacon
and James R. Garrison
and Jennifer R. Glick
and Caleb Johnson
and Saasha Joshi
and Edwin Pednault
and C. D. Pemmaraju
and Pedro Rivero
and Seetharami Seelam
and Ibrahim Shehzad
and Dharmashankar Subramanian
and Wei Tang
and Stefan Woerner
},
title = {{Circuit Knitting Toolbox}},
howpublished = {\url{https://github.com/Qiskit-Extensions/circuit-knitting-toolbox}},
year = {2023},
Expand Down
42 changes: 27 additions & 15 deletions circuit_knitting/cutting/cut_finding/best_first_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

import heapq
import numpy as np
from typing import TYPE_CHECKING, Callable, cast
from typing import TYPE_CHECKING, Callable, cast, NamedTuple
from itertools import count

from .optimization_settings import OptimizationSettings
Expand All @@ -26,6 +26,21 @@
from .cut_optimization import CutOptimizationFuncArgs


class SearchStats(NamedTuple):
"""NamedTuple for collecting search statistics.
It carries information about the number of states visited
(dequeued from the search queue), the number of next-states generated,
the number of next-states that are enqueued after cost pruning,
and the number of backjumps performed.
"""

states_visited: int
next_states_generated: int
states_enqueued: int
backjumps: int


class BestFirstPriorityQueue:
"""Class that implements priority queues for best-first search.
Expand Down Expand Up @@ -149,6 +164,8 @@ class BestFirstSearch:
``stop_at_first_min`` (Boolean) is a flag that indicates whether or not to
stop the search after the first minimum-cost goal state has been reached.
In the absence of any non-LO QPD assignments, it always makes sense to stop once
the first minimum has been reached and therefore, we set this bool to ``True``.
``max_backjumps`` (int or None) is the maximum number of backjump operations that
can be performed before the search is forced to terminate. None indicates
Expand Down Expand Up @@ -185,7 +202,7 @@ def __init__(
self,
optimization_settings: OptimizationSettings,
search_functions: SearchFunctions,
stop_at_first_min: bool = False,
stop_at_first_min: bool = True,
):
"""Initialize an instance of :class:`BestFirstSearch`.
Expand Down Expand Up @@ -213,7 +230,7 @@ def __init__(
self.num_next_states = 0
self.num_enqueues = 0
self.num_backjumps = 0
self.penultimate_stats: np.typing.NDArray | None = None
self.penultimate_stats: SearchStats | None = None

def initialize(
self,
Expand Down Expand Up @@ -258,7 +275,6 @@ def optimization_pass(
self.mincost_bound = self.mincost_bound_func(*args) # type: ignore

prev_depth = None

while (
self.pqueue.qsize() > 0
and (not self.stop_at_first_min or not self.min_reached)
Expand All @@ -267,7 +283,6 @@ def optimization_pass(
state, depth, cost = self.pqueue.get()

self.update_minimum_reached(cost)

if cost is None or self.cost_bounds_exceeded(cost):
return None, None

Expand Down Expand Up @@ -299,10 +314,10 @@ def minimum_reached(self) -> bool:
"""Return True if the optimization reached a global minimum."""
return self.min_reached

def get_stats(self, penultimate: bool = False) -> np.typing.NDArray[np.int_] | None:
def get_stats(self, penultimate: bool = False) -> SearchStats | None:
"""Return statistics of the search that was performed.
This is a Numpy array containing the number of states visited
This is a NamedTuple containing the number of states visited
(dequeued), the number of next-states generated, the number of
next-states that are enqueued after cost pruning, and the number
of backjumps performed. Return None if no search is performed.
Expand All @@ -312,14 +327,11 @@ def get_stats(self, penultimate: bool = False) -> np.typing.NDArray[np.int_] | N
if penultimate:
return self.penultimate_stats

return np.array(
(
self.num_states_visited,
self.num_next_states,
self.num_enqueues,
self.num_backjumps,
),
dtype=int,
return SearchStats(
states_visited=self.num_states_visited,
next_states_generated=self.num_next_states,
states_enqueued=self.num_enqueues,
backjumps=self.num_backjumps,
)

def get_upperbound_cost(
Expand Down
6 changes: 3 additions & 3 deletions circuit_knitting/cutting/cut_finding/cut_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import numpy as np
from dataclasses import dataclass
from typing import cast
from numpy.typing import NDArray
from .search_space_generator import ActionNames
from .cco_utils import select_search_engine, greedy_best_first_search
from .cutting_actions import disjoint_subcircuit_actions
Expand All @@ -27,6 +26,7 @@
SearchFunctions,
SearchSpaceGenerator,
)
from .best_first_search import SearchStats
from .disjoint_subcircuits_state import DisjointSubcircuitsState
from .circuit_interface import SimpleGateList, GateSpec
from .optimization_settings import OptimizationSettings
Expand Down Expand Up @@ -261,7 +261,7 @@ def __init__(
"CutOptimization",
self.settings,
self.search_funcs,
stop_at_first_min=False,
stop_at_first_min=True,
)
sq.initialize([start_state], self.func_args)

Expand Down Expand Up @@ -299,7 +299,7 @@ def minimum_reached(self) -> bool:
"""
return self.search_engine.minimum_reached()

def get_stats(self, penultimate: bool = False) -> NDArray[np.int_]:
def get_stats(self, penultimate: bool = False) -> SearchStats | None:
"""Return the search-engine statistics.
This is a Numpy array containing the number of states visited
Expand Down
2 changes: 1 addition & 1 deletion circuit_knitting/cutting/cut_finding/cutting_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ def next_state_primitive(
new_state.bell_pairs.append((r2, rnew_2))
new_state.gamma_UB *= 16

new_state.add_action(self, gate_spec, ((1, w1, rnew_1), (2, w2, rnew_2)))
new_state.add_action(self, gate_spec, (1, w1, rnew_1), (2, w2, rnew_2))

return [new_state]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class Action(NamedTuple):

action: DisjointSearchAction
gate_spec: GateSpec
args: list
args: list | tuple


class GateCutLocation(NamedTuple):
Expand Down Expand Up @@ -430,12 +430,12 @@ def add_action(
self,
action_obj: DisjointSearchAction,
gate_spec: GateSpec,
args: tuple | None = None,
*args: tuple,
) -> None:
"""Append the specified action to the list of search-space actions that have been performed."""
if action_obj.get_name() is not None:
self.actions = cast(list, self.actions)
self.actions.append(Action(action_obj, gate_spec, [args]))
self.actions.append(Action(action_obj, gate_spec, args))

def get_search_level(self) -> int:
"""Return the search level."""
Expand Down
9 changes: 3 additions & 6 deletions circuit_knitting/cutting/cut_finding/lo_cuts_optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"""File containing the wrapper class for optimizing LO gate and wire cuts."""
from __future__ import annotations

from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, NamedTuple

from .cut_optimization import CutOptimization
from .cut_optimization import disjoint_subcircuit_actions
Expand All @@ -21,9 +21,6 @@
from .cut_optimization import cut_optimization_min_cost_bound_func
from .cut_optimization import cut_optimization_upper_bound_cost_func
from .search_space_generator import SearchFunctions, SearchSpaceGenerator

import numpy as np
from numpy.typing import NDArray
from .disjoint_subcircuits_state import DisjointSubcircuitsState

if TYPE_CHECKING: # pragma: no cover
Expand Down Expand Up @@ -155,10 +152,10 @@ def get_results(self) -> DisjointSubcircuitsState | None:
"""Return the optimization results."""
return self.best_result

def get_stats(self, penultimate=False) -> dict[str, NDArray[np.int_]]:
def get_stats(self, penultimate=False) -> dict[str, NamedTuple | None]:
"""Return a dictionary containing optimization results.
The value is a Numpy array containing the number of states visited
The value is a NamedTuple containing the number of states visited
(dequeued), the number of next-states generated, the number of
next-states that are enqueued after cost pruning, and the number
of backjumps performed. Return None if no search is performed.
Expand Down
9 changes: 9 additions & 0 deletions circuit_knitting/cutting/cutqc/wire_cutting_evaluation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import copy
from typing import Sequence, Any
from multiprocessing.pool import ThreadPool
from warnings import warn

import numpy as np

Expand Down Expand Up @@ -81,6 +82,14 @@ def run_subcircuit_instances(
options[i % len(options)] for i, _ in enumerate(subcircuits)
]
else:
warn(
"Please provide a list of `backend_names` alongside the `service`. "
"With no backend specified, CutQC defaults to using "
"ibmq_qasm_simulator, but cloud simulators are not expected to be "
"operational past May 15, 2024. For more details, see: "
"https://docs.quantum.ibm.com/announcements/product-updates/2024-03-22-announcement-cloud-simulators-and-lab",
stacklevel=2,
)
backend_names_repeated = ["ibmq_qasm_simulator"] * len(subcircuits)
if options:
options_repeated = [options[0]] * len(subcircuits)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,10 @@
"# Set the Sampler and runtime options\n",
"options = Options(execution={\"shots\": 4000})\n",
"\n",
"# Run 2 parallel qasm simulator threads\n",
"backend_names = [\"ibmq_qasm_simulator\"] * 2"
"# Use the least busy backend for both threads\n",
"backend_names = None\n",
"if service is not None:\n",
" backend_names = [service.least_busy().name] * 2"
]
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,60 +75,6 @@
"circuit.draw(\"mpl\", fold=-1, scale=0.75)"
]
},
{
"cell_type": "markdown",
"id": "461e57e3",
"metadata": {},
"source": [
"## Set up the Qiskit Runtime Service\n",
"\n",
"The Qiskit Runtime Service provides access to IBM Runtime Primitives and quantum backends.\n",
"Alternatively, a local statevector simulator can be used with the Qiskit primitives."
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "5d1fb2ca",
"metadata": {},
"outputs": [],
"source": [
"from qiskit_ibm_runtime import (\n",
" QiskitRuntimeService, # noqa: F401\n",
" Options,\n",
")\n",
"\n",
"# Use local versions of the primitives by default.\n",
"service = None\n",
"\n",
"# Uncomment the following line to instead use Qiskit Runtime.\n",
"# service = QiskitRuntimeService()"
]
},
{
"cell_type": "markdown",
"id": "5fb383d2",
"metadata": {},
"source": [
"The wire cutter tool uses a `Sampler` primitive to evaluate the probabilities of each subcircuit. Here, we configure the options for the Runtime Sampler and specify the backend(s) to be used to evaluate the subcircuits.\n",
"\n",
"If no service was set up, the `backend_names` argument will be ignored, and Qiskit primitives will be used with statevector simulator."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "d409553d",
"metadata": {},
"outputs": [],
"source": [
"# Set the Sampler and runtime options\n",
"options = Options(execution={\"shots\": 4000})\n",
"\n",
"# Run 2 parallel qasm simulator threads\n",
"backend_names = [\"ibmq_qasm_simulator\"] * 2"
]
},
{
"attachments": {
"how-to-manual-cut.png": {
Expand Down Expand Up @@ -244,6 +190,11 @@
"metadata": {},
"outputs": [],
"source": [
"from qiskit_ibm_runtime import (\n",
" QiskitRuntimeService, # noqa: F401\n",
" Options,\n",
")\n",
"\n",
"# Use local versions of the primitives by default.\n",
"service = None\n",
"\n",
Expand Down Expand Up @@ -273,8 +224,10 @@
"# Set the Sampler and runtime options\n",
"options = Options(execution={\"shots\": 4000})\n",
"\n",
"# Run 2 parallel qasm simulator threads\n",
"backend_names = [\"ibmq_qasm_simulator\"] * 2"
"# Use the least busy backend for both threads\n",
"backend_names = None\n",
"if service is not None:\n",
" backend_names = [service.least_busy().name] * 2"
]
},
{
Expand Down
14 changes: 7 additions & 7 deletions docs/circuit_cutting/tutorials/04_automatic_cut_finding.ipynb

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ If you use the Circuit Knitting Toolbox in your research, please cite it accordi
.. literalinclude:: ../CITATION.bib
:language: bibtex

If you are using the entanglement forging tool in CKT version 0.5.0 or earlier, please use `an older version of the citation file <https://github.com/Qiskit-Extensions/circuit-knitting-toolbox/blob/stable/0.5/CITATION.bib>`__ which includes the authors of that tool.

Developer guide
---------------

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
upgrade:
- |
The search engine inside the automated cut-finder has been primed to avoid extraneous searches and is therefore expected to run faster.
Loading

0 comments on commit 73fe385

Please sign in to comment.