Skip to content

Commit 2bdbea8

Browse files
authored
Use cirq.q instead of str in analog (#7736)
We should use the internal str style representation, it is natural to use cirq.q and cirq.Coupler in the analog construction.
1 parent 1deffd6 commit 2bdbea8

File tree

4 files changed

+149
-113
lines changed

4 files changed

+149
-113
lines changed

cirq-google/cirq_google/experimental/analog_experiments/analog_trajectory_util.py

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
if TYPE_CHECKING:
2828
from matplotlib.axes import Axes
2929

30+
from cirq_google.ops import coupler as cgc
31+
3032

3133
@attrs.mutable
3234
class FrequencyMap:
@@ -40,8 +42,8 @@ class FrequencyMap:
4042
"""
4143

4244
duration: su.ValueOrSymbol
43-
qubit_freqs: dict[str, su.ValueOrSymbol | None]
44-
couplings: dict[tuple[str, str], su.ValueOrSymbol]
45+
qubit_freqs: dict[cirq.Qid, su.ValueOrSymbol | None]
46+
couplings: dict[cgc.Coupler, su.ValueOrSymbol]
4547
is_wait_step: bool
4648

4749
def _is_parameterized_(self) -> bool:
@@ -86,52 +88,52 @@ def __init__(
8688
self,
8789
*,
8890
full_trajectory: list[FrequencyMap],
89-
qubits: list[str],
90-
pairs: list[tuple[str, str]],
91+
qubits: list[cirq.Qid],
92+
couplers: list[cgc.Coupler],
9193
):
9294
self.full_trajectory = full_trajectory
9395
self.qubits = qubits
94-
self.pairs = pairs
96+
self.couplers = couplers
9597

9698
@classmethod
9799
def from_sparse_trajectory(
98100
cls,
99101
sparse_trajectory: list[
100102
tuple[
101103
tu.Value,
102-
dict[str, su.ValueOrSymbol | None],
103-
dict[tuple[str, str], su.ValueOrSymbol],
104+
dict[cirq.Qid, su.ValueOrSymbol | None],
105+
dict[cgc.Coupler, su.ValueOrSymbol],
104106
],
105107
],
106-
qubits: list[str] | None = None,
107-
pairs: list[tuple[str, str]] | None = None,
108+
qubits: list[cirq.Qid] | None = None,
109+
couplers: list[cgc.Coupler] | None = None,
108110
):
109111
"""Construct AnalogTrajectory from sparse trajectory.
110112
111113
Args:
112114
sparse_trajectory: A list of tuples, where each tuple defines a `FrequencyMap`
113115
and contains three elements: (duration, qubit_freqs, coupling_strengths).
114-
`duration` is a tunits value, `qubit_freqs` is a dictionary mapping qubit strings
115-
to detuning frequencies, and `coupling_strengths` is a dictionary mapping qubit
116-
pairs to their coupling strength. This format is considered "sparse" because each
116+
`duration` is a tunits value, `qubit_freqs` is a dictionary mapping cirq qubits
117+
to detuning frequencies, and `coupling_strengths` is a dictionary mapping
118+
couplers to their coupling strength. This format is considered "sparse" because each
117119
tuple does not need to fully specify all qubits and coupling pairs; any missing
118120
detuning frequency or coupling strength will be set to the same value as the
119121
previous value in the list.
120122
qubits: The qubits in interest. If not provided, automatically parsed from trajectory.
121-
pairs: The pairs in interest. If not provided, automatically parsed from trajectory.
123+
couplers: The couplers in interest. If not provided, auto. parsed from trajectory.
122124
"""
123-
if qubits is None or pairs is None:
124-
qubits_in_traj: list[str] = []
125-
pairs_in_traj: list[tuple[str, str]] = []
125+
if qubits is None or couplers is None:
126+
qubits_in_traj: list[cirq.Qid] = []
127+
couplers_in_traj: list[cgc.Coupler] = []
126128
for _, q, p in sparse_trajectory:
127129
qubits_in_traj.extend(q.keys())
128-
pairs_in_traj.extend(p.keys())
130+
couplers_in_traj.extend(p.keys())
129131
qubits = list(set(qubits_in_traj))
130-
pairs = list(set(pairs_in_traj))
132+
couplers = list(set(couplers_in_traj))
131133

132134
full_trajectory: list[FrequencyMap] = []
133-
init_qubit_freq_dict: dict[str, tu.Value | None] = {q: None for q in qubits}
134-
init_g_dict: dict[tuple[str, str], tu.Value] = {p: 0 * tu.MHz for p in pairs}
135+
init_qubit_freq_dict: dict[cirq.Qid, tu.Value | None] = {q: None for q in qubits}
136+
init_g_dict: dict[cgc.Coupler, tu.Value] = {c: 0 * tu.MHz for c in couplers}
135137
full_trajectory.append(FrequencyMap(0 * tu.ns, init_qubit_freq_dict, init_g_dict, False))
136138

137139
for dt, qubit_freq_dict, g_dict in sparse_trajectory:
@@ -142,15 +144,15 @@ def from_sparse_trajectory(
142144
q: qubit_freq_dict.get(q, full_trajectory[-1].qubit_freqs.get(q)) for q in qubits
143145
}
144146
# If no g provided, set equal to previous
145-
new_g_dict: dict[tuple[str, str], tu.Value] = {
146-
p: g_dict.get(p, full_trajectory[-1].couplings.get(p)) for p in pairs # type: ignore[misc]
147+
new_g_dict: dict[cgc.Coupler, tu.Value] = {
148+
c: g_dict.get(c, full_trajectory[-1].couplings.get(c)) for c in couplers # type: ignore[misc]
147149
}
148150

149151
full_trajectory.append(FrequencyMap(dt, new_qubit_freq_dict, new_g_dict, is_wait_step))
150-
return cls(full_trajectory=full_trajectory, qubits=qubits, pairs=pairs)
152+
return cls(full_trajectory=full_trajectory, qubits=qubits, couplers=couplers)
151153

152154
def get_full_trajectory_with_resolved_idles(
153-
self, idle_freq_map: dict[str, tu.Value]
155+
self, idle_freq_map: dict[cirq.Qid, tu.Value]
154156
) -> list[FrequencyMap]:
155157
"""Insert idle frequencies instead of None in trajectory."""
156158

@@ -164,13 +166,19 @@ def get_full_trajectory_with_resolved_idles(
164166

165167
def plot(
166168
self,
167-
idle_freq_map: dict[str, tu.Value] | None = None,
168-
default_idle_freq: tu.Value = 6.5 * tu.GHz,
169+
idle_freq_map: dict[cirq.Qid, tu.Value] | None = None,
169170
resolver: cirq.ParamResolverOrSimilarType | None = None,
170171
axes: tuple[Axes, Axes] | None = None,
171172
) -> tuple[Axes, Axes]:
172173
if idle_freq_map is None:
173-
idle_freq_map = {q: default_idle_freq for q in self.qubits}
174+
# Because we use relative frequencies and we do not expose the idle frequencies,
175+
# we randomly assign idle frequencies for plotting purposes only.
176+
idle_freq_map = {q: np.random.randn() * 50 * tu.MHz for q in self.qubits}
177+
else: # pragma: no cover
178+
for q in self.qubits:
179+
if q not in idle_freq_map: # Fill in missing idle freqs
180+
idle_freq_map[q] = np.random.randn() * 50 * tu.MHz
181+
174182
full_trajectory_resolved = cirq.resolve_parameters(
175183
self.get_full_trajectory_with_resolved_idles(idle_freq_map), resolver
176184
)
@@ -185,17 +193,17 @@ def plot(
185193
if axes is None:
186194
_, axes = plt.subplots(1, 2, figsize=(10, 4))
187195

188-
for qubit_agent in self.qubits:
196+
for qubit in self.qubits:
189197
axes[0].plot(
190198
times,
191-
[step.qubit_freqs[qubit_agent][tu.GHz] for step in full_trajectory_resolved], # type: ignore[index]
192-
label=qubit_agent,
199+
[step.qubit_freqs[qubit][tu.GHz] for step in full_trajectory_resolved], # type: ignore[index]
200+
label=qubit,
193201
)
194-
for pair_agent in self.pairs:
202+
for coupler in self.couplers:
195203
axes[1].plot(
196204
times,
197-
[step.couplings[pair_agent][tu.MHz] for step in full_trajectory_resolved],
198-
label=pair_agent,
205+
[step.couplings[coupler][tu.MHz] for step in full_trajectory_resolved],
206+
label=coupler,
199207
)
200208

201209
for ax, ylabel in zip(axes, ["Qubit freq. (GHz)", "Coupling (MHz)"]):

cirq-google/cirq_google/experimental/analog_experiments/analog_trajectory_util_test.py

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@
1717
import tunits as tu
1818

1919
import cirq
20+
import cirq_google as cg
2021
from cirq_google.experimental.analog_experiments import analog_trajectory_util as atu
2122

2223

2324
@pytest.fixture
2425
def freq_map() -> atu.FrequencyMap:
2526
return atu.FrequencyMap(
2627
10 * tu.ns,
27-
{"q0_0": 5 * tu.GHz, "q0_1": 6 * tu.GHz, "q0_2": sympy.Symbol("f_q0_2")},
28-
{("q0_0", "q0_1"): 5 * tu.MHz, ("q0_1", "q0_2"): sympy.Symbol("g_q0_1_q0_2")},
28+
{cirq.q(0, 0): 5 * tu.GHz, cirq.q(0, 1): 6 * tu.GHz, cirq.q(0, 2): sympy.Symbol("f_q0_2")},
29+
{
30+
cg.Coupler(cirq.q(0, 0), cirq.q(0, 1)): 5 * tu.MHz,
31+
cg.Coupler(cirq.q(0, 1), cirq.q(0, 2)): sympy.Symbol("g_q0_1_q0_2"),
32+
},
2933
False,
3034
)
3135

@@ -41,59 +45,68 @@ def test_freq_map_resolve(freq_map: atu.FrequencyMap) -> None:
4145
)
4246
assert resolved_freq_map == atu.FrequencyMap(
4347
10 * tu.ns,
44-
{"q0_0": 5 * tu.GHz, "q0_1": 6 * tu.GHz, "q0_2": 6 * tu.GHz},
45-
{("q0_0", "q0_1"): 5 * tu.MHz, ("q0_1", "q0_2"): 7 * tu.MHz},
48+
{cirq.q(0, 0): 5 * tu.GHz, cirq.q(0, 1): 6 * tu.GHz, cirq.q(0, 2): 6 * tu.GHz},
49+
{
50+
cg.Coupler(cirq.q(0, 0), cirq.q(0, 1)): 5 * tu.MHz,
51+
cg.Coupler(cirq.q(0, 1), cirq.q(0, 2)): 7 * tu.MHz,
52+
},
4653
False,
4754
)
4855

4956

50-
FreqMapType = tuple[tu.Value, dict[str, tu.Value | None], dict[tuple[str, str], tu.Value]]
57+
FreqMapType = tuple[tu.Value, dict[cirq.Qid, tu.Value | None], dict[cg.Coupler, tu.Value]]
5158

5259

5360
@pytest.fixture
5461
def sparse_trajectory() -> list[FreqMapType]:
55-
traj1: FreqMapType = (20 * tu.ns, {"q0_1": 5 * tu.GHz}, {})
56-
traj2: FreqMapType = (30 * tu.ns, {"q0_2": 8 * tu.GHz}, {})
62+
traj1: FreqMapType = (20 * tu.ns, {cirq.q(0, 1): 5 * tu.GHz}, {})
63+
traj2: FreqMapType = (30 * tu.ns, {cirq.q(0, 2): 8 * tu.GHz}, {})
5764
traj3: FreqMapType = (35 * tu.ns, {}, {})
5865
traj4: FreqMapType = (
5966
40 * tu.ns,
60-
{"q0_0": 8 * tu.GHz, "q0_1": None, "q0_2": None},
61-
{("q0_0", "q0_1"): 5 * tu.MHz, ("q0_1", "q0_2"): 8 * tu.MHz},
67+
{cirq.q(0, 0): 8 * tu.GHz, cirq.q(0, 1): None, cirq.q(0, 2): None},
68+
{
69+
cg.Coupler(cirq.q(0, 0), cirq.q(0, 1)): 5 * tu.MHz,
70+
cg.Coupler(cirq.q(0, 1), cirq.q(0, 2)): 8 * tu.MHz,
71+
},
6272
)
6373
return [traj1, traj2, traj3, traj4]
6474

6575

6676
def test_full_traj(sparse_trajectory: list[FreqMapType]) -> None:
6777
analog_traj = atu.AnalogTrajectory.from_sparse_trajectory(sparse_trajectory)
78+
coupler1 = cg.Coupler(cirq.q(0, 0), cirq.q(0, 1))
79+
coupler2 = cg.Coupler(cirq.q(0, 1), cirq.q(0, 2))
80+
6881
assert len(analog_traj.full_trajectory) == 5
6982
assert analog_traj.full_trajectory[0] == atu.FrequencyMap(
7083
0 * tu.ns,
71-
{"q0_0": None, "q0_1": None, "q0_2": None},
72-
{("q0_0", "q0_1"): 0 * tu.MHz, ("q0_1", "q0_2"): 0 * tu.MHz},
84+
{cirq.q(0, 0): None, cirq.q(0, 1): None, cirq.q(0, 2): None},
85+
{coupler1: 0 * tu.MHz, coupler2: 0 * tu.MHz},
7386
False,
7487
)
7588
assert analog_traj.full_trajectory[1] == atu.FrequencyMap(
7689
20 * tu.ns,
77-
{"q0_0": None, "q0_1": 5 * tu.GHz, "q0_2": None},
78-
{("q0_0", "q0_1"): 0 * tu.MHz, ("q0_1", "q0_2"): 0 * tu.MHz},
90+
{cirq.q(0, 0): None, cirq.q(0, 1): 5 * tu.GHz, cirq.q(0, 2): None},
91+
{coupler1: 0 * tu.MHz, coupler2: 0 * tu.MHz},
7992
False,
8093
)
8194
assert analog_traj.full_trajectory[2] == atu.FrequencyMap(
8295
30 * tu.ns,
83-
{"q0_0": None, "q0_1": 5 * tu.GHz, "q0_2": 8 * tu.GHz},
84-
{("q0_0", "q0_1"): 0 * tu.MHz, ("q0_1", "q0_2"): 0 * tu.MHz},
96+
{cirq.q(0, 0): None, cirq.q(0, 1): 5 * tu.GHz, cirq.q(0, 2): 8 * tu.GHz},
97+
{coupler1: 0 * tu.MHz, coupler2: 0 * tu.MHz},
8598
False,
8699
)
87100
assert analog_traj.full_trajectory[3] == atu.FrequencyMap(
88101
35 * tu.ns,
89-
{"q0_0": None, "q0_1": 5 * tu.GHz, "q0_2": 8 * tu.GHz},
90-
{("q0_0", "q0_1"): 0 * tu.MHz, ("q0_1", "q0_2"): 0 * tu.MHz},
102+
{cirq.q(0, 0): None, cirq.q(0, 1): 5 * tu.GHz, cirq.q(0, 2): 8 * tu.GHz},
103+
{coupler1: 0 * tu.MHz, coupler2: 0 * tu.MHz},
91104
True,
92105
)
93106
assert analog_traj.full_trajectory[4] == atu.FrequencyMap(
94107
40 * tu.ns,
95-
{"q0_0": 8 * tu.GHz, "q0_1": None, "q0_2": None},
96-
{("q0_0", "q0_1"): 5 * tu.MHz, ("q0_1", "q0_2"): 8 * tu.MHz},
108+
{cirq.q(0, 0): 8 * tu.GHz, cirq.q(0, 1): None, cirq.q(0, 2): None},
109+
{coupler1: 5 * tu.MHz, coupler2: 8 * tu.MHz},
97110
False,
98111
)
99112

@@ -102,53 +115,59 @@ def test_get_full_trajectory_with_resolved_idles(sparse_trajectory: list[FreqMap
102115

103116
analog_traj = atu.AnalogTrajectory.from_sparse_trajectory(sparse_trajectory)
104117
resolved_full_traj = analog_traj.get_full_trajectory_with_resolved_idles(
105-
{"q0_0": 5 * tu.GHz, "q0_1": 6 * tu.GHz, "q0_2": 7 * tu.GHz}
118+
{cirq.q(0, 0): 5 * tu.GHz, cirq.q(0, 1): 6 * tu.GHz, cirq.q(0, 2): 7 * tu.GHz}
106119
)
120+
coupler1 = cg.Coupler(cirq.q(0, 0), cirq.q(0, 1))
121+
coupler2 = cg.Coupler(cirq.q(0, 1), cirq.q(0, 2))
107122

108123
assert len(resolved_full_traj) == 5
109124
assert resolved_full_traj[0] == atu.FrequencyMap(
110125
0 * tu.ns,
111-
{"q0_0": 5 * tu.GHz, "q0_1": 6 * tu.GHz, "q0_2": 7 * tu.GHz},
112-
{("q0_0", "q0_1"): 0 * tu.MHz, ("q0_1", "q0_2"): 0 * tu.MHz},
126+
{cirq.q(0, 0): 5 * tu.GHz, cirq.q(0, 1): 6 * tu.GHz, cirq.q(0, 2): 7 * tu.GHz},
127+
{coupler1: 0 * tu.MHz, coupler2: 0 * tu.MHz},
113128
False,
114129
)
115130
assert resolved_full_traj[1] == atu.FrequencyMap(
116131
20 * tu.ns,
117-
{"q0_0": 5 * tu.GHz, "q0_1": 5 * tu.GHz, "q0_2": 7 * tu.GHz},
118-
{("q0_0", "q0_1"): 0 * tu.MHz, ("q0_1", "q0_2"): 0 * tu.MHz},
132+
{cirq.q(0, 0): 5 * tu.GHz, cirq.q(0, 1): 5 * tu.GHz, cirq.q(0, 2): 7 * tu.GHz},
133+
{coupler1: 0 * tu.MHz, coupler2: 0 * tu.MHz},
119134
False,
120135
)
121136
assert resolved_full_traj[2] == atu.FrequencyMap(
122137
30 * tu.ns,
123-
{"q0_0": 5 * tu.GHz, "q0_1": 5 * tu.GHz, "q0_2": 8 * tu.GHz},
124-
{("q0_0", "q0_1"): 0 * tu.MHz, ("q0_1", "q0_2"): 0 * tu.MHz},
138+
{cirq.q(0, 0): 5 * tu.GHz, cirq.q(0, 1): 5 * tu.GHz, cirq.q(0, 2): 8 * tu.GHz},
139+
{coupler1: 0 * tu.MHz, coupler2: 0 * tu.MHz},
125140
False,
126141
)
127142
assert resolved_full_traj[3] == atu.FrequencyMap(
128143
35 * tu.ns,
129-
{"q0_0": 5 * tu.GHz, "q0_1": 5 * tu.GHz, "q0_2": 8 * tu.GHz},
130-
{("q0_0", "q0_1"): 0 * tu.MHz, ("q0_1", "q0_2"): 0 * tu.MHz},
144+
{cirq.q(0, 0): 5 * tu.GHz, cirq.q(0, 1): 5 * tu.GHz, cirq.q(0, 2): 8 * tu.GHz},
145+
{coupler1: 0 * tu.MHz, coupler2: 0 * tu.MHz},
131146
True,
132147
)
133148
assert resolved_full_traj[4] == atu.FrequencyMap(
134149
40 * tu.ns,
135-
{"q0_0": 8 * tu.GHz, "q0_1": 6 * tu.GHz, "q0_2": 7 * tu.GHz},
136-
{("q0_0", "q0_1"): 5 * tu.MHz, ("q0_1", "q0_2"): 8 * tu.MHz},
150+
{cirq.q(0, 0): 8 * tu.GHz, cirq.q(0, 1): 6 * tu.GHz, cirq.q(0, 2): 7 * tu.GHz},
151+
{coupler1: 5 * tu.MHz, coupler2: 8 * tu.MHz},
137152
False,
138153
)
139154

140155

141156
def test_plot_with_unresolved_parameters() -> None:
142-
traj1: FreqMapType = (20 * tu.ns, {"q0_1": sympy.Symbol("qf")}, {})
143-
traj2: FreqMapType = (sympy.Symbol("t"), {"q0_2": 8 * tu.GHz}, {})
157+
traj1: FreqMapType = (20 * tu.ns, {cirq.q(0, 1): sympy.Symbol("qf")}, {})
158+
traj2: FreqMapType = (sympy.Symbol("t"), {cirq.q(0, 2): 8 * tu.GHz}, {})
144159
analog_traj = atu.AnalogTrajectory.from_sparse_trajectory([traj1, traj2])
145160

146161
with pytest.raises(ValueError):
147162
analog_traj.plot()
148163

149164

150165
def test_analog_traj_plot() -> None:
151-
traj1: FreqMapType = (5 * tu.ns, {"q0_1": sympy.Symbol("qf")}, {("q0_0", "q0_1"): 2 * tu.MHz})
152-
traj2: FreqMapType = (sympy.Symbol("t"), {"q0_2": 8 * tu.GHz}, {})
166+
traj1: FreqMapType = (
167+
5 * tu.ns,
168+
{cirq.q(0, 1): sympy.Symbol("qf")},
169+
{cg.Coupler(cirq.q(0, 0), cirq.q(0, 1)): 2 * tu.MHz},
170+
)
171+
traj2: FreqMapType = (sympy.Symbol("t"), {cirq.q(0, 2): 8 * tu.GHz}, {})
153172
analog_traj = atu.AnalogTrajectory.from_sparse_trajectory([traj1, traj2])
154173
analog_traj.plot(resolver={"t": 10 * tu.ns, "qf": 5 * tu.GHz})

0 commit comments

Comments
 (0)