Skip to content

Commit cd7501f

Browse files
Merge branch 'main' into optimize-qubit-permutation-gate-decompose-method
2 parents 4efa639 + 9ddb8c8 commit cd7501f

38 files changed

+296
-60
lines changed

Diff for: .github/workflows/ci.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ jobs:
149149
name: Pytest Ubuntu
150150
strategy:
151151
matrix:
152-
python-version: [ '3.10', '3.11' ]
152+
python-version: ['3.10', '3.11', '3.12']
153153
runs-on: ubuntu-20.04
154154
steps:
155155
- uses: actions/checkout@v4
@@ -234,7 +234,7 @@ jobs:
234234
name: Pytest Windows
235235
strategy:
236236
matrix:
237-
python-version: [ '3.10', '3.11' ]
237+
python-version: ['3.10', '3.11', '3.12']
238238
runs-on: windows-2019
239239
steps:
240240
- uses: actions/checkout@v4
@@ -259,7 +259,7 @@ jobs:
259259
name: Pytest MacOS
260260
strategy:
261261
matrix:
262-
python-version: [ '3.10', '3.11' ]
262+
python-version: ['3.10', '3.11', '3.12']
263263
# TODO(#6577): upgrade to macos-latest when it runs Python 3.10
264264
runs-on: macos-13
265265
steps:

Diff for: cirq-aqt/setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
url='http://github.com/quantumlib/cirq',
6161
author='The Cirq Developers',
6262
author_email='[email protected]',
63-
python_requires=('>=3.9.0'),
63+
python_requires=('>=3.10.0'),
6464
install_requires=requirements,
6565
license='Apache 2',
6666
description=description,

Diff for: cirq-core/cirq/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,7 @@
336336

