-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add classical simulator #6124
Add classical simulator #6124
Changes from 74 commits
87dc8f4
1b7ab94
28a1049
21efeaa
152e054
fa1102c
9795119
041c293
7cf6b87
8bff310
df7bfcc
da44625
9559af9
2f3d765
36fcce5
3b7bc89
95800f3
690ef69
867f6cf
9f9c760
154dc72
1f3f321
3d700e6
16738e9
7466c3c
bab3b0b
e4b1f58
5146a44
1d7744f
0a486b2
a5723eb
f2f2c60
30855ee
a8bc3c5
ded44ed
01aa246
15924e7
12e0571
68c85ff
886944f
a368eac
4f069a8
53d918e
9546e79
5cf16bb
d30d536
62aabfd
60a0ce1
c87689c
669d33c
3338e7c
be30e5e
686c502
b2f7835
61e4c98
7710ef2
8814187
7d1677d
962b8b2
d7c4d3c
7c68107
7d29f8b
baeeee0
ef4831a
1f9de2c
f54d232
8fa47d0
866bc4e
fc85691
07962bb
be240e8
3403f9a
6b64f7b
3c8798c
c9b7741
7dc9213
136e3c0
20bb30e
2d436f6
57d0770
5b522f6
eac993e
90f9855
36d4391
f284513
732234e
97946c6
9f08a2d
dbb83d7
2d8b903
0edea6c
ba426f8
eccf29b
89a52ae
464dd53
e32cdb2
ad62928
0a72999
1ef4f52
1b612f1
b22c47e
77c6430
11f7803
2ca4c11
de7d1c4
e32a046
4cdd45d
399ff68
6477270
ed0ae54
749200e
a62de00
0b258a2
21a7f9d
2c7e003
6168c75
93d16f7
c09aeca
d479fb2
2b9c078
c3b557e
9e6c138
41bb80e
3bc22cf
c8cd782
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,3 +50,6 @@ docs/api_docs | |
|
||
# notebook test output | ||
out | ||
|
||
#ignore vs files | ||
.vs/* |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -209,7 +209,6 @@ def _test_gate_that_allocates_qubits(gate): | |
def test_decompose_gate_that_allocates_clean_qubits( | ||
theta: float, phase_state: int, target_bitsize: int, ancilla_bitsize: int | ||
): | ||
|
||
gate = testing.PhaseUsingCleanAncilla(theta, phase_state, target_bitsize, ancilla_bitsize) | ||
_test_gate_that_allocates_qubits(gate) | ||
|
||
|
@@ -220,7 +219,6 @@ def test_decompose_gate_that_allocates_clean_qubits( | |
def test_decompose_gate_that_allocates_dirty_qubits( | ||
phase_state: int, target_bitsize: int, ancilla_bitsize: int | ||
): | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto, please revert the unrelated changes. |
||
gate = testing.PhaseUsingDirtyAncilla(phase_state, target_bitsize, ancilla_bitsize) | ||
_test_gate_that_allocates_qubits(gate) | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,110 @@ | ||||||
# Copyright 2023 The Cirq Developers | ||||||
# | ||||||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||||||
# you may not use this file except in compliance with the License. | ||||||
# You may obtain a copy of the License at | ||||||
# | ||||||
# https://www.apache.org/licenses/LICENSE-2.0 | ||||||
# | ||||||
# Unless required by applicable law or agreed to in writing, software | ||||||
# distributed under the License is distributed on an "AS IS" BASIS, | ||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||||
# See the License for the specific language governing permissions and | ||||||
# limitations under the License. | ||||||
|
||||||
from typing import Dict | ||||||
from collections import defaultdict | ||||||
from cirq.sim.simulator import SimulatesSamples | ||||||
from cirq import ops, protocols | ||||||
from cirq.study.resolver import ParamResolver | ||||||
from cirq.circuits.circuit import AbstractCircuit | ||||||
from cirq.ops.raw_types import Qid | ||||||
import numpy as np | ||||||
|
||||||
|
||||||
class ClassicalStateSimulator(SimulatesSamples): | ||||||
NoureldinYosri marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
"""basic simulator that only accepts cirq.X, cirq.ISwap, and cirq.CNOT gates | ||||||
NoureldinYosri marked this conversation as resolved.
Show resolved
Hide resolved
NoureldinYosri marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please replace the docstring with """A simulator that only accepts only gates with classical counterparts.
This simulator evolves a single state, using only gates that output a single state for each
input state. The simulator runs in linear time, at the cost of not supporting superposition.
It can be used to estimate costs and simulate circuits for simple non-quantum algorithms using
many more qubits than fully capable quantum simulators.
The supported gates are:
- cirq.X
- cirq.CNOT
- cirq.SWAP
- cirq.TOFFOLI
- cirq.measure
Args:
circuit: The circuit to simulate.
param_resolver: Parameters to run with the program.
repetitions: Number of times to repeat the run. It is expected that
this is validated greater than zero before calling this method.
Returns:
A dictionary mapping measurement keys to measurement results.
Raises:
ValueError: If one of the gates is not an X, CNOT, SWAP, TOFFOLI or a measurement.
""" |
||||||
|
||||||
Run a simulation, mimicking quantum hardware. | ||||||
NoureldinYosri marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
|
||||||
Args: | ||||||
NoureldinYosri marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
circuit: The circuit to simulate. | ||||||
param_resolver: Parameters to run with the program. | ||||||
repetitions: Number of times to repeat the run. It is expected that | ||||||
this is validated greater than zero before calling this method. | ||||||
|
||||||
Returns: | ||||||
A dictionary from measurement gate key to measurement | ||||||
results. Measurement results are stored in a 3-dimensional | ||||||
numpy array, the first dimension corresponding to the repetition. | ||||||
the second to the instance of that key in the circuit, and the | ||||||
third to the actual boolean measurement results (ordered by the | ||||||
qubits being measured.) | ||||||
|
||||||
Raises: | ||||||
ValuesError: gate is not a cirq.XGate or cirq.Cnot | ||||||
NoureldinYosri marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
""" | ||||||
|
||||||
def _run( | ||||||
self, circuit: AbstractCircuit, param_resolver: ParamResolver, repetitions: int | ||||||
) -> Dict[str, np.ndarray]: | ||||||
results_dict: Dict[str, np.ndarray] = {} | ||||||
values_dict: Dict[Qid, int] = defaultdict(int) | ||||||
param_resolver = param_resolver or ParamResolver({}) | ||||||
resolved_circuit = protocols.resolve_parameters(circuit, param_resolver) | ||||||
|
||||||
for moment in resolved_circuit: | ||||||
for op in moment: | ||||||
gate = op.gate | ||||||
if isinstance(gate, ops.XPowGate) and gate.exponent == 1: | ||||||
values_dict[op.qubits[0]] = 1 - values_dict[op.qubits[0]] | ||||||
|
||||||
elif isinstance(gate, ops.CNotPowGate) and gate.exponent == 1: | ||||||
if values_dict[op.qubits[0]] == 1: | ||||||
values_dict[op.qubits[1]] = 1 - values_dict[op.qubits[1]] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We also need to check for |
||||||
|
||||||
dstrain115 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
elif isinstance(gate, ops.SwapPowGate) and gate.exponent == 1: | ||||||
hold_qubit = values_dict[op.qubits[1]] | ||||||
values_dict[op.qubits[1]] = values_dict[op.qubits[0]] | ||||||
values_dict[op.qubits[0]] = hold_qubit | ||||||
|
||||||
elif isinstance(gate, ops.CCXPowGate) and gate.exponent == 1: | ||||||
if (values_dict[op.qubits[0]] == 1) and (values_dict[op.qubits[1]] == 1): | ||||||
values_dict[op.qubits[2]] = 1 - values_dict[op.qubits[2]] | ||||||
|
||||||
elif isinstance(gate, ops.MeasurementGate): | ||||||
qubits_in_order = op.qubits | ||||||
##add the new instance of a key to the numpy array in results dictionary | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
if (gate.key) in results_dict.keys(): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
shape = len(qubits_in_order) | ||||||
current_array = results_dict[gate.key] | ||||||
new_instance = np.zeros(shape, dtype=np.uint8) | ||||||
for bits in range(0, len(qubits_in_order)): | ||||||
new_instance[bits] = values_dict[qubits_in_order[bits]] | ||||||
results_dict[gate.key] = np.insert( | ||||||
current_array, len(current_array[0]), new_instance, axis=1 | ||||||
) | ||||||
else: | ||||||
##create the array for the results dictionary | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
new_array_shape = (repetitions, 1, len(qubits_in_order)) | ||||||
new_array = np.zeros(new_array_shape, dtype=np.uint8) | ||||||
for reps in range(0, repetitions): | ||||||
for instances in range(0, 1): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
for bits in range(0, len(qubits_in_order)): | ||||||
new_array[reps][instances][bits] = values_dict[ | ||||||
qubits_in_order[bits] | ||||||
] | ||||||
results_dict[gate.key] = new_array | ||||||
|
||||||
elif not ( | ||||||
(isinstance(gate, ops.XPowGate) and gate.exponent == 0) | ||||||
or (isinstance(gate, ops.CCXPowGate) and gate.exponent == 0) | ||||||
or (isinstance(gate, ops.SwapPowGate) and gate.exponent == 0) | ||||||
or (isinstance(gate, ops.CNotPowGate) and gate.exponent == 0) | ||||||
): | ||||||
raise ValueError( | ||||||
"Can not simulate gates other than cirq.XGate, " | ||||||
+ "cirq.CNOT, cirq.SWAP, and cirq.CCNOT" | ||||||
) | ||||||
|
||||||
return results_dict |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
# Copyright 2023 The Cirq Developers | ||
# | ||
# Licensed under the Apache License, Version 2.0 (the "License"); | ||
# you may not use this file except in compliance with the License. | ||
# You may obtain a copy of the License at | ||
# | ||
# https://www.apache.org/licenses/LICENSE-2.0 | ||
# | ||
# Unless required by applicable law or agreed to in writing, software | ||
# distributed under the License is distributed on an "AS IS" BASIS, | ||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
# See the License for the specific language governing permissions and | ||
# limitations under the License. | ||
import numpy as np | ||
import pytest | ||
import cirq | ||
import sympy | ||
|
||
|
||
class TestSimulator: | ||
NoureldinYosri marked this conversation as resolved.
Show resolved
Hide resolved
|
||
def test_x_gate(self): | ||
q0, q1 = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit() | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(cirq.X(q1)) | ||
circuit.append(cirq.X(q1)) | ||
circuit.append(cirq.measure((q0, q1), key='key')) | ||
expected_results = {'key': np.array([[[1, 0]]], dtype=np.uint8)} | ||
sim = cirq.ClassicalStateSimulator() | ||
results = sim._run(circuit=circuit, param_resolver=None, repetitions=1) | ||
np.testing.assert_equal(results, expected_results) | ||
|
||
def test_CNOT(self): | ||
q0, q1 = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit() | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(cirq.CNOT(q0, q1)) | ||
circuit.append(cirq.measure(q1, key='key')) | ||
expected_results = {'key': np.array([[[1]]], dtype=np.uint8)} | ||
sim = cirq.ClassicalStateSimulator() | ||
results = sim._run(circuit=circuit, param_resolver=None, repetitions=1) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the function is a response to the _run function in the SimulatesStates class it wont's work correctly using the public run method |
||
np.testing.assert_equal(results, expected_results) | ||
|
||
def test_Swap(self): | ||
q0, q1 = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit() | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(cirq.SWAP(q0, q1)) | ||
circuit.append(cirq.measure((q0, q1), key='key')) | ||
expected_results = {'key': np.array([[[0, 1]]], dtype=np.uint8)} | ||
sim = cirq.ClassicalStateSimulator() | ||
results = sim._run(circuit=circuit, param_resolver=None, repetitions=1) | ||
np.testing.assert_equal(results, expected_results) | ||
|
||
def test_CCNOT(self): | ||
q0, q1, q2 = cirq.LineQubit.range(3) | ||
circuit = cirq.Circuit() | ||
circuit.append(cirq.CCNOT(q0, q1, q2)) | ||
circuit.append(cirq.measure((q0, q1, q2), key='key')) | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(cirq.CCNOT(q0, q1, q2)) | ||
circuit.append(cirq.measure((q0, q1, q2), key='key')) | ||
circuit.append(cirq.X(q1)) | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(cirq.CCNOT(q0, q1, q2)) | ||
circuit.append(cirq.measure((q0, q1, q2), key='key')) | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(cirq.CCNOT(q0, q1, q2)) | ||
circuit.append(cirq.measure((q0, q1, q2), key='key')) | ||
expected_results = { | ||
'key': np.array([[[0, 0, 0], [1, 0, 0], [0, 1, 0], [1, 1, 1]]], dtype=np.uint8) | ||
} | ||
sim = cirq.ClassicalStateSimulator() | ||
results = sim._run(circuit=circuit, param_resolver=None, repetitions=1) | ||
np.testing.assert_equal(results, expected_results) | ||
|
||
def test_measurement_gate(self): | ||
q0, q1 = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit() | ||
circuit.append(cirq.measure((q0, q1), key='key')) | ||
expected_results = {'key': np.array([[[0, 0]]], dtype=np.uint8)} | ||
sim = cirq.ClassicalStateSimulator() | ||
results = sim._run(circuit=circuit, param_resolver=None, repetitions=1) | ||
np.testing.assert_equal(results, expected_results) | ||
|
||
def test_qubit_order(self): | ||
q0, q1 = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit() | ||
circuit.append(cirq.CNOT(q0, q1)) | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(cirq.measure((q0, q1), key='key')) | ||
expected_results = {'key': np.array([[[1, 0]]], dtype=np.uint8)} | ||
sim = cirq.ClassicalStateSimulator() | ||
results = sim._run(circuit=circuit, param_resolver=None, repetitions=1) | ||
np.testing.assert_equal(results, expected_results) | ||
|
||
def test_same_key_instances(self): | ||
q0, q1 = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit() | ||
circuit.append(cirq.measure((q0, q1), key='key')) | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(cirq.measure((q0, q1), key='key')) | ||
expected_results = {'key': np.array([[[0, 0], [1, 0]]], dtype=np.uint8)} | ||
sim = cirq.ClassicalStateSimulator() | ||
results = sim._run(circuit=circuit, param_resolver=None, repetitions=1) | ||
np.testing.assert_equal(results, expected_results) | ||
|
||
def test_same_key_instances_order(self): | ||
q0, q1 = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit() | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(cirq.measure((q0, q1), key='key')) | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(cirq.measure((q1, q0), key='key')) | ||
expected_results = {'key': np.array([[[1, 0], [0, 0]]], dtype=np.uint8)} | ||
sim = cirq.ClassicalStateSimulator() | ||
results = sim._run(circuit=circuit, param_resolver=None, repetitions=1) | ||
np.testing.assert_equal(results, expected_results) | ||
|
||
def test_repetitions(self): | ||
q0 = cirq.LineQubit.range(1) | ||
circuit = cirq.Circuit() | ||
circuit.append(cirq.measure(q0, key='key')) | ||
expected_results = { | ||
'key': np.array( | ||
[[[0]], [[0]], [[0]], [[0]], [[0]], [[0]], [[0]], [[0]], [[0]], [[0]]], | ||
dtype=np.uint8, | ||
) | ||
} | ||
sim = cirq.ClassicalStateSimulator() | ||
results = sim._run(circuit=circuit, param_resolver=None, repetitions=10) | ||
np.testing.assert_equal(results, expected_results) | ||
|
||
def test_multiple_gates(self): | ||
q0, q1 = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit() | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(cirq.CNOT(q0, q1)) | ||
circuit.append(cirq.CNOT(q0, q1)) | ||
circuit.append(cirq.CNOT(q0, q1)) | ||
circuit.append(cirq.X(q1)) | ||
circuit.append(cirq.measure((q0, q1), key='key')) | ||
expected_results = {'key': np.array([[[1, 0]]], dtype=np.uint8)} | ||
sim = cirq.ClassicalStateSimulator() | ||
results = sim._run(circuit=circuit, param_resolver=None, repetitions=1) | ||
np.testing.assert_equal(results, expected_results) | ||
|
||
def test_multiple_gates_order(self): | ||
q0, q1 = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit() | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(cirq.CNOT(q0, q1)) | ||
circuit.append(cirq.CNOT(q1, q0)) | ||
circuit.append(cirq.measure((q0, q1), key='key')) | ||
expected_results = {'key': np.array([[[0, 1]]], dtype=np.uint8)} | ||
sim = cirq.ClassicalStateSimulator() | ||
results = sim._run(circuit=circuit, param_resolver=None, repetitions=1) | ||
np.testing.assert_equal(results, expected_results) | ||
|
||
def test_param_resolver(self): | ||
gate = cirq.CNOT ** sympy.Symbol('t') | ||
q0, q1 = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit() | ||
circuit.append(cirq.X(q0)) | ||
circuit.append(gate(q0, q1)) | ||
circuit.append(cirq.measure((q1), key='key')) | ||
resolver = cirq.ParamResolver({'t': 0}) | ||
sim = cirq.ClassicalStateSimulator() | ||
results_with_paramter_zero = sim._run( | ||
circuit=circuit, param_resolver=resolver, repetitions=1 | ||
) | ||
resolver = cirq.ParamResolver({'t': 1}) | ||
results_with_paramter_one = sim._run( | ||
circuit=circuit, param_resolver=resolver, repetitions=1 | ||
) | ||
np.testing.assert_equal( | ||
results_with_paramter_zero, {'key': np.array([[[0]]], dtype=np.uint8)} | ||
) | ||
np.testing.assert_equal( | ||
results_with_paramter_one, {'key': np.array([[[1]]], dtype=np.uint8)} | ||
) | ||
|
||
def test_unknown_gates(self): | ||
gate = cirq.CNOT ** sympy.Symbol('t') | ||
q0, q1 = cirq.LineQubit.range(2) | ||
circuit = cirq.Circuit() | ||
circuit.append(gate(q0, q1)) | ||
circuit.append(cirq.measure((q0), key='key')) | ||
resolver = cirq.ParamResolver({'t': 0.5}) | ||
sim = cirq.ClassicalStateSimulator() | ||
with pytest.raises( | ||
ValueError, | ||
match="Can not simulate gates other than " | ||
+ "cirq.XGate, cirq.CNOT, cirq.SWAP, and cirq.CCNOT", | ||
): | ||
sim._run(circuit=circuit, param_resolver=resolver, repetitions=1) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revert this file please, since these whitespace changes are not relevant to the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1, please revert