-
Notifications
You must be signed in to change notification settings - Fork 603
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
Generate all configurations for a fragment tape #2169
Changes from 121 commits
3274339
df01372
bef8586
7e3f493
f441f10
d32b030
5c7dccd
56d0b4b
1c747cd
2542b74
c1c5dc5
b886815
24b6765
d054a6b
4a1ab6e
3b13808
24af86e
58cabdf
701b9d1
f66bceb
f78d462
1364568
a78e07a
ecf0d57
8df6d3f
b61d975
557eaa4
5b6e47e
b51dff1
5591931
699f639
c3c6df6
43d04a8
831d3b3
514e5ba
22a8ff5
7cd2af7
6861e9e
b1b8573
fd7c53a
844acb5
616022a
dc06d6a
76befee
755663e
4eda405
0489867
a130fde
b511c18
b5d5ba6
535e5e0
036dea8
8495fff
cecc6fc
ed15a26
3bc3ae9
0dd34f1
44fed02
a7c0496
b5ace5f
3036c58
39679e8
69c2891
0996e08
7cc03b2
817afda
ff7509e
1de8674
e1154bc
501c429
fd09982
7492d39
5bcddbe
c6232ab
25bdfe3
74ff942
c190f19
694cd51
0b47303
b4ea9a8
8ae0ae2
2c39ca9
5efa006
0bd46e2
b717706
8f83a56
922bf6f
6b5e073
92808e5
81eb088
2e7ca12
50a0785
11d7440
453b807
8fbea30
aa40cda
e571609
c335fea
e48dc3f
6fb65ac
1921452
915f6c6
9e9551a
66f9d98
2af6282
9819c62
270d32d
6fcdc17
180daf0
593e4d7
7907b8f
80361c5
bea35f3
9b38be5
52cfc45
308631e
2def3d2
70c88e8
cf7c435
c96a251
efaa052
be99aef
cff1237
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,14 +18,15 @@ | |
|
||
import copy | ||
import string | ||
from typing import Sequence, Tuple | ||
|
||
from networkx import MultiDiGraph, weakly_connected_components | ||
from itertools import product | ||
from typing import List, Sequence, Tuple | ||
|
||
import pennylane as qml | ||
from pennylane import apply | ||
from networkx import MultiDiGraph, weakly_connected_components | ||
from pennylane import Hadamard, Identity, PauliX, S, apply, expval | ||
from pennylane.grouping import string_to_pauli_word | ||
from pennylane.measure import MeasurementProcess | ||
from pennylane.operation import Operation, Operator, Tensor | ||
from pennylane.operation import Expectation, Operation, Operator, Tensor | ||
from pennylane.ops.qubit.non_parametric_ops import WireCut | ||
from pennylane.tape import QuantumTape | ||
from pennylane.wires import Wires | ||
|
@@ -364,6 +365,174 @@ def graph_to_tape(graph: MultiDiGraph) -> QuantumTape: | |
return tape | ||
|
||
|
||
def _get_measurements( | ||
group: Sequence[Operator], measurements: Sequence[MeasurementProcess] | ||
) -> List[MeasurementProcess]: | ||
"""Pairs each observable in ``group`` with the fixed circuit ``measurements``. | ||
|
||
Only a single fixed measurement of an expectation value is currently supported. | ||
anthayes92 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Args: | ||
group (Sequence[Operator]): a collection of qubit-wise commuting observables | ||
anthayes92 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
measurements (Sequence[MeasurementProcess]): fixed circuit measurements | ||
anthayes92 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
Returns: | ||
List[MeasurementProcess]: the expectation values of ``g @ obs``, where ``g`` is iterated | ||
over ``group`` and ``obs`` is the observable composing the single fixed circuit measurement | ||
anthayes92 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
in ``measurements`` | ||
""" | ||
n_measurements = len(measurements) | ||
if n_measurements > 1: | ||
raise ValueError( | ||
"The circuit cutting workflow only supports circuits with a single output " | ||
"measurement" | ||
) | ||
if n_measurements == 0: | ||
return [expval(g) for g in group] | ||
|
||
measurement = measurements[0] | ||
|
||
if measurement.return_type is not Expectation: | ||
raise ValueError( | ||
"The circuit cutting workflow only supports circuits with expectation " | ||
"value measurements" | ||
) | ||
|
||
obs = measurement.obs | ||
|
||
return [expval(obs @ g) for g in group] | ||
Comment on lines
+385
to
+404
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @anthayes92 - this is a simplified version of what we had before. I recall some of the complexity of the prototype implementation came from a potential bug in PennyLane where the order of observables composing a tensor product resulted in different results. I propose that, for now, we keep the simple version above and see if the potential bug surfaces as we add some integration tests. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the addition and the explanation! This sounds like a sensible approach to me. Is this bug known and was there an issue raised for the bug? The above seems to work well with the current unit tests 👌 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Trying to remember back - I wasn't sure exactly what was going on, and it could have been a bug in PL or in the prototype implementation. But yes I wouldn't say it's a well defined bug at this stage, and there isn't an issue. I'm sure we'll find it though when we get to the integration tests. |
||
|
||
|
||
def _prep_zero_state(wire): | ||
Identity(wire) | ||
|
||
|
||
def _prep_one_state(wire): | ||
PauliX(wire) | ||
|
||
|
||
def _prep_plus_state(wire): | ||
Hadamard(wire) | ||
|
||
|
||
def _prep_iplus_state(wire): | ||
Hadamard(wire) | ||
S(wires=wire) | ||
|
||
|
||
PREPARE_SETTINGS = [_prep_zero_state, _prep_one_state, _prep_plus_state, _prep_iplus_state] | ||
|
||
|
||
def expand_fragment_tapes( | ||
tape: QuantumTape, | ||
) -> Tuple[List[QuantumTape], List[PrepareNode], List[MeasureNode]]: | ||
""" | ||
Expands a fragment tape into a collection of tapes for each configuration of | ||
anthayes92 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
:class:`MeasureNode` and :class:`PrepareNode` operations. | ||
|
||
.. note:: | ||
|
||
This function is designed for use as part of the circuit cutting workflow. Check out the | ||
:doc:`transforms </code/qml_transforms>` page for more details. | ||
|
||
Args: | ||
tape (QuantumTape): the fragment tape to be expanded. | ||
|
||
Returns: | ||
Tuple[List[QuantumTape], List[PrepareNode], List[MeasureNode]]: the | ||
tapes corresponding to each configuration, the preparation nodes and | ||
the measurement nodes. | ||
|
||
**Example** | ||
|
||
Consider the following circuit, which contains a :class:`~.MeasureNode` and :class:`~.PrepareNode` | ||
operation: | ||
|
||
.. code-block:: python | ||
|
||
from pennylane.transforms import qcut | ||
|
||
with qml.tape.QuantumTape() as tape: | ||
qcut.PrepareNode(wires=0) | ||
qml.RX(0.5, wires=0) | ||
qcut.MeasureNode(wires=0) | ||
|
||
We can expand over the measurement and preparation nodes using: | ||
|
||
.. code-block:: python | ||
|
||
>>> tapes, prep, meas = qml.transforms.expand_fragment_tapes(tape) | ||
>>> for t in tapes: | ||
... print(t.draw()) | ||
0: ──I──RX(0.5)──┤ ⟨I⟩ ┤ ⟨Z⟩ | ||
|
||
0: ──I──RX(0.5)──┤ ⟨X⟩ | ||
|
||
0: ──I──RX(0.5)──┤ ⟨Y⟩ | ||
|
||
0: ──X──RX(0.5)──┤ ⟨I⟩ ┤ ⟨Z⟩ | ||
|
||
0: ──X──RX(0.5)──┤ ⟨X⟩ | ||
|
||
0: ──X──RX(0.5)──┤ ⟨Y⟩ | ||
|
||
0: ──H──RX(0.5)──┤ ⟨I⟩ ┤ ⟨Z⟩ | ||
|
||
0: ──H──RX(0.5)──┤ ⟨X⟩ | ||
|
||
0: ──H──RX(0.5)──┤ ⟨Y⟩ | ||
|
||
0: ──H──S──RX(0.5)──┤ ⟨I⟩ ┤ ⟨Z⟩ | ||
|
||
0: ──H──S──RX(0.5)──┤ ⟨X⟩ | ||
|
||
0: ──H──S──RX(0.5)──┤ ⟨Y⟩ | ||
""" | ||
prepare_nodes = [o for o in tape.operations if isinstance(o, PrepareNode)] | ||
measure_nodes = [o for o in tape.operations if isinstance(o, MeasureNode)] | ||
|
||
wire_map = {mn.wires[0]: i for i, mn in enumerate(measure_nodes)} | ||
|
||
n_meas = len(measure_nodes) | ||
if n_meas >= 1: | ||
measure_combinations = qml.grouping.partition_pauli_group(len(measure_nodes)) | ||
else: | ||
measure_combinations = [[""]] | ||
Comment on lines
+497
to
+501
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should update this when we merge in #2192 |
||
|
||
tapes = [] | ||
|
||
for prepare_settings in product(range(len(PREPARE_SETTINGS)), repeat=len(prepare_nodes)): | ||
for measure_group in measure_combinations: | ||
if n_meas >= 1: | ||
group = [ | ||
string_to_pauli_word(paulis, wire_map=wire_map) for paulis in measure_group | ||
] | ||
else: | ||
group = [] | ||
|
||
prepare_mapping = { | ||
n: PREPARE_SETTINGS[s] for n, s in zip(prepare_nodes, prepare_settings) | ||
} | ||
|
||
with QuantumTape() as tape_: | ||
for op in tape.operations: | ||
if isinstance(op, PrepareNode): | ||
w = op.wires[0] | ||
prepare_mapping[op](w) | ||
elif not isinstance(op, MeasureNode): | ||
apply(op) | ||
|
||
with qml.tape.stop_recording(): | ||
measurements = _get_measurements(group, tape.measurements) | ||
|
||
for meas in measurements: | ||
apply(meas) | ||
|
||
tapes.append(tape_) | ||
|
||
return tapes, prepare_nodes, measure_nodes | ||
|
||
|
||
def _get_symbol(i): | ||
"""Finds the i-th ASCII symbol. Works for lowercase and uppercase letters, allowing i up to | ||
51.""" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is a "fixed" circuit measurement, fixed as opposed to... ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed as in the measurements of the original circuit, rather than the product of Pauli terms