337337
from cirq.transformers import (
338338
AbstractInitialMapper,
339+
add_dynamical_decoupling,
339340
align_left,
340341
align_right,
341342
CompilationTargetGateset,

Diff for: cirq-core/cirq/_version.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@
1313
# limitations under the License.
1414

1515
"""Define version number here, read it from setup.py automatically,
16-
and warn users that the latest version of cirq uses python 3.9+"""
16+
and warn users that the latest version of cirq uses python 3.10+"""
1717

1818
import sys
1919

20-
if sys.version_info < (3, 9, 0): # pragma: no cover
20+
if sys.version_info < (3, 10, 0): # pragma: no cover
2121
raise SystemError(
22-
"You installed the latest version of cirq but aren't on python 3.9+.\n"
22+
"You installed the latest version of cirq but aren't on python 3.10+.\n"
2323
'To fix this error, you need to either:\n'
2424
'\n'
25-
'A) Update to python 3.9 or later.\n'
25+
'A) Update to python 3.10 or later.\n'
2626
'- OR -\n'
2727
'B) Explicitly install an older deprecated-but-compatible version '
2828
'of cirq (e.g. "python -m pip install cirq==1.1.*")'

Diff for: cirq-core/cirq/contrib/requirements.txt

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
# Runtime requirements for contrib.
22

33
ply>=3.6
4-
pylatex~=1.3.0
4+
pylatex~=1.4
55

66
# quimb
7-
quimb~=1.6.0
7+
quimb~=1.7
88
opt_einsum
9-
autoray
10-
# required for py39 opcodes.
11-
numba~=0.58.0

Diff for: cirq-core/cirq/ops/qid_util.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,7 @@ def q(*args: Union[int, str]) -> Union['cirq.LineQubit', 'cirq.GridQubit', 'cirq
4141
>>> cirq.q("foo") == cirq.NamedQubit("foo")
4242
True
4343
44-
Note that arguments should be treated as positional only, even
45-
though this is only enforceable in python 3.8 or later.
44+
Note that arguments should be treated as positional only.
4645
4746
Args:
4847
*args: One or two ints, or a single str, as described above.

Diff for: cirq-core/cirq/protocols/json_serialization_test.py

+1-7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14+
1415
import contextlib
1516
import dataclasses
1617
import datetime
@@ -19,7 +20,6 @@
1920
import json
2021
import os
2122
import pathlib
22-
import sys
2323
import warnings
2424
from typing import Dict, List, Optional, Tuple, Type
2525
from unittest import mock
@@ -56,12 +56,6 @@ class _ModuleDeprecation:
5656
}
5757

5858

59-
# pyQuil 3.0, necessary for cirq_rigetti module requires
60-
# python >= 3.9
61-
if sys.version_info < (3, 9): # pragma: no cover
62-
del TESTED_MODULES['cirq_rigetti']
63-
64-
6559
def _get_testspecs_for_modules() -> List[ModuleJsonTestSpec]:
6660
modules = []
6761
for m in TESTED_MODULES.keys():

Diff for: cirq-core/cirq/transformers/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@
7878

7979
from cirq.transformers.drop_negligible_operations import drop_negligible_operations
8080

81+
from cirq.transformers.dynamical_decoupling import add_dynamical_decoupling
82+
8183
from cirq.transformers.eject_z import eject_z
8284

8385
from cirq.transformers.measurement_transformers import (

Diff for: cirq-core/cirq/transformers/dynamical_decoupling.py

+122
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# Copyright 2024 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+
"""Transformer pass that adds dynamical decoupling operations to a circuit."""
16+
17+
from functools import reduce
18+
from typing import Dict, Optional, Sequence, Tuple, Union
19+
20+
from cirq.transformers import transformer_api
21+
import cirq
22+
import numpy as np
23+
24+
25+
def _repeat_sequence(
26+
base_sequence: Sequence['cirq.Gate'], num_idle_moments: int
27+
) -> Sequence['cirq.Gate']:
28+
"""Returns the longest possible dynamical decoupling sequence."""
29+
repeat_times = num_idle_moments // len(base_sequence)
30+
return list(base_sequence) * repeat_times
31+
32+
33+
def _get_dd_sequence_from_schema_name(schema: str) -> Sequence['cirq.Gate']:
34+
"""Gets dynamical decoupling sequence from a schema name."""
35+
dd_sequence: Sequence['cirq.Gate']
36+
match schema:
37+
case 'XX_PAIR':
38+
dd_sequence = (cirq.X, cirq.X)
39+
case 'X_XINV':
40+
dd_sequence = (cirq.X, cirq.X**-1)
41+
case 'YY_PAIR':
42+
dd_sequence = (cirq.Y, cirq.Y)
43+
case 'Y_YINV':
44+
dd_sequence = (cirq.Y, cirq.Y**-1)
45+
case _:
46+
raise ValueError('Invalid schema name.')
47+
return dd_sequence
48+
49+
50+
def _validate_dd_sequence(dd_sequence: Sequence['cirq.Gate']) -> None:
51+
"""Validates a given dynamical decoupling sequence.
52+
53+
Args:
54+
dd_sequence: Input dynamical sequence to be validated.
55+
56+
Returns:
57+
A tuple containing:
58+
- is_valid (bool): True if the dd sequence is valid, False otherwise.
59+
- error_message (str): An error message if the dd sequence is invalid, else None.
60+
61+
Raises:
62+
ValueError: If dd_sequence is not valid.
63+
"""
64+
if len(dd_sequence) < 2:
65+
raise ValueError('Invalid dynamical decoupling sequence. Expect more than one gates.')
66+
matrices = [cirq.unitary(gate) for gate in dd_sequence]
67+
product = reduce(np.matmul, matrices)
68+
69+
if not cirq.equal_up_to_global_phase(product, np.eye(2)):
70+
raise ValueError(
71+
'Invalid dynamical decoupling sequence. Expect sequence production equals'
72+
f' identity up to a global phase, got {product}.'.replace('\n', ' ')
73+
)
74+
75+
76+
def _parse_dd_sequence(schema: Union[str, Sequence['cirq.Gate']]) -> Sequence['cirq.Gate']:
77+
"""Parses and returns dynamical decoupling sequence from schema."""
78+
if isinstance(schema, str):
79+
dd_sequence = _get_dd_sequence_from_schema_name(schema)
80+
else:
81+
_validate_dd_sequence(schema)
82+
dd_sequence = schema
83+
return dd_sequence
84+
85+
86+
@transformer_api.transformer
87+
def add_dynamical_decoupling(
88+
circuit: 'cirq.AbstractCircuit',
89+
*,
90+
context: Optional['cirq.TransformerContext'] = None,
91+
schema: Union[str, Sequence['cirq.Gate']] = 'X_XINV',
92+
) -> 'cirq.Circuit':
93+
"""Adds dynamical decoupling gate operations to idle moments of a given circuit.
94+
This transformer preserves the moment structure of the circuit.
95+
96+
Args:
97+
circuit: Input circuit to transform.
98+
context: `cirq.TransformerContext` storing common configurable options for transformers.
99+
schema: Dynamical decoupling schema name or a dynamical decoupling sequence.
100+
If a schema is specified, provided dynamical decouping sequence will be used.
101+
Otherwise, customized dynamical decoupling sequence will be applied.
102+
103+
Returns:
104+
A copy of the input circuit with dynamical decoupling operations.
105+
"""
106+
last_busy_moment_by_qubits: Dict['cirq.Qid', int] = {q: 0 for q in circuit.all_qubits()}
107+
insert_into: list[Tuple[int, 'cirq.OP_TREE']] = []
108+
109+
base_dd_sequence = _parse_dd_sequence(schema)
110+
111+
for moment_id, moment in enumerate(circuit):
112+
for q in moment.qubits:
113+
insert_gates = _repeat_sequence(
114+
base_dd_sequence, num_idle_moments=moment_id - last_busy_moment_by_qubits[q] - 1
115+
)
116+
for idx, gate in enumerate(insert_gates):
117+
insert_into.append((last_busy_moment_by_qubits[q] + idx + 1, gate.on(q)))
118+
last_busy_moment_by_qubits[q] = moment_id
119+
120+
updated_circuit = circuit.unfreeze(copy=True)
121+
updated_circuit.batch_insert_into(insert_into)
122+
return updated_circuit
+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Copyright 2024 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+
from typing import Sequence, Union
16+
import cirq
17+
from cirq import add_dynamical_decoupling
18+
import pytest
19+
20+
21+
def assert_dd(
22+
input_circuit: cirq.Circuit,
23+
expected_circuit: cirq.Circuit,
24+
schema: Union[str, Sequence['cirq.Gate']],
25+
):
26+
updated_circuit = add_dynamical_decoupling(input_circuit, schema=schema)
27+
cirq.testing.assert_same_circuits(updated_circuit, expected_circuit)
28+
29+
30+
def test_no_insert_due_to_no_consecutive_moments():
31+
a = cirq.NamedQubit('a')
32+
b = cirq.NamedQubit('b')
33+
34+
# No insertion as there is no room for a dd sequence.
35+
assert_dd(
36+
input_circuit=cirq.Circuit(
37+
cirq.Moment(cirq.H(a)), cirq.Moment(cirq.CNOT(a, b)), cirq.Moment(cirq.H(b))
38+
),
39+
expected_circuit=cirq.Circuit(
40+
cirq.Moment(cirq.H(a)), cirq.Moment(cirq.CNOT(a, b)), cirq.Moment(cirq.H(b))
41+
),
42+
schema='XX_PAIR',
43+
)
44+
45+
46+
@pytest.mark.parametrize(
47+
'schema,inserted_gates',
48+
[
49+
('XX_PAIR', (cirq.X, cirq.X)),
50+
('X_XINV', (cirq.X, cirq.X**-1)),
51+
('YY_PAIR', (cirq.Y, cirq.Y)),
52+
('Y_YINV', (cirq.Y, cirq.Y**-1)),
53+
],
54+
)
55+
def test_insert_provided_schema(schema: str, inserted_gates: Sequence['cirq.Gate']):
56+
a = cirq.NamedQubit('a')
57+
b = cirq.NamedQubit('b')
58+
c = cirq.NamedQubit('c')
59+
60+
input_circuit = cirq.Circuit(
61+
cirq.Moment(cirq.H(a)),
62+
cirq.Moment(cirq.CNOT(a, b)),
63+
cirq.Moment(cirq.CNOT(b, c)),
64+
cirq.Moment(cirq.CNOT(b, c)),
65+
cirq.Moment(cirq.measure_each(a, b, c)),
66+
)
67+
expected_circuit = cirq.Circuit(
68+
cirq.Moment(cirq.H(a)),
69+
cirq.Moment(cirq.CNOT(a, b)),
70+
cirq.Moment(cirq.CNOT(b, c), inserted_gates[0](a)),
71+
cirq.Moment(cirq.CNOT(b, c), inserted_gates[1](a)),
72+
cirq.Moment(cirq.measure_each(a, b, c)),
73+
)
74+
75+
# Insert one dynamical decoupling sequence in idle moments.
76+
assert_dd(input_circuit, expected_circuit, schema=schema)
77+
78+
79+
def test_insert_by_customized_dd_sequence():
80+
a = cirq.NamedQubit('a')
81+
b = cirq.NamedQubit('b')
82+
c = cirq.NamedQubit('c')
83+
84+
assert_dd(
85+
input_circuit=cirq.Circuit(
86+
cirq.Moment(cirq.H(a)),
87+
cirq.Moment(cirq.CNOT(a, b)),
88+
cirq.Moment(cirq.CNOT(b, c)),
89+
cirq.Moment(cirq.CNOT(b, c)),
90+
cirq.Moment(cirq.CNOT(b, c)),
91+
cirq.Moment(cirq.CNOT(b, c)),
92+
cirq.Moment(cirq.measure_each(a, b, c)),
93+
),
94+
expected_circuit=cirq.Circuit(
95+
cirq.Moment(cirq.H(a)),
96+
cirq.Moment(cirq.CNOT(a, b)),
97+
cirq.Moment(cirq.CNOT(b, c), cirq.X(a)),
98+
cirq.Moment(cirq.CNOT(b, c), cirq.X(a)),
99+
cirq.Moment(cirq.CNOT(b, c), cirq.Y(a)),
100+
cirq.Moment(cirq.CNOT(b, c), cirq.Y(a)),
101+
cirq.Moment(cirq.measure_each(a, b, c)),
102+
),
103+
schema=[cirq.X, cirq.X, cirq.Y, cirq.Y],
104+
)
105+
106+
107+
@pytest.mark.parametrize(
108+
'schema,error_msg_regex',
109+
[
110+
('INVALID_SCHEMA', 'Invalid schema name.'),
111+
([cirq.X], 'Invalid dynamical decoupling sequence. Expect more than one gates.'),
112+
(
113+
[cirq.X, cirq.H],
114+
'Invalid dynamical decoupling sequence. Expect sequence production equals identity'
115+
' up to a global phase, got',
116+
),
117+
],
118+
)
119+
def test_invalid_dd_schema(schema: Union[str, Sequence['cirq.Gate']], error_msg_regex):
120+
a = cirq.NamedQubit('a')
121+
input_circuit = cirq.Circuit(cirq.H(a))
122+
with pytest.raises(ValueError, match=error_msg_regex):
123+
add_dynamical_decoupling(input_circuit, schema=schema)

Diff for: cirq-core/requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ attrs
44
duet>=0.2.8
55
matplotlib~=3.0
66
networkx>=2.4
7-
numpy~=1.16
7+
numpy~=1.22
88
pandas
99
sortedcontainers~=2.0
1010
scipy<1.13.0

Diff for: cirq-core/setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
url='http://github.com/quantumlib/cirq',
6464
author='The Cirq Developers',
6565
author_email='[email protected]',
66-
python_requires=('>=3.9.0'),
66+
python_requires=('>=3.10.0'),
6767
install_requires=requirements,
6868
extras_require={'contrib': contrib_requirements},
6969
license='Apache 2',

0 commit comments

Comments
 (0)