Skip to content

Commit

Permalink
Improves semantics of Circuit.findall_operations_until_blocked (#1916)
Browse files Browse the repository at this point in the history
* Improves semantics of Circuit.findall_operations_until_blocked

Now takes into account blocking operations on qubits in the 'light cone'
of the start frontier.
  • Loading branch information
bryano committed Feb 17, 2020
1 parent 6a4fbfb commit 37a222f
Show file tree
Hide file tree
Showing 2 changed files with 374 additions and 70 deletions.
124 changes: 96 additions & 28 deletions cirq/circuits/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
from itertools import groupby
import math

from typing import (List, Any, Dict, FrozenSet, Callable, Iterable, Iterator,
Optional, Sequence, Union, Set, Type, Tuple, cast, TypeVar,
overload, TYPE_CHECKING)
from typing import (Any, Callable, cast, Dict, FrozenSet, Iterable, Iterator,
List, Optional, overload, Sequence, Set, Tuple, Type,
TYPE_CHECKING, TypeVar, Union)

import re
import numpy as np
Expand Down Expand Up @@ -748,16 +748,41 @@ def findall_operations_until_blocked(
is_blocker: Callable[['cirq.Operation'], bool] = lambda op: False
) -> List[Tuple[int, ops.Operation]]:
"""
Finds all operations until a blocking operation is hit. This returns
a list of all operations from the starting frontier until a blocking
operation is encountered. An operation is part of the list if
it is involves a qubit in the start_frontier dictionary, comes after
the moment listed in that dictionary, and before any blocking
operations that involve that qubit. Operations are only considered
to be blocking the qubits that they operate on, so a blocking operation
that does not operate on any qubit in the starting frontier is not
actually considered blocking. See `reachable_frontier_from` for a more
in depth example of reachable states.
Finds all operations until a blocking operation is hit.
An operation is considered blocking if
a) It is in the 'light cone' of start_frontier.
AND
(
1) is_blocker returns a truthy value.
OR
2) It acts on a blocked qubit.
)
Every qubit acted on by a blocking operation is thereafter itself
blocked.
The notion of reachability here differs from that in
reachable_frontier_from in two respects:
1) An operation is not considered blocking only because it is in a
moment before the start_frontier of one of the qubits on which it
acts.
2) Operations that act on qubits not in start_frontier are not
automatically blocking.
For every (moment_index, operation) returned:
1) moment_index >= min((start_frontier[q] for q in operation.qubits
if q in start_frontier), default=0)
2) set(operation.qubits).intersection(start_frontier)
Args:
start_frontier: A starting set of reachable locations.
Expand All @@ -771,25 +796,68 @@ def findall_operations_until_blocked(
the start frontier and a blocking operation. The first item of
each tuple is the index of the moment containing the operation,
and the second item is the operation itself.
Below are some examples, where on the left the opening parentheses show
`start_frontier` and on the right are the operations included (with
their moment indices) in the output. `F` and `T` indicate that
`is_blocker` return `False` or `True`, respectively, when applied to
the gates; `M` indicates that it doesn't matter.
─(─F───F─────── ┄(─F───F─)┄┄┄┄┄
│ │ │ │
─(─F───F───T─── => ┄(─F───F─)┄┄┄┄┄
│ ┊
───────────T─── ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
───M─────(─F─── ┄┄┄┄┄┄┄┄┄(─F─)┄┄
│ │ ┊ │
───M───M─(─F─── ┄┄┄┄┄┄┄┄┄(─F─)┄┄
│ => ┊
───────M───M─── ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
│ ┊
───────────M─── ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
───M─(─────M─── ┄┄┄┄┄()┄┄┄┄┄┄┄┄
│ │ ┊ ┊
───M─(─T───M─── ┄┄┄┄┄()┄┄┄┄┄┄┄┄
│ => ┊
───────T───M─── ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
│ ┊
───────────M─── ┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
─(─F───F─── ┄(─F───F─)┄
│ │ => │ │
───F─(─F─── ┄(─F───F─)┄
─(─F─────────── ┄(─F─)┄┄┄┄┄┄┄┄┄
│ │
───F───F─────── ┄(─F─)┄┄┄┄┄┄┄┄┄
│ => ┊
───────F───F─── ┄┄┄┄┄┄┄┄┄(─F─)┄
│ │
─(─────────F─── ┄┄┄┄┄┄┄┄┄(─F─)┄
"""
op_list = [] # type: List[Tuple[int, ops.Operation]]
frontier = dict(start_frontier)
if not frontier:
if not start_frontier:
return op_list
index = min(frontier.values())
for moment in self._moments[index:]:
active_qubits = set(q for q, s in frontier.items() if s <= index)
if len(active_qubits) <= 0:
return op_list
start_index = min(start_frontier.values())
blocked_qubits = set() # type: Set[cirq.Qid]
for index, moment in enumerate(self[start_index:], start_index):
active_qubits = set(
q for q, s in start_frontier.items() if s <= index)
for op in moment.operations:
active_op_qubits = active_qubits.intersection(op.qubits)
if active_op_qubits:
if is_blocker(op):
for q in active_op_qubits:
del frontier[q]
else:
op_list.append((index, op))
index += 1
if is_blocker(op) or blocked_qubits.intersection(op.qubits):
blocked_qubits.update(op.qubits)
elif active_qubits.intersection(op.qubits):
op_list.append((index, op))
if blocked_qubits.issuperset(start_frontier):
break
return op_list

def operation_at(self, qubit: 'cirq.Qid',
Expand Down

0 comments on commit 37a222f

Please sign in to comment.