cirq-core 1.5.0.dev20240710220908__py3-none-any.whl → 1.5.0.dev20240718000937__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of cirq-core might be problematic. Click here for more details.
- cirq/_version.py +1 -1
- cirq/_version_test.py +1 -1
- cirq/qis/__init__.py +1 -0
- cirq/qis/entropy.py +115 -0
- cirq/qis/entropy_test.py +42 -0
- cirq/transformers/__init__.py +2 -0
- cirq/transformers/noise_adding.py +114 -0
- cirq/transformers/noise_adding_test.py +53 -0
- cirq/transformers/randomized_measurements.py +109 -0
- cirq/transformers/randomized_measurements_test.py +55 -0
- {cirq_core-1.5.0.dev20240710220908.dist-info → cirq_core-1.5.0.dev20240718000937.dist-info}/METADATA +1 -1
- {cirq_core-1.5.0.dev20240710220908.dist-info → cirq_core-1.5.0.dev20240718000937.dist-info}/RECORD +15 -9
- {cirq_core-1.5.0.dev20240710220908.dist-info → cirq_core-1.5.0.dev20240718000937.dist-info}/LICENSE +0 -0
- {cirq_core-1.5.0.dev20240710220908.dist-info → cirq_core-1.5.0.dev20240718000937.dist-info}/WHEEL +0 -0
- {cirq_core-1.5.0.dev20240710220908.dist-info → cirq_core-1.5.0.dev20240718000937.dist-info}/top_level.txt +0 -0
cirq/_version.py
CHANGED
cirq/_version_test.py
CHANGED
cirq/qis/__init__.py
CHANGED
cirq/qis/entropy.py
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
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 concurrent.futures import ThreadPoolExecutor
|
|
16
|
+
from collections.abc import Sequence
|
|
17
|
+
from itertools import product
|
|
18
|
+
from typing import Any, Optional
|
|
19
|
+
|
|
20
|
+
import numpy as np
|
|
21
|
+
import numpy.typing as npt
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _get_hamming_distance(
|
|
25
|
+
bitstring_1: npt.NDArray[np.int8], bitstring_2: npt.NDArray[np.int8]
|
|
26
|
+
) -> int:
|
|
27
|
+
"""Calculates the Hamming distance between two bitstrings.
|
|
28
|
+
Args:
|
|
29
|
+
bitstring_1: Bitstring 1
|
|
30
|
+
bitstring_2: Bitstring 2
|
|
31
|
+
Returns: The Hamming distance
|
|
32
|
+
"""
|
|
33
|
+
return (bitstring_1 ^ bitstring_2).sum().item()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _bitstrings_to_probs(
|
|
37
|
+
bitstrings: npt.NDArray[np.int8],
|
|
38
|
+
) -> tuple[npt.NDArray[np.int8], npt.NDArray[Any]]:
|
|
39
|
+
"""Given a list of bitstrings from different measurements returns a probability distribution.
|
|
40
|
+
Args:
|
|
41
|
+
bitstrings: The bitstring
|
|
42
|
+
Returns:
|
|
43
|
+
A tuple of bitstrings and their corresponding probabilities.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
num_shots = bitstrings.shape[0]
|
|
47
|
+
unique_bitstrings, counts = np.unique(bitstrings, return_counts=True, axis=0)
|
|
48
|
+
probs = counts / num_shots
|
|
49
|
+
|
|
50
|
+
return (unique_bitstrings, probs)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _bitstring_format_helper(
|
|
54
|
+
measured_bitstrings: npt.NDArray[np.int8], subsystem: Sequence[int] | None = None
|
|
55
|
+
) -> npt.NDArray[np.int8]:
|
|
56
|
+
"""Formats the bitstring for analysis based on the selected subsystem.
|
|
57
|
+
Args:
|
|
58
|
+
measured_bitstrings: List of sampled measurement outcomes as a numpy array of bitstrings.
|
|
59
|
+
subsystem: Subsystem of interest
|
|
60
|
+
Returns: The bitstring string for the subsystem
|
|
61
|
+
"""
|
|
62
|
+
if subsystem is None:
|
|
63
|
+
return measured_bitstrings
|
|
64
|
+
|
|
65
|
+
return measured_bitstrings[:, :, subsystem]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _compute_bitstrings_contribution_to_purity(bitstrings: npt.NDArray[np.int8]) -> float:
|
|
69
|
+
"""Computes the contribution to the purity of the bitstrings.
|
|
70
|
+
Args:
|
|
71
|
+
bitstrings: The bitstrings measured using the same unitary operators
|
|
72
|
+
Returns: The purity of the bitstring
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
bitstrings, probs = _bitstrings_to_probs(bitstrings)
|
|
76
|
+
purity = 0
|
|
77
|
+
for (s, p), (s_prime, p_prime) in product(zip(bitstrings, probs), repeat=2):
|
|
78
|
+
purity += (-2.0) ** float(-_get_hamming_distance(s, s_prime)) * p * p_prime
|
|
79
|
+
|
|
80
|
+
return purity * 2 ** (bitstrings.shape[-1])
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def process_renyi_entropy_from_bitstrings(
|
|
84
|
+
measured_bitstrings: npt.NDArray[np.int8],
|
|
85
|
+
subsystem: tuple[int] | None = None,
|
|
86
|
+
pool: Optional[ThreadPoolExecutor] = None,
|
|
87
|
+
) -> float:
|
|
88
|
+
"""Compute the Rényi entropy of an array of bitstrings.
|
|
89
|
+
Args:
|
|
90
|
+
measured_bitstrings: List of sampled measurement outcomes as a numpy array of bitstrings.
|
|
91
|
+
subsystem: Subsystem of interest
|
|
92
|
+
pool: ThreadPoolExecutor used to paralelleize the computation.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
A float indicating the computed entropy.
|
|
96
|
+
"""
|
|
97
|
+
bitstrings = _bitstring_format_helper(measured_bitstrings, subsystem)
|
|
98
|
+
num_shots = bitstrings.shape[1]
|
|
99
|
+
num_qubits = bitstrings.shape[-1]
|
|
100
|
+
|
|
101
|
+
if num_shots == 1:
|
|
102
|
+
return 0
|
|
103
|
+
|
|
104
|
+
if pool is not None:
|
|
105
|
+
purities = list(pool.map(_compute_bitstrings_contribution_to_purity, list(bitstrings)))
|
|
106
|
+
purity = np.mean(purities)
|
|
107
|
+
|
|
108
|
+
else:
|
|
109
|
+
purity = np.mean(
|
|
110
|
+
[_compute_bitstrings_contribution_to_purity(bitstring) for bitstring in bitstrings]
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
purity_unbiased = purity * num_shots / (num_shots - 1) - (2**num_qubits) / (num_shots - 1)
|
|
114
|
+
|
|
115
|
+
return -np.log2(purity_unbiased)
|
cirq/qis/entropy_test.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
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 concurrent.futures import ThreadPoolExecutor
|
|
16
|
+
import pytest
|
|
17
|
+
import numpy as np
|
|
18
|
+
|
|
19
|
+
from cirq.qis.entropy import process_renyi_entropy_from_bitstrings
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.mark.parametrize('pool', [None, ThreadPoolExecutor(max_workers=1)])
|
|
23
|
+
def test_process_renyi_entropy_from_bitstrings(pool):
|
|
24
|
+
bitstrings = np.array(
|
|
25
|
+
[
|
|
26
|
+
[[0, 1, 1, 0], [1, 0, 0, 0], [0, 0, 0, 1], [0, 1, 1, 1]],
|
|
27
|
+
[[0, 1, 1, 0], [0, 1, 1, 0], [1, 1, 0, 0], [1, 1, 0, 1]],
|
|
28
|
+
[[0, 0, 1, 1], [0, 0, 0, 0], [0, 1, 1, 1], [0, 1, 1, 1]],
|
|
29
|
+
[[1, 0, 1, 1], [0, 1, 0, 0], [0, 1, 0, 1], [0, 1, 0, 0]],
|
|
30
|
+
[[1, 0, 1, 1], [0, 0, 1, 0], [0, 0, 1, 0], [0, 0, 0, 1]],
|
|
31
|
+
]
|
|
32
|
+
)
|
|
33
|
+
substsytem = (0, 1)
|
|
34
|
+
entropy = process_renyi_entropy_from_bitstrings(bitstrings, substsytem, pool)
|
|
35
|
+
assert entropy == 0.5145731728297583
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_process_renyi_entropy_from_bitstrings_safeguards_against_divide_by_0_error():
|
|
39
|
+
bitstrings = np.array([[[0, 1, 1, 0]], [[0, 1, 1, 0]], [[0, 0, 1, 1]]])
|
|
40
|
+
|
|
41
|
+
entropy = process_renyi_entropy_from_bitstrings(bitstrings)
|
|
42
|
+
assert entropy == 0
|
cirq/transformers/__init__.py
CHANGED
|
@@ -0,0 +1,114 @@
|
|
|
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 collections.abc import Mapping
|
|
16
|
+
|
|
17
|
+
from typing import cast
|
|
18
|
+
from cirq import ops, circuits
|
|
19
|
+
from cirq.transformers import transformer_api
|
|
20
|
+
import numpy as np
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _gate_in_moment(gate: ops.Gate, moment: circuits.Moment) -> bool:
|
|
24
|
+
"""Check whether `gate` is in `moment`."""
|
|
25
|
+
return any(op.gate == gate for op in moment)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@transformer_api.transformer
|
|
29
|
+
class DepolerizingNoiseTransformer:
|
|
30
|
+
"""Add local depolarizing noise after two-qubit gates in a specified circuit. More specifically,
|
|
31
|
+
with probability p, append a random non-identity two-qubit Pauli operator after each specified
|
|
32
|
+
two-qubit gate.
|
|
33
|
+
|
|
34
|
+
Attrs:
|
|
35
|
+
p: The probability with which to add noise.
|
|
36
|
+
target_gate: Add depolarizing nose after this type of gate
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self, p: float | Mapping[tuple[ops.Qid, ops.Qid], float], target_gate: ops.Gate = ops.CZ
|
|
41
|
+
):
|
|
42
|
+
"""Initialize the depolarizing noise transformer with some depolarizing probability and
|
|
43
|
+
target gate.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
p: The depolarizing probability, either a single float or a mapping from pairs of qubits
|
|
47
|
+
to floats.
|
|
48
|
+
target_gate: The gate after which to add depolarizing noise.
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
TypeError: If `p` is not either be a float or a mapping from sorted qubit pairs to
|
|
52
|
+
floats.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
if not isinstance(p, (Mapping, float)):
|
|
56
|
+
raise TypeError( # pragma: no cover
|
|
57
|
+
"p must either be a float or a mapping from" # pragma: no cover
|
|
58
|
+
+ "sorted qubit pairs to floats" # pragma: no cover
|
|
59
|
+
) # pragma: no cover
|
|
60
|
+
self.p = p
|
|
61
|
+
self.p_func = (
|
|
62
|
+
(lambda _: p)
|
|
63
|
+
if isinstance(p, (int, float))
|
|
64
|
+
else (lambda pair: cast(Mapping, p).get(pair, 0.0))
|
|
65
|
+
)
|
|
66
|
+
self.target_gate = target_gate
|
|
67
|
+
|
|
68
|
+
def __call__(
|
|
69
|
+
self,
|
|
70
|
+
circuit: circuits.AbstractCircuit,
|
|
71
|
+
rng: np.random.Generator | None = None,
|
|
72
|
+
*,
|
|
73
|
+
context: transformer_api.TransformerContext | None = None,
|
|
74
|
+
):
|
|
75
|
+
"""Apply the transformer to the given circuit.
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
circuit: The circuit to add noise to.
|
|
79
|
+
context: Not used; to satisfy transformer API.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
The transformed circuit.
|
|
83
|
+
"""
|
|
84
|
+
if rng is None:
|
|
85
|
+
rng = np.random.default_rng()
|
|
86
|
+
target_gate = self.target_gate
|
|
87
|
+
|
|
88
|
+
# add random Pauli gates with probability p after each of the specified gate
|
|
89
|
+
assert target_gate.num_qubits() == 2, "`target_gate` must be a two-qubit gate."
|
|
90
|
+
paulis = [ops.I, ops.X, ops.Y, ops.Z]
|
|
91
|
+
new_moments = []
|
|
92
|
+
for moment in circuit:
|
|
93
|
+
new_moments.append(moment)
|
|
94
|
+
if _gate_in_moment(target_gate, moment):
|
|
95
|
+
# add a new moment with the Paulis
|
|
96
|
+
target_pairs = {
|
|
97
|
+
tuple(sorted(op.qubits)) for op in moment.operations if op.gate == target_gate
|
|
98
|
+
}
|
|
99
|
+
added_moment_ops = []
|
|
100
|
+
for pair in target_pairs:
|
|
101
|
+
pair_sorted_tuple = (pair[0], pair[1])
|
|
102
|
+
p_i = self.p_func(pair_sorted_tuple)
|
|
103
|
+
apply = rng.choice([True, False], p=[p_i, 1 - p_i])
|
|
104
|
+
if apply:
|
|
105
|
+
choices = [
|
|
106
|
+
(pauli_a(pair[0]), pauli_b(pair[1]))
|
|
107
|
+
for pauli_a in paulis
|
|
108
|
+
for pauli_b in paulis
|
|
109
|
+
][1:]
|
|
110
|
+
pauli_to_apply = rng.choice(np.array(choices, dtype=object))
|
|
111
|
+
added_moment_ops.append(pauli_to_apply)
|
|
112
|
+
if len(added_moment_ops) > 0:
|
|
113
|
+
new_moments.append(circuits.Moment(*added_moment_ops))
|
|
114
|
+
return circuits.Circuit.from_moments(*new_moments)
|
|
@@ -0,0 +1,53 @@
|
|
|
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 cirq import ops, circuits, devices
|
|
16
|
+
import cirq.transformers.noise_adding as na
|
|
17
|
+
import numpy as np
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_noise_adding():
|
|
21
|
+
qubits = devices.LineQubit.range(4)
|
|
22
|
+
one_layer = circuits.Circuit(ops.CZ(*qubits[:2]), ops.CZ(*qubits[2:]))
|
|
23
|
+
circuit = one_layer * 10
|
|
24
|
+
|
|
25
|
+
# test that p=0 does nothing
|
|
26
|
+
transformed_circuit_p0 = na.DepolerizingNoiseTransformer(0.0)(circuit)
|
|
27
|
+
assert transformed_circuit_p0 == circuit
|
|
28
|
+
|
|
29
|
+
# test that p=1 doubles the circuit depth
|
|
30
|
+
transformed_circuit_p1 = na.DepolerizingNoiseTransformer(1.0)(circuit)
|
|
31
|
+
assert len(transformed_circuit_p1) == 20
|
|
32
|
+
|
|
33
|
+
# test that we get a deterministic result when using a specific rng
|
|
34
|
+
rng = np.random.default_rng(0)
|
|
35
|
+
transformed_circuit_p0_03 = na.DepolerizingNoiseTransformer(0.03)(circuit, rng=rng)
|
|
36
|
+
expected_circuit = (
|
|
37
|
+
one_layer * 2
|
|
38
|
+
+ circuits.Circuit(ops.I(qubits[2]), ops.Z(qubits[3]))
|
|
39
|
+
+ one_layer * 4
|
|
40
|
+
+ circuits.Circuit(ops.Z(qubits[0]), ops.X(qubits[1]))
|
|
41
|
+
+ one_layer * 4
|
|
42
|
+
+ circuits.Circuit(ops.I(qubits[2]), ops.X(qubits[3]))
|
|
43
|
+
)
|
|
44
|
+
assert transformed_circuit_p0_03 == expected_circuit
|
|
45
|
+
|
|
46
|
+
# test that supplying a dictionary for p works
|
|
47
|
+
transformed_circuit_p_dict = na.DepolerizingNoiseTransformer(
|
|
48
|
+
{tuple(qubits[:2]): 1.0, tuple(qubits[2:]): 0.0}
|
|
49
|
+
)(circuit)
|
|
50
|
+
assert len(transformed_circuit_p_dict) == 20 # depth should be doubled
|
|
51
|
+
assert transformed_circuit_p_dict[1::2].all_qubits() == frozenset(
|
|
52
|
+
qubits[:2]
|
|
53
|
+
) # no single-qubit gates get added to qubits[2:]
|
|
@@ -0,0 +1,109 @@
|
|
|
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 collections.abc import Sequence
|
|
16
|
+
from typing import Any, Literal
|
|
17
|
+
|
|
18
|
+
import cirq
|
|
19
|
+
import numpy as np
|
|
20
|
+
from cirq.transformers import transformer_api
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@transformer_api.transformer
|
|
24
|
+
class RandomizedMeasurements:
|
|
25
|
+
"""A transformer that appends a moment of random rotations to map qubits to
|
|
26
|
+
random pauli bases."""
|
|
27
|
+
|
|
28
|
+
def __init__(self, subsystem: Sequence[int] | None = None):
|
|
29
|
+
"""Class structure for performing and analyzing a general randomized measurement protocol.
|
|
30
|
+
For more details on the randomized measurement toolbox see https://arxiv.org/abs/2203.11374
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
subsystem: The specific subsystem (e.g qubit index) to measure in random basis
|
|
34
|
+
"""
|
|
35
|
+
self.subsystem = subsystem
|
|
36
|
+
|
|
37
|
+
def __call__(
|
|
38
|
+
self,
|
|
39
|
+
circuit: 'cirq.AbstractCircuit',
|
|
40
|
+
rng: np.random.Generator | None = None,
|
|
41
|
+
*,
|
|
42
|
+
context: transformer_api.TransformerContext | None = None,
|
|
43
|
+
):
|
|
44
|
+
"""Apply the transformer to the given circuit. Given an input circuit returns
|
|
45
|
+
a list of circuits with the pre-measurement unitaries. If no arguments are specified,
|
|
46
|
+
it will default to computing the entropy of the entire circuit.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
circuit: The circuit to add randomized measurements to.
|
|
50
|
+
rng: Random number generator.
|
|
51
|
+
context: Not used; to satisfy transformer API.
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
List of circuits with pre-measurement unitaries and measurements added
|
|
55
|
+
"""
|
|
56
|
+
if rng is None:
|
|
57
|
+
rng = np.random.default_rng()
|
|
58
|
+
|
|
59
|
+
qubits = sorted(circuit.all_qubits())
|
|
60
|
+
num_qubits = len(qubits)
|
|
61
|
+
|
|
62
|
+
pre_measurement_unitaries_list = self._generate_unitaries_list(rng, num_qubits)
|
|
63
|
+
pre_measurement_moment = self.unitaries_to_moment(pre_measurement_unitaries_list, qubits)
|
|
64
|
+
|
|
65
|
+
return cirq.Circuit.from_moments(
|
|
66
|
+
*circuit.moments, pre_measurement_moment, cirq.M(*qubits, key='m')
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
def _generate_unitaries_list(self, rng: np.random.Generator, num_qubits: int) -> Sequence[Any]:
|
|
70
|
+
"""Generates a list of pre-measurement unitaries."""
|
|
71
|
+
|
|
72
|
+
pauli_strings = rng.choice(["X", "Y", "Z"], size=num_qubits)
|
|
73
|
+
|
|
74
|
+
if self.subsystem is not None:
|
|
75
|
+
for i in range(pauli_strings.shape[0]):
|
|
76
|
+
if i not in self.subsystem:
|
|
77
|
+
pauli_strings[i] = np.array("Z")
|
|
78
|
+
|
|
79
|
+
return pauli_strings.tolist()
|
|
80
|
+
|
|
81
|
+
def unitaries_to_moment(
|
|
82
|
+
self, unitaries: Sequence[Literal["X", "Y", "Z"]], qubits: Sequence[Any]
|
|
83
|
+
) -> 'cirq.Moment':
|
|
84
|
+
"""Outputs the cirq moment associated with the pre-measurement rotations.
|
|
85
|
+
Args:
|
|
86
|
+
unitaries: List of pre-measurement unitaries
|
|
87
|
+
qubits: List of qubits
|
|
88
|
+
|
|
89
|
+
Returns: The cirq moment associated with the pre-measurement rotations
|
|
90
|
+
"""
|
|
91
|
+
op_list: list[cirq.Operation] = []
|
|
92
|
+
for idx, pauli in enumerate(unitaries):
|
|
93
|
+
op_list.append(_pauli_basis_rotation(pauli).on(qubits[idx]))
|
|
94
|
+
|
|
95
|
+
return cirq.Moment.from_ops(*op_list)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _pauli_basis_rotation(basis: Literal["X", "Y", "Z"]) -> 'cirq.Gate':
|
|
99
|
+
"""Given a measurement basis returns the associated rotation.
|
|
100
|
+
Args:
|
|
101
|
+
basis: Measurement basis
|
|
102
|
+
Returns: The cirq gate for associated with measurement basis
|
|
103
|
+
"""
|
|
104
|
+
if basis == "X":
|
|
105
|
+
return cirq.Ry(rads=-np.pi / 2)
|
|
106
|
+
elif basis == "Y":
|
|
107
|
+
return cirq.Rx(rads=np.pi / 2)
|
|
108
|
+
elif basis == "Z":
|
|
109
|
+
return cirq.I
|
|
@@ -0,0 +1,55 @@
|
|
|
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
|
+
import cirq
|
|
16
|
+
import cirq.transformers.randomized_measurements as rand_meas
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_randomized_measurements_appends_two_moments_on_returned_circuit():
|
|
20
|
+
# Create a 4-qubit circuit
|
|
21
|
+
q0, q1, q2, q3 = cirq.LineQubit.range(4)
|
|
22
|
+
circuit = cirq.Circuit([cirq.H(q0), cirq.CNOT(q0, q1), cirq.CNOT(q1, q2), cirq.CNOT(q2, q3)])
|
|
23
|
+
num_moments_pre = len(circuit.moments)
|
|
24
|
+
|
|
25
|
+
# Append randomized measurements to subsystem
|
|
26
|
+
circuit = rand_meas.RandomizedMeasurements()(circuit)
|
|
27
|
+
|
|
28
|
+
num_moments_post = len(circuit.moments)
|
|
29
|
+
assert num_moments_post == num_moments_pre + 2
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_append_randomized_measurements_leaves_qubits_not_in_specified_subsystem_unchanged():
|
|
33
|
+
# Create a 4-qubit circuit
|
|
34
|
+
q0, q1, q2, q3 = cirq.LineQubit.range(4)
|
|
35
|
+
circuit = cirq.Circuit([cirq.H(q0), cirq.CNOT(q0, q1), cirq.CNOT(q1, q2), cirq.CNOT(q2, q3)])
|
|
36
|
+
|
|
37
|
+
# Append randomized measurements to subsystem
|
|
38
|
+
circuit = rand_meas.RandomizedMeasurements(subsystem=(0, 1))(circuit)
|
|
39
|
+
|
|
40
|
+
# assert latter subsystems were not changed.
|
|
41
|
+
assert circuit.operation_at(q2, 4) == cirq.I(q2)
|
|
42
|
+
assert circuit.operation_at(q3, 4) == cirq.I(q3)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_append_randomized_measurements_leaves_qubits_not_in_noncontinuous_subsystem_unchanged():
|
|
46
|
+
# Create a 4-qubit circuit
|
|
47
|
+
q0, q1, q2, q3 = cirq.LineQubit.range(4)
|
|
48
|
+
circuit = cirq.Circuit([cirq.H(q0), cirq.CNOT(q0, q1), cirq.CNOT(q1, q2), cirq.CNOT(q2, q3)])
|
|
49
|
+
|
|
50
|
+
# Append randomized measurements to subsystem
|
|
51
|
+
circuit = rand_meas.RandomizedMeasurements(subsystem=(0, 2))(circuit)
|
|
52
|
+
|
|
53
|
+
# assert latter subsystems were not changed.
|
|
54
|
+
assert circuit.operation_at(q1, 4) == cirq.I(q1)
|
|
55
|
+
assert circuit.operation_at(q3, 4) == cirq.I(q3)
|
{cirq_core-1.5.0.dev20240710220908.dist-info → cirq_core-1.5.0.dev20240718000937.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cirq-core
|
|
3
|
-
Version: 1.5.0.
|
|
3
|
+
Version: 1.5.0.dev20240718000937
|
|
4
4
|
Summary: A framework for creating, editing, and invoking Noisy Intermediate Scale Quantum (NISQ) circuits.
|
|
5
5
|
Home-page: http://github.com/quantumlib/cirq
|
|
6
6
|
Author: The Cirq Developers
|
{cirq_core-1.5.0.dev20240710220908.dist-info → cirq_core-1.5.0.dev20240718000937.dist-info}/RECORD
RENAMED
|
@@ -4,8 +4,8 @@ cirq/_compat_test.py,sha256=Qq3ZcfgD-Nb81cEppQdJqhAyrVqXKtfXZYGXT0p-Wh0,34718
|
|
|
4
4
|
cirq/_doc.py,sha256=yDyWUD_2JDS0gShfGRb-rdqRt9-WeL7DhkqX7np0Nko,2879
|
|
5
5
|
cirq/_import.py,sha256=p9gMHJscbtDDkfHOaulvd3Aer0pwUF5AXpL89XR8dNw,8402
|
|
6
6
|
cirq/_import_test.py,sha256=6K_v0riZJXOXUphHNkGA8MY-JcmGlezFaGmvrNhm3OQ,1015
|
|
7
|
-
cirq/_version.py,sha256=
|
|
8
|
-
cirq/_version_test.py,sha256=
|
|
7
|
+
cirq/_version.py,sha256=Bl_c1ibn6TlivdrGoRinVKP3fETl9rzDPa7zTj3j61c,1206
|
|
8
|
+
cirq/_version_test.py,sha256=kxtQqZ0O8rcduhpinxfcBl3zdbrNKJOQtG_Ry7UAZ5g,147
|
|
9
9
|
cirq/conftest.py,sha256=X7yLFL8GLhg2CjPw0hp5e_dGASfvHx1-QT03aUbhKJw,1168
|
|
10
10
|
cirq/json_resolver_cache.py,sha256=ytePZtNZgKjOF2NiVpUTuotB-JKZmQNOFIFdvXqsxHw,13271
|
|
11
11
|
cirq/py.typed,sha256=VFSlmh_lNwnaXzwY-ZuW-C2Ws5PkuDoVgBdNCs0jXJE,63
|
|
@@ -874,11 +874,13 @@ cirq/protocols/json_test_data/sympy.Unequality.json,sha256=Bd2vedLFPwZuWyIcipOYX
|
|
|
874
874
|
cirq/protocols/json_test_data/sympy.Unequality.repr,sha256=PsC01doMaEuySfnFUQhHdcAq9tlmLzSdQ8WKe8PUaTM,54
|
|
875
875
|
cirq/protocols/json_test_data/sympy.pi.json,sha256=9LMRtr_ef-On0sxRe0vbUeXZt8cdDvvPvmmwXZ5iwnw,40
|
|
876
876
|
cirq/protocols/json_test_data/sympy.pi.repr,sha256=ZQS0my0esr3dWTZ3mWlqgR63uorPCpuSkOgnvu_x_c4,12
|
|
877
|
-
cirq/qis/__init__.py,sha256=
|
|
877
|
+
cirq/qis/__init__.py,sha256=YxFbb_S4mnR1OlAiiz8rst8vtU7o_1DBr0-YjMeROJA,1903
|
|
878
878
|
cirq/qis/channels.py,sha256=AxKgLUbWLrb1pz9xLtSpYm_stjN-IWOwLFYC9YZSons,12798
|
|
879
879
|
cirq/qis/channels_test.py,sha256=iqkSyfvx_c2ZpJKAVEsFyZRmKpzNrJsyFjHbaJUMqcQ,14454
|
|
880
880
|
cirq/qis/clifford_tableau.py,sha256=X25eCXWEUWzoB-x6cna4LdLMFVNxUiIEUxKHJkkc2aE,25353
|
|
881
881
|
cirq/qis/clifford_tableau_test.py,sha256=Z-5YSysJw2H3AfgZxhptvUMr5GUjUFgPcAUdcust9Qk,18441
|
|
882
|
+
cirq/qis/entropy.py,sha256=H9WcGlTK0NBU3aOtRWS8HM53ghmUqLB4OXicbbmwJj0,3954
|
|
883
|
+
cirq/qis/entropy_test.py,sha256=NUT7Otn14rkoBbuOI3_PRthUf5aYxfc5tafCv0_wETw,1672
|
|
882
884
|
cirq/qis/measures.py,sha256=-e2mStl9882z4mbLwWtbdmmdJilmf6Ex2b_jSXhMX3c,12194
|
|
883
885
|
cirq/qis/measures_test.py,sha256=3LTTGdvuFfZWmFyWFeUHpg1yGc9z0VE8j7Kd7J7y8i4,10092
|
|
884
886
|
cirq/qis/noise_utils.py,sha256=wakkdQdBzOUWCf71sCxMCWwfPPAnJi5bNYsXDzGzkx0,3680
|
|
@@ -1017,7 +1019,7 @@ cirq/testing/test_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
|
1017
1019
|
cirq/testing/test_data/test_module_missing_json_test_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1018
1020
|
cirq/testing/test_data/test_module_missing_testspec/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1019
1021
|
cirq/testing/test_data/test_module_missing_testspec/json_test_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
1020
|
-
cirq/transformers/__init__.py,sha256
|
|
1022
|
+
cirq/transformers/__init__.py,sha256=lFptBKOurHHsh55pq6alfA5dZJFcPOg59di72wDr2FY,4300
|
|
1021
1023
|
cirq/transformers/align.py,sha256=B4DuX84DWd4tLfMH21JviMZSOFYu7KCOMSEMALYsQpw,2727
|
|
1022
1024
|
cirq/transformers/align_test.py,sha256=M4etT2cgESv1RdkKASguiGxEuqY7kmI1IswjSi-1jjY,7174
|
|
1023
1025
|
cirq/transformers/drop_empty_moments.py,sha256=Rtn_BrpwkLXyZBdLzwdnsnEGWTdYuf1xOPakzbpv7-w,1517
|
|
@@ -1038,10 +1040,14 @@ cirq/transformers/merge_k_qubit_gates.py,sha256=dUsswQOIHfbb6Lu37fecgrpT6_45zmDE
|
|
|
1038
1040
|
cirq/transformers/merge_k_qubit_gates_test.py,sha256=k_ROZvUebHgPy5vsNnWNMBYU4kfIkrunPsw39ZngMwo,13920
|
|
1039
1041
|
cirq/transformers/merge_single_qubit_gates.py,sha256=NRREV4Z6Ptc3mZnOUgzQePdj4H0aix17WOUwZUB7iQ8,5826
|
|
1040
1042
|
cirq/transformers/merge_single_qubit_gates_test.py,sha256=SWf1Il7Bz0iUCDM7JoDG2Yxe4p2yYr2PgViQpjByFm0,9914
|
|
1043
|
+
cirq/transformers/noise_adding.py,sha256=Ng1JoGzSJdrA7WBAOLmM5kSGWP4bqEnQc-g9kYxePNQ,4439
|
|
1044
|
+
cirq/transformers/noise_adding_test.py,sha256=C0G16cj_hGWgpAYV0L7p3_p4LkD7SR99p0DJJq1fUwk,2134
|
|
1041
1045
|
cirq/transformers/optimize_for_target_gateset.py,sha256=MxhFsCm2XgW3gdpNW4NGVmz1VdQvzKdNNCtVZDuZiVE,7229
|
|
1042
1046
|
cirq/transformers/optimize_for_target_gateset_test.py,sha256=MgAHjsPbVtd0fl2ytRz2R-LQuhSqImtrFK5F45QXkA8,19523
|
|
1043
1047
|
cirq/transformers/qubit_management_transformers.py,sha256=A7Mweu9ElLSCsy_atmgFbYlzOFXKhct5gQ5YNTjjaVU,9430
|
|
1044
1048
|
cirq/transformers/qubit_management_transformers_test.py,sha256=GGuZ4uxtFI59t9diW67_J17XQdBu9NFZjOHeMAHmm8Y,13991
|
|
1049
|
+
cirq/transformers/randomized_measurements.py,sha256=GfHI2dtx6MmHbp0akXACi44GD7CoDNB_wdmFZPyaETc,4049
|
|
1050
|
+
cirq/transformers/randomized_measurements_test.py,sha256=gvlXALIe1pm-fgNth0usSePnj0Gdx3kc40rKSbaAFHI,2265
|
|
1045
1051
|
cirq/transformers/stratify.py,sha256=EEcXD6PEdHTZAoaAfaHnsw3Hf1SftbIl19hZOU_ZnXE,10469
|
|
1046
1052
|
cirq/transformers/stratify_test.py,sha256=17ic2VAUPEGuPG2o5j98yDxQ2j2J_PN3EsPsfh5xwUk,15220
|
|
1047
1053
|
cirq/transformers/synchronize_terminal_measurements.py,sha256=p061MYYglY6HhWYYkFzIny0CtaY9LUygPg4UbGewZhQ,3842
|
|
@@ -1175,8 +1181,8 @@ cirq/work/sampler.py,sha256=JEAeQQRF3bqlO9AkOf4XbrTATDI5f5JgyM_FAUCNxao,19751
|
|
|
1175
1181
|
cirq/work/sampler_test.py,sha256=B2ZsuqGT854gQtBIAh8k0LiG9Vj5wSzcGvkxOUoTcW4,13217
|
|
1176
1182
|
cirq/work/zeros_sampler.py,sha256=x1C7cup66a43n-3tm8QjhiqJa07qcJW10FxNp9jJ59Q,2356
|
|
1177
1183
|
cirq/work/zeros_sampler_test.py,sha256=JIkpBBFPJe5Ba4142vzogyWyboG1Q1ZAm0UVGgOoZn8,3279
|
|
1178
|
-
cirq_core-1.5.0.
|
|
1179
|
-
cirq_core-1.5.0.
|
|
1180
|
-
cirq_core-1.5.0.
|
|
1181
|
-
cirq_core-1.5.0.
|
|
1182
|
-
cirq_core-1.5.0.
|
|
1184
|
+
cirq_core-1.5.0.dev20240718000937.dist-info/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
|
|
1185
|
+
cirq_core-1.5.0.dev20240718000937.dist-info/METADATA,sha256=fO-wpDwloy9E44tiQkEGy3-iAn7WKVkt3cpzheGjpmI,2007
|
|
1186
|
+
cirq_core-1.5.0.dev20240718000937.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
1187
|
+
cirq_core-1.5.0.dev20240718000937.dist-info/top_level.txt,sha256=Sz9iOxHU0IEMLSFGwiwOCaN2e9K-jFbBbtpPN1hB73g,5
|
|
1188
|
+
cirq_core-1.5.0.dev20240718000937.dist-info/RECORD,,
|
{cirq_core-1.5.0.dev20240710220908.dist-info → cirq_core-1.5.0.dev20240718000937.dist-info}/LICENSE
RENAMED
|
File without changes
|
{cirq_core-1.5.0.dev20240710220908.dist-info → cirq_core-1.5.0.dev20240718000937.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|