Skip to content

Commit a6aef3f

Browse files
authored
Add files via upload
1 parent 0d9a50d commit a6aef3f

File tree

7 files changed

+578
-0
lines changed

7 files changed

+578
-0
lines changed

recirq/cluster_state_mipt/README.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,106 @@
1+
# Cluster State Circuit Construction
12

3+
This module provides tools for constructing cluster state circuits using Google's Cirq framework.
4+
5+
## Module Structure
6+
7+
The Cluster State MIPT module consists of the following components:
8+
9+
- `cluster_state.py`: Core functionality for creating quantum circuits for cluster states
10+
- `data_collect.py`: Data collection utilities for quantum measurements
11+
- `cluster_state_test.py`: Tests for the cluster state functionality
12+
- `data_collect_test.py`: Tests for the data collection functionality
13+
14+
## Circuit Construction
15+
16+
The module implements a 6x6 grid of qubits with ancilla qubits for error mitigation. The circuit construction process involves:
17+
18+
1. **Grid Setup**:
19+
- Creates a 6x6 grid of physical qubits
20+
- Adds ancilla qubits for error mitigation
21+
- Positions probe qubits at specific distances (3-6 units apart)
22+
23+
2. **Circuit Generation**:
24+
- Applies Hadamard gates to all qubits
25+
- Applies CZ gates to create cluster state
26+
- Implements single-qubit rotations (Rz and Ry gates)
27+
- Adds basis rotations for measurement in X, Y, or Z basis
28+
- Includes ancilla qubit operations for error mitigation
29+
- Measures all preparation qubits to create entanglement pairs for probe qubits
30+
31+
### Implementation Details
32+
33+
The circuit is constructed on a 6x6 grid of qubits. Let $L$ be the system linear dimension and initialize all qubits in the product state $|0\rangle^{\otimes N}$. The protocol proceeds as follows:
34+
35+
1. **Apply Hadamard gates:** $\bigotimes_j H_j$
36+
2. **Apply nearest-neighbor $ZZ$ gates for $t = \pi/4$:**
37+
$e^{i(\pi/4)\sum_{\langle j,k \rangle} Z_j Z_k}$
38+
3. **Apply single-qubit rotations:** $\bigotimes_j e^{i(\theta/2) Y_j} e^{i(\phi/2) Z_j}$
39+
40+
After these steps, projective measurement in the $Z$ basis is performed on the preparation qubits (solid red in the figure below) to prepare the probe qubits' states (solid green). Shadow measurements are then performed on the probe qubits using the $X$, $Y$, and $Z$ bases with equal probability to collect data.
41+
42+
The nearest-neighbor gate $e^{i(\pi/4)\sum_{\langle j,k \rangle} Z_j Z_k}$ is implemented by first applying a controlled-$Z$ ($\mathrm{CZ}$) gate between each pair of qubits, followed by local $Z^{-1/2}$ on both qubits, i.e., $\mathrm{CZ}_{jk}Z_j^{-1/2}Z_k^{-1/2}$. All nearest-neighbor two-qubit gates are applied in the following sequence to maintain constant circuit depth as the system size scales up: first, odd horizontal links; then, even horizontal links; followed by odd vertical links and finally even vertical links.
43+
44+
![Experimental realization of the circuit on quantum chip with 6x6 lattice at d=5.](grid_d=5.png)
45+
46+
*Figure: Experimental realization of the circuit on a quantum chip with $6\times6$ lattice at $d=5$. Solid red: preparation qubits; solid green: probe qubits A and B. Faded qubits are ancillaries for readout error mitigation.*
47+
48+
#### Error Mitigation
49+
50+
Since the dominant source of error in this experiment is readout error, a replication-based error mitigation strategy is employed. After all gates (including basis rotations on probe qubits for shadow measurement), an additional CNOT gate is inserted from each perimeter qubit to its corresponding ancillary qubit (represented as faded boxes in the figure above, indicated by yellow bonds). This operation replicates the measurement outcome of the physical qubit onto the ancillary qubit. In post-processing, only measurement outcomes with matching values on each physical-ancillary pair are retained (post-selection), further enhancing the reliability of the experimental results on the mitigated qubits.
51+
52+
## Usage
53+
54+
To use the module:
55+
56+
```python
57+
from recirq.cluster_state_mipt import get_two_qubits_6x6, get_circuit
58+
59+
# Create a 6x6 grid with probe qubits at distance 6
60+
qubits_matrix, probe_qubits, anc_pairs, all_qubits = get_two_qubits_6x6(d=6)
61+
62+
# Generate a circuit with X-basis measurements
63+
circuit = get_circuit(
64+
qubits_matrix=qubits_matrix,
65+
theta=0.0,
66+
phi=0.0,
67+
probe_qubits=probe_qubits,
68+
basis=[0, 0], # X basis
69+
anc_pairs=anc_pairs
70+
)
71+
72+
# For data collection
73+
from recirq.cluster_state_mipt import collect_data
74+
import cirq_google as cg
75+
76+
# Setup engine
77+
engine = cg.get_engine('your-engine-id')
78+
79+
# Collect data
80+
collect_data(
81+
eng=engine,
82+
processor_id='your-processor-id',
83+
repetitions=10000,
84+
num_rc_circuits=20,
85+
folder_name='my_experiment'
86+
)
87+
```
88+
89+
## Testing
90+
91+
The module includes tests for both the cluster state and data collection functionalities:
92+
93+
```bash
94+
# Run cluster state tests
95+
python -m pytest recirq/cluster_state_mipt/cluster_state_test.py
96+
97+
# Run data collection tests
98+
python -m pytest recirq/cluster_state_mipt/data_collect_test.py
99+
```
100+
101+
## Dependencies
102+
103+
- Cirq >= 0.13.0
104+
- Cirq-Google >= 0.13.0
105+
- NumPy >= 1.19.0
106+
- PyTorch >= 1.9.0 (for data collection)

