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

Provide options in the cut-finder API to turn LO gate and wire cut finding off or on, expose min-reached flag. #586

Merged
merged 25 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b0809c7
enable only wire cut finding
ibrahim-shehzad May 10, 2024
d2396e2
edit tests
ibrahim-shehzad May 10, 2024
d064584
explore adding new flags
ibrahim-shehzad May 10, 2024
98c693a
Handle multiple arguments when cutting both wires
ibrahim-shehzad May 10, 2024
21c35cf
black, mypy, remove the erroneous example I added to the tutorial.
ibrahim-shehzad May 10, 2024
b581a18
doc string
ibrahim-shehzad May 10, 2024
381bbf7
update doc string
ibrahim-shehzad May 13, 2024
422ee7d
update tests
ibrahim-shehzad May 14, 2024
f784d9b
reorganise tests
ibrahim-shehzad May 14, 2024
0ffcc5f
add cut both wires test
ibrahim-shehzad May 14, 2024
9f03bea
Merge branch 'main' into wire-cut-finder
ibrahim-shehzad May 14, 2024
8748a1c
add release note
ibrahim-shehzad May 14, 2024
7fcfcb1
edit release note
ibrahim-shehzad May 14, 2024
2a486ad
un-expose LOCC cost functions everywhere
ibrahim-shehzad May 23, 2024
6791391
pull type hint change from main
ibrahim-shehzad May 23, 2024
5ae9106
add min reached flag.
ibrahim-shehzad May 23, 2024
0bd4250
edit release note
ibrahim-shehzad May 23, 2024
4d6c264
Merge branch 'main' into wire-cut-finder
ibrahim-shehzad May 23, 2024
f041356
Change to upper case in doc string
ibrahim-shehzad May 23, 2024
c8b7858
Italicize
ibrahim-shehzad May 29, 2024
6b04f2a
Edit bool in release note
ibrahim-shehzad May 29, 2024
645c58d
Update reference in release note
ibrahim-shehzad May 29, 2024
3517b31
Edit reference in release note
ibrahim-shehzad May 29, 2024
53deb86
change flag to minimum reached
ibrahim-shehzad May 30, 2024
4c4d4bb
pull changes
ibrahim-shehzad May 30, 2024
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: 5 additions & 1 deletion circuit_knitting/cutting/automated_cut_finding.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ def find_cuts(
seed=optimization.seed,
max_gamma=optimization.max_gamma,
max_backjumps=optimization.max_backjumps,
gate_lo=optimization.gate_lo,
wire_lo=optimization.wire_lo,
)

# Hard-code the optimizer to an LO-only optimizer
Expand Down Expand Up @@ -106,7 +108,7 @@ def find_cuts(
)
counter += 1

if action.action.get_name() == "CutBothWires": # pragma: no cover
if action.action.get_name() == "CutBothWires":
# There should be two wires specified in the action in this case
assert len(action.args) == 2
qubit_id2 = action.args[1][0] - 1
Expand Down Expand Up @@ -137,6 +139,8 @@ class OptimizationParameters:
seed: int | None = OptimizationSettings().seed
max_gamma: float = OptimizationSettings().max_gamma
max_backjumps: None | int = OptimizationSettings().max_backjumps
gate_lo: bool = OptimizationSettings().gate_lo
wire_lo: bool = OptimizationSettings().wire_lo
Comment on lines +147 to +148
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One might want to investigate what happens when one sets both gate_lo and gate_lo to False. Ideally this will result in an informative error message (right?).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, for sure.

Copy link
Member

@garrison garrison May 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I want to acknowledge somewhere (i.e., in this comment) that we'll probably change the name of gate_lo eventually. since I doubt it makes any sense to implement LOCC gate cutting now that https://arxiv.org/abs/2312.11638 is out. I cannot think of a better name right this moment. The proper names will depend how far we get implementing parallel and black box cutting from that paper.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have gone ahead and added some of the updates that we discussed (7fcfcb1).



