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

Add cirq.toggle_tags helper to apply transformers on specific subsets of operations in a circuit #4973

Merged
merged 3 commits into from
Feb 10, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
1 change: 1 addition & 0 deletions cirq-core/cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,7 @@
unroll_circuit_op,
unroll_circuit_op_greedy_earliest,
unroll_circuit_op_greedy_frontier,
xor_ops_with_tags,
)

from cirq.qis import (
Expand Down
1 change: 1 addition & 0 deletions cirq-core/cirq/transformers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,5 @@
unroll_circuit_op,
unroll_circuit_op_greedy_earliest,
unroll_circuit_op_greedy_frontier,
xor_ops_with_tags,
)
34 changes: 34 additions & 0 deletions cirq-core/cirq/transformers/align_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,40 @@ def test_align_left_no_compile_context():
)


def test_align_left_subset_of_operations():
q1 = cirq.NamedQubit('q1')
q2 = cirq.NamedQubit('q2')
tag = "op_to_align"
c_orig = cirq.Circuit(
[
cirq.Moment([cirq.Y(q1)]),
cirq.Moment([cirq.X(q2)]),
cirq.Moment([cirq.X(q1).with_tags(tag)]),
cirq.Moment([cirq.Y(q2)]),
cirq.measure(*[q1, q2], key='a'),
]
)
c_exp = cirq.Circuit(
[
cirq.Moment([cirq.Y(q1)]),
cirq.Moment([cirq.X(q1).with_tags(tag), cirq.X(q2)]),
cirq.Moment(),
cirq.Moment([cirq.Y(q2)]),
cirq.measure(*[q1, q2], key='a'),
]
)
cirq.testing.assert_same_circuits(
cirq.xor_ops_with_tags(
cirq.align_left(
cirq.xor_ops_with_tags(c_orig, [tag]),
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is going on here? Is the xor a necessary step for alignment, or just a test element to verify proper behavior of align_left?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So in the example here, directly doing an align_left on the entire circuit would cause both X(q1) and Y(q2) in moments 2 & 3 (0-based indexing) to move left. But by adding a tag to X(q1) and using xor_ops_with_tags to toggle tags --> do optimization --> toggle tags again, we are able to do the transformation only the subset of operations that are tagged -- therefore only X(q1) aligns to the left but Y(q2) stays in-place.

context=cirq.TransformerContext(tags_to_ignore=[tag]),
),
[tag],
),
c_exp,
)


def test_align_right_no_compile_context():
q1 = cirq.NamedQubit('q1')
q2 = cirq.NamedQubit('q2')
Expand Down
15 changes: 15 additions & 0 deletions cirq-core/cirq/transformers/transformer_primitives.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,3 +426,18 @@ def unroll_circuit_op_greedy_frontier(
)
frontier = unrolled_circuit.insert_at_frontier(sub_circuit.all_operations(), idx, frontier)
return _to_target_circuit_type(unrolled_circuit, circuit)


def xor_ops_with_tags(circuit: CIRCUIT_TYPE, tags: Sequence[Hashable], *, deep: bool = False):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add a function docstring.

Also, I'm not sure about this name - we're not xor'ing ops, we're xor'ing their tags. Maybe (as used in the test) "flip_tags" or "toggle_tags" would be clearer?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah, naming this method is hard. changed to toggle_tags and added a docstring

tags_to_xor = set(tags)

def map_func(op: 'cirq.Operation', _) -> 'cirq.Operation':
op_tags = set(op.tags)
new_tags = (op_tags - tags_to_xor) | (tags_to_xor - op_tags)
tanujkhattar marked this conversation as resolved.
Show resolved Hide resolved
return (
op
if deep and isinstance(op, circuits.CircuitOperation)
else op.untagged.with_tags(*new_tags)
)

return map_operations(circuit, map_func, deep=deep)
16 changes: 16 additions & 0 deletions cirq-core/cirq/transformers/transformer_primitives_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,22 @@ def test_map_operations_respects_tags_to_ignore():
)


def test_apply_tag_to_inverted_op_set():
q = cirq.LineQubit.range(2)
op = cirq.CNOT(*q)
tag = "tag_to_flip"
c_orig = cirq.Circuit(op, op.with_tags(tag), cirq.CircuitOperation(cirq.FrozenCircuit(op)))
c_flipped_with_deep = cirq.Circuit(
op.with_tags(tag), op, cirq.CircuitOperation(cirq.FrozenCircuit(op.with_tags(tag)))
)
c_flipped_without_deep = cirq.Circuit(
op.with_tags(tag), op, cirq.CircuitOperation(cirq.FrozenCircuit(op)).with_tags(tag)
)
for c_flip, deep in zip([c_flipped_with_deep, c_flipped_without_deep], [True, False]):
tanujkhattar marked this conversation as resolved.
Show resolved Hide resolved
cirq.testing.assert_same_circuits(cirq.xor_ops_with_tags(c_orig, [tag], deep=deep), c_flip)
cirq.testing.assert_same_circuits(cirq.xor_ops_with_tags(c_flip, [tag], deep=deep), c_orig)


def test_unroll_circuit_op_and_variants():
q = cirq.LineQubit.range(2)
c = cirq.Circuit(cirq.X(q[0]), cirq.CNOT(q[0], q[1]), cirq.X(q[0]))
Expand Down