recirq/cluster_state_mipt/__init__.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
"""Cluster state measurement implementation at MIPT.
2+
3+
This package contains implementations for creating and measuring cluster states
4+
on quantum processors.
5+
"""
6+
7+
from recirq.cluster_state_mipt.cluster_state import get_two_qubits_6x6, get_circuit
8+
from recirq.cluster_state_mipt.data_collect import collect_data
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Copyright 2025 Google
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 cirq
16+
import cirq_google as cg
17+
import numpy as np
18+
19+
def get_two_qubits_6x6(d=6):
20+
"""Create a 6x6 grid of qubits with probe qubits at specified distance.
21+
22+
This function creates a 6x6 qubit grid structure with two probe qubits positioned
23+
at a certain distance from each other. The distance parameter determines which
24+
specific grid locations are selected for the probe qubits.
25+
26+
Args:
27+
d: Distance between probe qubits (3, 4, 5, or 6).
28+
29+
Returns:
30+
A tuple of (qubits_matrix, probe_qubits, anc_pairs, all_qubits) where:
31+
qubits_matrix: 2D array of grid qubits
32+
probe_qubits: List of the two probe qubits
33+
anc_pairs: Ancilla qubit pairs for error mitigation
34+
all_qubits: Flattened array of all qubits in the system
35+
36+
Raises:
37+
ValueError: If d is not 3, 4, 5, or 6.
38+
"""
39+
# 6x6 grid
40+
if d == 6:
41+
probe_qubits = [cirq.GridQubit(3,4), cirq.GridQubit(3,9)]
42+
elif d == 5:
43+
probe_qubits = [cirq.GridQubit(3,4), cirq.GridQubit(3,8)]
44+
#probe_qubits = [cirq.GridQubit(3,9), cirq.GridQubit(7,9)]
45+
elif d == 4:
46+
probe_qubits = [cirq.GridQubit(3,5), cirq.GridQubit(3,8)]
47+
elif d == 3:
48+
probe_qubits = [cirq.GridQubit(3,5), cirq.GridQubit(3,7)]
49+
else:
50+
raise ValueError("d must be 3, 4, 5, or 6")
51+
52+
qubits_matrix = []
53+
for x in [3,4,5,6,7,8]:
54+
qbs = []
55+
for y in [4,5,6,7,8,9]:
56+
qbs.append(cirq.GridQubit(x,y))
57+
qubits_matrix.append(qbs)
58+
59+
# add ancilla pairs, [phy, anc]
60+
anc_pairs = [
61+
# top row
62+
[[cirq.GridQubit(3,4), cirq.GridQubit(2,4)],
63+
[cirq.GridQubit(3,5), cirq.GridQubit(2,5)],
64+
[cirq.GridQubit(3,6), cirq.GridQubit(2,6)],
65+
[cirq.GridQubit(3,7), cirq.GridQubit(2,7)],
66+
[cirq.GridQubit(3,8), cirq.GridQubit(2,8)],
67+
[cirq.GridQubit(3,9), cirq.GridQubit(2,9)],
68+
# bottom row
69+
[cirq.GridQubit(8,4), cirq.GridQubit(9,4)],
70+
[cirq.GridQubit(8,5), cirq.GridQubit(9,5)],
71+
[cirq.GridQubit(8,6), cirq.GridQubit(9,6)],
72+
[cirq.GridQubit(8,7), cirq.GridQubit(9,7)],
73+
[cirq.GridQubit(8,8), cirq.GridQubit(9,8)],
74+
[cirq.GridQubit(8,9), cirq.GridQubit(9,9)],
75+
# left column
76+
[cirq.GridQubit(3,4), cirq.GridQubit(3,3)],
77+
[cirq.GridQubit(4,4), cirq.GridQubit(4,3)],
78+
[cirq.GridQubit(5,4), cirq.GridQubit(5,3)],
79+
[cirq.GridQubit(6,4), cirq.GridQubit(6,3)],
80+
[cirq.GridQubit(7,4), cirq.GridQubit(7,3)],
81+
[cirq.GridQubit(8,4), cirq.GridQubit(8,3)],
82+
# right column
83+
[cirq.GridQubit(3,9), cirq.GridQubit(3,10)],
84+
[cirq.GridQubit(4,9), cirq.GridQubit(4,10)],
85+
[cirq.GridQubit(5,9), cirq.GridQubit(5,10)],
86+
[cirq.GridQubit(6,9), cirq.GridQubit(6,10)],
87+
[cirq.GridQubit(7,9), cirq.GridQubit(7,10)],
88+
[cirq.GridQubit(8,9), cirq.GridQubit(8,10)]],
89+
# extra ancilla pairs
90+
# [[cirq.GridQubit(2,8), cirq.GridQubit(1,8)]]
91+
]
92+
# get all qubits
93+
all_qubits = np.unique(np.concatenate(
94+
[np.array(qubits_matrix).flatten()]+[np.array(anc_pair_set).flatten() for anc_pair_set in anc_pairs]))
95+
return qubits_matrix, probe_qubits, anc_pairs, all_qubits
96+
97+
def get_circuit(qubits_matrix, theta, phi, probe_qubits, basis=[0,0], anc_pairs=[]):
98+
"""Create a quantum circuit for a cluster state with specified parameters.
99+
100+
This function constructs a quantum circuit for creating a cluster state on a grid of qubits.
101+
The circuit includes Hadamard gates on all qubits, CZ gates for entanglement, single-qubit
102+
rotations, probe qubit basis rotations, and optional ancilla qubit operations.
103+
104+
Args:
105+
qubits_matrix: 2D array of grid qubits.
106+
theta: Angle for Ry rotation (radians).
107+
phi: Angle for Rz rotation (radians).
108+
probe_qubits: List of qubits to be used as probe qubits.
109+
basis: List specifying measurement basis for each probe qubit
110+
(0 for X basis, 1 for Y basis, 2 for Z basis). Defaults to [0,0].
111+
anc_pairs: List of ancilla qubit pairs for error mitigation. Defaults to [].
112+
113+
Returns:
114+
cirq.Circuit: A quantum circuit implementing the cluster state preparation.
115+
116+
Raises:
117+
ValueError: If the number of basis states doesn't match the number of probe qubits.
118+
"""
119+
circ = cirq.Circuit()
120+
qubits_list = np.array(qubits_matrix).flatten()
121+
num_qubits = len(qubits_list)
122+
num_col = len(qubits_matrix[0])
123+
num_row = len(qubits_matrix)
124+
if len(basis) != len(probe_qubits):
125+
raise ValueError("The number of basis states must match the number of probe qubits.")
126+
127+
# Hadamard on all qubits
128+
circ.append([cirq.H(q) for q in qubits_list])
129+
130+
# Horizontal bonds
131+
for i in range(num_row):
132+
for j in range(0, num_col-1, 2):
133+
circ.append(cirq.CZ(qubits_matrix[i][j],qubits_matrix[i][j+1]))
134+
circ.append(cirq.PhasedXZGate(axis_phase_exponent=0,x_exponent=0,z_exponent=-0.5).on(qubits_matrix[i][j]))
135+
circ.append(cirq.PhasedXZGate(axis_phase_exponent=0,x_exponent=0,z_exponent=-0.5).on(qubits_matrix[i][j+1]))
136+
for j in range(1, num_col-1, 2):
137+
circ.append(cirq.CZ(qubits_matrix[i][j],qubits_matrix[i][j+1]))
138+
circ.append(cirq.PhasedXZGate(axis_phase_exponent=0,x_exponent=0,z_exponent=-0.5).on(qubits_matrix[i][j]))
139+
circ.append(cirq.PhasedXZGate(axis_phase_exponent=0,x_exponent=0,z_exponent=-0.5).on(qubits_matrix[i][j+1]))
140+
141+
# Vertical bonds
142+
for j in range(num_col):
143+
for i in range(0, num_row-1, 2):
144+
circ.append(cirq.CZ(qubits_matrix[i][j], qubits_matrix[i+1][j]))
145+
circ.append(cirq.PhasedXZGate(axis_phase_exponent=0, x_exponent=0, z_exponent=-0.5).on(qubits_matrix[i][j]))
146+
circ.append(cirq.PhasedXZGate(axis_phase_exponent=0, x_exponent=0, z_exponent=-0.5).on(qubits_matrix[i+1][j]))
147+
for i in range(1, num_row-1, 2):
148+
circ.append(cirq.CZ(qubits_matrix[i][j], qubits_matrix[i+1][j]))
149+
circ.append(cirq.PhasedXZGate(axis_phase_exponent=0, x_exponent=0, z_exponent=-0.5).on(qubits_matrix[i][j]))
150+
circ.append(cirq.PhasedXZGate(axis_phase_exponent=0, x_exponent=0, z_exponent=-0.5).on(qubits_matrix[i+1][j]))
151+
152+
# Single qubit rotations
153+
for i in range(num_qubits):
154+
circ.append(cirq.rz(-phi).on(qubits_list[i]))
155+
circ.append(cirq.ry(-theta).on(qubits_list[i]))
156+
157+
# Rotate probe qubits: x y z -> 0 1 2
158+
for q, b in zip(probe_qubits, basis):
159+
if b == 0:
160+
circ.append(cirq.H.on(q), strategy=cirq.circuits.InsertStrategy.INLINE)
161+
elif b == 1:
162+
circ.append(cirq.rx(np.pi/2).on(q), strategy=cirq.circuits.InsertStrategy.INLINE)
163+
164+
# Ancilla pairs
165+
for anc_pair_set in anc_pairs:
166+
for anc_pair in anc_pair_set:
167+
circ.append(cirq.H.on(anc_pair[1]), strategy=cirq.circuits.InsertStrategy.INLINE)
168+
for anc_pair in anc_pair_set:
169+
circ.append(cirq.CZ(*anc_pair))
170+
for anc_pair in anc_pair_set:
171+
circ.append(cirq.H.on(anc_pair[1]), strategy=cirq.circuits.InsertStrategy.INLINE)
172+
173+
return circ

0 commit comments

Comments
 (0)