@dataclass
Expand Down
11 changes: 8 additions & 3 deletions circuit_knitting/cutting/cut_finding/cut_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,16 @@ def cut_optimization_cost_func(


def cut_optimization_upper_bound_cost_func(
goal_state, func_args: CutOptimizationFuncArgs
goal_state: DisjointSubcircuitsState, func_args: CutOptimizationFuncArgs
) -> tuple[float, float]:
"""Return the value of :math:`gamma` computed assuming all LO cuts."""
# pylint: disable=unused-argument
return (goal_state.upper_bound_gamma(), np.inf)
if goal_state is not None:
return (goal_state.upper_bound_gamma(), np.inf)
else:
raise ValueError(
"None state encountered: no cut state satisfying the specified constraints and settings could be found."
)


def cut_optimization_min_cost_bound_func(
Expand Down Expand Up @@ -125,7 +130,7 @@ def cut_optimization_goal_state_func(
# Global variable that holds the search-space functions for generating
# the cut optimization search space.
cut_optimization_search_funcs = SearchFunctions(
cost_func=cut_optimization_cost_func,
cost_func=cut_optimization_upper_bound_cost_func, # valid choice when considering only LO cuts.
upperbound_cost_func=cut_optimization_upper_bound_cost_func,
next_state_func=cut_optimization_next_state_func,
goal_state_func=cut_optimization_goal_state_func,
Expand Down
24 changes: 13 additions & 11 deletions circuit_knitting/cutting/cut_finding/disjoint_subcircuits_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,20 @@ class Action(NamedTuple):
args: list | tuple


class GateCutLocation(NamedTuple):
"""Named tuple for specification of gate cut location."""
class CutLocation(NamedTuple):
"""Named tuple for specifying cut locations.

This is used to specify instances of both :class:`CutTwoQubitGate` and :class:`CutBothWires`.
Both of these instances are fully specified by a gate reference.
"""

instruction_id: int
gate_name: str
qubits: Sequence


class WireCutLocation(NamedTuple):
"""Named tuple for specification of wire cut location.
"""Named tuple for specification of (single) wire cut locations.

Wire cuts are identified through the gates whose input wires are cut.
"""
Expand All @@ -64,10 +68,10 @@ class CutIdentifier(NamedTuple):
"""Named tuple for specification of location of :class:`CutTwoQubitGate` or :class:`CutBothWires` instances."""

cut_action: DisjointSearchAction
gate_cut_location: GateCutLocation
cut_location: CutLocation


class OneWireCutIdentifier(NamedTuple):
class SingleWireCutIdentifier(NamedTuple):
"""Named tuple for specification of location of :class:`CutLeftWire` or :class:`CutRightWire` instances."""

cut_action: DisjointSearchAction
Expand Down Expand Up @@ -130,15 +134,13 @@ def __init__(self, num_qubits: int | None = None, max_wire_cuts: int | None = No
if not (
num_qubits is None or (isinstance(num_qubits, int) and num_qubits >= 0)
):
raise ValueError("num_qubits must be either be None or a positive integer.")
raise ValueError("num_qubits must either be None or a positive integer.")

if not (
max_wire_cuts is None
or (isinstance(max_wire_cuts, int) and max_wire_cuts >= 0)
):
raise ValueError(
"max_wire_cuts must be either be None or a positive integer."
)
raise ValueError("max_wire_cuts must either be None or a positive integer.")

if num_qubits is None or max_wire_cuts is None:
self.wiremap: NDArray[np.int_] | None = None
Expand Down Expand Up @@ -213,7 +215,7 @@ def cut_actions_sublist(self) -> list[NamedTuple]:
for i in range(len(cut_actions)):
if cut_actions[i].action.get_name() in ("CutLeftWire", "CutRightWire"):
self.cut_actions_list.append(
OneWireCutIdentifier(
SingleWireCutIdentifier(
cut_actions[i].action.get_name(),
WireCutLocation(
cut_actions[i].gate_spec.instruction_id,
Expand All @@ -231,7 +233,7 @@ def cut_actions_sublist(self) -> list[NamedTuple]:
self.cut_actions_list.append(
CutIdentifier(
cut_actions[i].action.get_name(),
GateCutLocation(
CutLocation(
cut_actions[i].gate_spec.instruction_id,
cut_actions[i].gate_spec.gate.name,
cut_actions[i].gate_spec.gate.qubits,
Expand Down
38 changes: 20 additions & 18 deletions circuit_knitting/cutting/cut_finding/optimization_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,18 @@ class OptimizationSettings:
If None is used as the random seed, then a seed is obtained using an
operating-system call to achieve an unrepeatable randomized initialization.

NOTE: The current release only supports LO gate and wire cuts. LOCC
NOTE: The current release only supports LO gate and wire cuts. locc
flags have been incorporated with an eye towards future releases.
"""

max_gamma: float = 1024
max_backjumps: None | int = 10000
seed: int | None = None
LO: bool = True
LOCC_ancillas: bool = False
LOCC_no_ancillas: bool = False
gate_lo: bool = True
wire_lo: bool = True
gate_locc_ancillas: bool = False
wire_locc_ancillas: bool = False
wire_locc_no_ancillas: bool = False
engine_selections: dict[str, str] | None = None

def __post_init__(self):
Expand All @@ -57,12 +59,12 @@ def __post_init__(self):
if self.max_backjumps is not None and self.max_backjumps < 0:
raise ValueError("max_backjumps must be a positive semi-definite integer.")

self.gate_cut_LO = self.LO
self.gate_cut_LOCC_with_ancillas = self.LOCC_ancillas
self.gate_cut_lo = self.gate_lo
self.gate_cut_locc_with_ancillas = self.gate_locc_ancillas

self.wire_cut_LO = self.LO
self.wire_cut_LOCC_with_ancillas = self.LOCC_ancillas
self.wire_cut_LOCC_no_ancillas = self.LOCC_no_ancillas
self.wire_cut_lo = self.wire_lo
self.wire_cut_locc_with_ancillas = self.wire_locc_ancillas
self.wire_cut_locc_no_ancillas = self.wire_locc_no_ancillas
if self.engine_selections is None:
self.engine_selections = {"CutOptimization": "BestFirst"}

Expand Down Expand Up @@ -102,31 +104,31 @@ def set_gate_cut_types(self) -> None:
The default is to only include LO gate cuts, which are the
only cut types supported in this release.
"""
self.gate_cut_LO = self.LO
self.gate_cut_LOCC_with_ancillas = self.LOCC_ancillas
self.gate_cut_lo = self.gate_lo
self.gate_cut_locc_with_ancillas = self.gate_locc_ancillas

def set_wire_cut_types(self) -> None:
"""Select which wire-cut types to include in the optimization.

The default is to only include LO wire cuts, which are the
only cut types supported in this release.
"""
self.wire_cut_LO = self.LO
self.wire_cut_LOCC_with_ancillas = self.LOCC_ancillas
self.wire_cut_LOCC_no_ancillas = self.LOCC_no_ancillas
self.wire_cut_lo = self.wire_lo
self.wire_cut_locc_with_ancillas = self.wire_locc_ancillas
self.wire_cut_locc_no_ancillas = self.wire_locc_no_ancillas

def get_cut_search_groups(self) -> list[None | str]:
"""Return a list of action groups to include in the optimization."""
out: list
out = [None]

if self.gate_cut_LO or self.gate_cut_LOCC_with_ancillas:
if self.gate_cut_lo or self.gate_cut_locc_with_ancillas:
out.append("GateCut")

if (
self.wire_cut_LO
or self.wire_cut_LOCC_with_ancillas
or self.wire_cut_LOCC_no_ancillas
self.wire_cut_lo
or self.wire_cut_locc_with_ancillas
or self.wire_cut_locc_no_ancillas
):
out.append("WireCut")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.0"
"version": "3.9.6"
}
},
"nbformat": 4,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
features:
- |
When specifying instances of :class:`~OptimizationParameters` that are inputted to :meth:`circuit_knitting.cutting.find_cuts()`, the user can now control whether the
cut-finder looks only for gate cuts, only for wire cuts, or both, by setting the bools ``gate_lo`` and ``wire_lo`` appropriately. The default value
of both of these is set to `True` and so the default search considers the possibility of both gate and wire cuts.
3 changes: 2 additions & 1 deletion test/cutting/cut_finding/test_best_first_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,11 @@ def test_best_first_search(test_circuit: SimpleGateList):

out, _ = op.optimization_pass()

print(get_actions_list(out.actions))
assert op.search_engine.get_stats(penultimate=True) is not None
assert op.search_engine.get_stats() is not None
assert op.get_upperbound_cost() == (27, inf)
assert op.minimum_reached() is False
assert op.minimum_reached() is True
assert out is not None
assert (out.lower_bound_gamma(), out.gamma_UB, out.get_max_width()) == (
27,
Expand Down
Loading