Skip to content

Commit 6d50406

Browse files
committed
Add a symbolize transformer
Symbolizes single qubit gates by tags.
1 parent de37106 commit 6d50406

File tree

4 files changed

+146
-0
lines changed

4 files changed

+146
-0
lines changed

cirq-core/cirq/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@
393393
single_qubit_matrix_to_phxz as single_qubit_matrix_to_phxz,
394394
single_qubit_op_to_framed_phase_form as single_qubit_op_to_framed_phase_form,
395395
stratified_circuit as stratified_circuit,
396+
symbolize_single_qubit_gates_by_indexed_tags as symbolize_single_qubit_gates_by_indexed_tags,
396397
synchronize_terminal_measurements as synchronize_terminal_measurements,
397398
TRANSFORMER as TRANSFORMER,
398399
TransformerContext as TransformerContext,

cirq-core/cirq/transformers/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,10 @@
133133
unroll_circuit_op_greedy_frontier as unroll_circuit_op_greedy_frontier,
134134
)
135135

136+
from cirq.transformers.symbolize import (
137+
symbolize_single_qubit_gates_by_indexed_tags as symbolize_single_qubit_gates_by_indexed_tags,
138+
)
139+
136140
from cirq.transformers.gauge_compiling import (
137141
CZGaugeTransformer as CZGaugeTransformer,
138142
ConstantGauge as ConstantGauge,
+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Copyright 2025 The Cirq Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import re
16+
from typing import Hashable, Optional, TYPE_CHECKING
17+
18+
import sympy
19+
20+
from cirq import ops
21+
from cirq.transformers import transformer_api, transformer_primitives
22+
23+
if TYPE_CHECKING:
24+
import cirq
25+
26+
27+
@transformer_api.transformer
28+
def symbolize_single_qubit_gates_by_indexed_tags(
29+
circuit: 'cirq.AbstractCircuit',
30+
*,
31+
context: Optional['cirq.TransformerContext'] = None,
32+
tag_prefix: Optional[str] = "TO-PHXZ",
33+
) -> 'cirq.Circuit':
34+
"""Symbolizes single qubit operations by indexed tags prefixed by tag_prefix.
35+
36+
Example:
37+
>>> q0, q1 = cirq.LineQubit.range(2)
38+
>>> c = cirq.Circuit(\
39+
cirq.X(q0).with_tags("phxz_0"),\
40+
cirq.CZ(q0,q1),\
41+
cirq.Y(q0).with_tags("phxz_1"),\
42+
cirq.X(q0))
43+
>>> print(c)
44+
0: ───X[phxz_0]───@───Y[phxz_1]───X───
45+
46+
1: ───────────────@───────────────────
47+
>>> new_circuit = cirq.symbolize_single_qubit_gates_by_indexed_tags(\
48+
c, tag_prefix="phxz")
49+
>>> print(new_circuit)
50+
0: ───PhXZ(a=a0,x=x0,z=z0)───@───PhXZ(a=a1,x=x1,z=z1)───X───
51+
52+
1: ──────────────────────────@──────────────────────────────
53+
54+
Args:
55+
circuit: Input circuit to apply the transformations on. The input circuit is not mutated.
56+
context: `cirq.TransformerContext` storing common configurable options for transformers.
57+
tag_prefix: The prefix of the tag.
58+
59+
Returns:
60+
Copy of the transformed input circuit.
61+
"""
62+
63+
def _map_func(op: 'cirq.Operation', _):
64+
"""Maps an op with tag `{tag_prefix}_i` to a symbolzied `PhasedXZGate(xi,zi,ai)`."""
65+
tags: set[Hashable] = set(op.tags)
66+
tag_id: None | int = None
67+
for tag in tags:
68+
if re.fullmatch(f"{tag_prefix}_\\d+", str(tag)):
69+
if tag_id is None:
70+
tag_id = int(str(tag).rsplit("_", maxsplit=-1)[-1])
71+
else:
72+
raise ValueError(f"Multiple tags are prefixed with {tag_prefix}.")
73+
if tag_id is None:
74+
return op
75+
tags.remove(f"{tag_prefix}_{tag_id}")
76+
phxz_params = {
77+
"x_exponent": sympy.Symbol(f"x{tag_id}"),
78+
"z_exponent": sympy.Symbol(f"z{tag_id}"),
79+
"axis_phase_exponent": sympy.Symbol(f"a{tag_id}"),
80+
}
81+
82+
return ops.PhasedXZGate(**phxz_params).on(*op.qubits).with_tags(*tags)
83+
84+
return transformer_primitives.map_operations(
85+
circuit.freeze(),
86+
_map_func,
87+
deep=context.deep if context else False,
88+
tags_to_ignore=context.tags_to_ignore if context else [],
89+
).unfreeze(copy=False)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright 2025 The Cirq Developers
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import pytest
16+
import sympy
17+
18+
import cirq
19+
20+
21+
def test_symbolize_single_qubit_gates_by_indexed_tags_success():
22+
q = cirq.NamedQubit("a")
23+
input_circuit = cirq.Circuit(
24+
cirq.X(q).with_tags("TO-PHXZ_1"),
25+
cirq.Y(q).with_tags("tag1"),
26+
cirq.Z(q).with_tags("TO-PHXZ_0"),
27+
)
28+
output_circuit = cirq.symbolize_single_qubit_gates_by_indexed_tags(input_circuit)
29+
cirq.testing.assert_same_circuits(
30+
output_circuit,
31+
cirq.Circuit(
32+
cirq.PhasedXZGate(
33+
x_exponent=sympy.Symbol("x1"),
34+
z_exponent=sympy.Symbol("z1"),
35+
axis_phase_exponent=sympy.Symbol("a1"),
36+
).on(q),
37+
cirq.Y(q).with_tags("tag1"),
38+
cirq.PhasedXZGate(
39+
x_exponent=sympy.Symbol("x0"),
40+
z_exponent=sympy.Symbol("z0"),
41+
axis_phase_exponent=sympy.Symbol("a0"),
42+
).on(q),
43+
),
44+
)
45+
46+
47+
def test_symbolize_single_qubit_gates_by_indexed_tags_multiple_tags():
48+
q = cirq.NamedQubit("a")
49+
input_circuit = cirq.Circuit(cirq.X(q).with_tags("TO-PHXZ_0", "TO-PHXZ_2"))
50+
51+
with pytest.raises(ValueError, match="Multiple tags are prefixed with TO-PHXZ."):
52+
cirq.symbolize_single_qubit_gates_by_indexed_tags(input_circuit)

0 commit comments

Comments
 (0)