Skip to content
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

Update ClassicalSimulator to use simulation infrastructure #6432

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
106 commits
Select commit Hold shift + click to select a range
0a68ee5
Merge pull request #1 from quantumlib/master
shef4 Sep 28, 2023
9698377
router mishandling mid-circuit measurements
shef4 Sep 28, 2023
f68471d
updated editable install instr to add all packages
shef4 Sep 29, 2023
8c5816e
Merge pull request #2 from quantumlib/master
shef4 Sep 29, 2023
3a5828f
removed debug prints
shef4 Sep 29, 2023
4e4eb61
Merge pull request #4 from shef4/developer-enviroment-guideline-update
shef4 Sep 29, 2023
cf32124
Merge pull request #6 from quantumlib/master
shef4 Oct 25, 2023
44707af
add code to Break intermediate measurements
shef4 Nov 17, 2023
9f0bf30
removing intended changes
shef4 Nov 17, 2023
337aea8
updated sytanx and functionailty
shef4 Nov 17, 2023
601757b
Update development.md
shef4 Nov 17, 2023
3c28f45
clean up routing transformer notebook
shef4 Nov 17, 2023
7d4be5a
reset transformer notebook to same as main
shef4 Nov 17, 2023
01b0fea
added ignore type to key variable
shef4 Nov 17, 2023
33b89f9
Clearified error, comment and test information
shef4 Nov 20, 2023
93591b2
clarified test function names
shef4 Nov 20, 2023
9902705
Merge branch 'master' into #6293_routcqc_stage_2
shef4 Nov 20, 2023
d5e8338
reduced line length for comment and error msg
shef4 Nov 20, 2023
78386f2
updated error message
shef4 Nov 20, 2023
671c5b4
removed trailing white space
shef4 Nov 21, 2023
1f1eaad
Merge branch 'master' into #6293_routcqc_stage_2
shef4 Nov 21, 2023
8b7ef8e
Merge pull request #8 from shef4/#6293_routcqc_stage_2
shef4 Nov 21, 2023
b41c643
started drafts of class refactor and Todos
shef4 Nov 22, 2023
a5920a3
completed class infa and started test updates
shef4 Dec 14, 2023
e676b0f
removed test changes for now
shef4 Jan 5, 2024
e156b72
testing code changes
shef4 Jan 6, 2024
553cd5c
saving current changes before sync
shef4 Jan 15, 2024
e8d9bdf
Merge branch 'main' into refactor_classical_simulator_with_custom_sim…
shef4 Jan 15, 2024
36da4fe
cleaned up code and restored original test file
shef4 Jan 30, 2024
392540c
CI bug fixes
shef4 Jan 30, 2024
1c4bbf4
format fix
shef4 Jan 30, 2024
e3febef
format fix pt 2
shef4 Jan 30, 2024
7d01a6e
changed from state interface to simulator
shef4 Feb 2, 2024
ad3e307
changed class to update inherited _run function
shef4 Feb 2, 2024
ec4615c
cleaned up format
shef4 Feb 2, 2024
0ca1517
removed new line format
shef4 Feb 2, 2024
d0058e8
added new line at end of file
shef4 Feb 2, 2024
5382aa9
removed white space
shef4 Feb 2, 2024
3cc1f70
added class description added default state_type
shef4 Feb 2, 2024
b12a3db
simplifed deafult state_type SimulattionState
shef4 Feb 2, 2024
53e1c00
freduced method description length a
shef4 Feb 2, 2024
3174050
removed white space
shef4 Feb 2, 2024
0faa8e6
added different default state_type
shef4 Feb 2, 2024
608d241
updated deafult value
shef4 Feb 2, 2024
b5dda14
removed state_type
shef4 Feb 2, 2024
55d0431
formating issue
shef4 Feb 2, 2024
e11a0c2
set _create_partial_simulation_state to raise
shef4 Feb 3, 2024
f078ab4
formating coverage test
shef4 Feb 3, 2024
6b1c3ec
removed extra arg value
shef4 Feb 3, 2024
4440d12
added default simulation state and test
shef4 Feb 6, 2024
458edaa
formatting fix
shef4 Feb 6, 2024
d42c1dd
removed extra line
shef4 Feb 6, 2024
6fda8eb
type check fixes
shef4 Feb 6, 2024
0b37a28
format check fixes
shef4 Feb 6, 2024
52d9d90
removed trailing whitespace
shef4 Feb 6, 2024
c83adf7
updated match custom state test
shef4 Feb 7, 2024
8029d6a
revert to orginal run function
shef4 Feb 7, 2024
2e16a6c
coverage test
shef4 Feb 7, 2024
54c4cd0
added test for simulator with no custom state
shef4 Feb 8, 2024
a16924c
formating
shef4 Feb 8, 2024
3bd32f0
format
shef4 Feb 8, 2024
0550b8b
cleaned up test
shef4 Feb 8, 2024
9be68c8
added test and with classicalstate
shef4 Feb 8, 2024
21f999a
formatting
shef4 Feb 8, 2024
f5563a3
clean format and removed debug file
shef4 Feb 8, 2024
7f39784
bug fix
shef4 Feb 8, 2024
2a67924
Merge branch 'main' into refactor_classical_simulator_with_custom_sim…
shef4 Feb 8, 2024
ea76c43
cleaned up changes
shef4 Feb 8, 2024
9b63210
format and lint fixes
shef4 Feb 8, 2024
8eac4a9
added coverage test for Trial Result
shef4 Feb 8, 2024
938a00f
formatting
shef4 Feb 8, 2024
6e6a3f9
removed test input parameters
shef4 Feb 8, 2024
93e2eff
removed args from test function
shef4 Feb 8, 2024
ed8fe0b
removed type_check
shef4 Feb 8, 2024
c7bdb11
reverted change lol
shef4 Feb 8, 2024
19cc944
review updates - inprogress
shef4 Feb 13, 2024
dd7c316
review updates and format fix
shef4 Feb 13, 2024
6adf484
formating fix
shef4 Feb 13, 2024
1c16227
updated doc string
shef4 Feb 13, 2024
9e0b4d0
working on initial_state type cases
shef4 Feb 13, 2024
167bd49
format fix
shef4 Feb 13, 2024
b02b814
formatting fix
shef4 Feb 13, 2024
dac80b0
adding comments and deepcopy
shef4 Feb 24, 2024
2f0ebc0
working on test
shef4 Feb 24, 2024
4179b52
changed inital state type
shef4 Feb 24, 2024
0a025ee
swapped type to List[int]
shef4 Feb 24, 2024
cce74ed
reverted to Sequence[int]
shef4 Feb 24, 2024
ce247d8
fixed measure return type
shef4 Feb 24, 2024
67bcd75
type tweak
shef4 Feb 24, 2024
45399ce
aded coverage test
shef4 Feb 24, 2024
004ea5b
formatting
shef4 Feb 24, 2024
1ea7213
formatting
shef4 Feb 24, 2024
1da447e
formatting
shef4 Feb 24, 2024
e792a95
fixed comments and error messages
shef4 Feb 24, 2024
f3891ba
Merge branch 'main' into refactor_classical_simulator_with_custom_sim…
NoureldinYosri Feb 26, 2024
e761eff
Merge branch 'main' into refactor_classical_simulator_with_custom_sim…
NoureldinYosri Mar 13, 2024
17ebcea
review updates
shef4 Mar 14, 2024
5e4ecc6
format fix
shef4 Mar 14, 2024
e100b69
type fix for copy
shef4 Mar 14, 2024
b1b475d
define copy
shef4 Mar 14, 2024
88fc1d2
added dim check
shef4 Mar 15, 2024
adc7a9b
added coverage test
shef4 Mar 15, 2024
4192aaa
updated coverage test
shef4 Mar 16, 2024
0ee52a8
format fix
shef4 Mar 16, 2024
524aed8
changed basis state to List type
shef4 Mar 19, 2024
064ae51
Merge branch 'main' into refactor_classical_simulator_with_custom_sim…
NoureldinYosri Mar 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
298 changes: 211 additions & 87 deletions cirq-core/cirq/sim/classical_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,96 +12,220 @@
# 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

from typing import Dict, Generic, Any, Sequence, List, Optional, Union, TYPE_CHECKING
from copy import deepcopy, copy
from cirq import ops, qis
from cirq.value import big_endian_int_to_bits
from cirq import sim
from cirq.sim.simulation_state import TSimulationState, SimulationState
import numpy as np

if TYPE_CHECKING:
import cirq

Check warning on line 25 in cirq-core/cirq/sim/classical_simulator.py

View check run for this annotation

Codecov / codecov/patch

cirq-core/cirq/sim/classical_simulator.py#L25

Added line #L25 was not covered by tests


def _is_identity(op: ops.Operation) -> bool:
if isinstance(op.gate, (ops.XPowGate, ops.CXPowGate, ops.CCXPowGate, ops.SwapPowGate)):
return op.gate.exponent % 2 == 0
def _is_identity(action) -> bool:
"""Check if the given action is equivalent to an identity."""
gate = action.gate if isinstance(action, ops.Operation) else action
if isinstance(gate, (ops.XPowGate, ops.CXPowGate, ops.CCXPowGate, ops.SwapPowGate)):
return gate.exponent % 2 == 0
return False


class ClassicalStateSimulator(SimulatesSamples):
"""A simulator that 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.
- A measurement key is used for measurements on different numbers of qubits.
"""

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:
if _is_identity(op):
continue
if op.gate == ops.X:
(q,) = op.qubits
values_dict[q] ^= 1
elif op.gate == ops.CNOT:
c, q = op.qubits
values_dict[q] ^= values_dict[c]
elif op.gate == ops.SWAP:
a, b = op.qubits
values_dict[a], values_dict[b] = values_dict[b], values_dict[a]
elif op.gate == ops.TOFFOLI:
c1, c2, q = op.qubits
values_dict[q] ^= values_dict[c1] & values_dict[c2]
elif protocols.is_measurement(op):
measurement_values = np.array(
[[[values_dict[q] for q in op.qubits]]] * repetitions, dtype=np.uint8
)
key = op.gate.key # type: ignore
if key in results_dict:
if op._num_qubits_() != results_dict[key].shape[-1]:
raise ValueError(
f'Measurement shape {len(measurement_values)} does not match '
f'{results_dict[key].shape[-1]} in {key}.'
)
results_dict[key] = np.concatenate(
(results_dict[key], measurement_values), axis=1
)
else:
results_dict[key] = measurement_values
else:
raise ValueError(
f'{op} is not one of cirq.X, cirq.CNOT, cirq.SWAP, '
'cirq.CCNOT, or a measurement'
)

return results_dict
class ClassicalBasisState(qis.QuantumStateRepresentation):
"""Represents a classical basis state for efficient state evolution."""

def __init__(self, initial_state: Union[List[int], np.ndarray]):
"""Initializes the ClassicalBasisState object.
Args:
initial_state: The initial state in the computational basis.
"""
self.basis = initial_state

def copy(self, deep_copy_buffers: bool = True) -> 'ClassicalBasisState':
"""Creates a copy of the ClassicalBasisState object.
Args:
deep_copy_buffers: Whether to deep copy the internal buffers.
Returns:
A copy of the ClassicalBasisState object.
"""
return ClassicalBasisState(
initial_state=deepcopy(self.basis) if deep_copy_buffers else copy(self.basis)
)

def measure(
self, axes: Sequence[int], seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None
) -> List[int]:
"""Measures the density matrix.
Args:
axes: The axes to measure.
seed: The random number seed to use.
Returns:
The measurements in order.
"""
return [self.basis[i] for i in axes]


class ClassicalBasisSimState(SimulationState[ClassicalBasisState]):
"""Represents the state of a quantum simulation using classical basis states."""

def __init__(
self,
initial_state: Union[int, List[int]] = 0,
qubits: Optional[Sequence['cirq.Qid']] = None,
classical_data: Optional['cirq.ClassicalDataStore'] = None,
):
"""Initializes the ClassicalBasisSimState object.
Args:
qubits: The qubits to simulate.
initial_state: The initial state for the simulation.
classical_data: The classical data container for the simulation.
Raises:
ValueError: If qubits not provided and initial_state is int.
If initial_state is not an int, List[int], or np.ndarray.
An initial_state value of type integer is parsed in big endian order.
"""
if isinstance(initial_state, int):
if qubits is None:
raise ValueError('qubits must be provided if initial_state is not List[int]')
state = ClassicalBasisState(
big_endian_int_to_bits(initial_state, bit_count=len(qubits))
shef4 marked this conversation as resolved.
Show resolved Hide resolved
)
elif isinstance(initial_state, (list, np.ndarray)):
state = ClassicalBasisState(initial_state)
else:
raise ValueError('initial_state must be an int or List[int] or np.ndarray')
super().__init__(state=state, qubits=qubits, classical_data=classical_data)

def _act_on_fallback_(self, action, qubits: Sequence['cirq.Qid'], allow_decompose: bool = True):
"""Acts on the state with a given operation.
Args:
action: The operation to apply.
qubits: The qubits to apply the operation to.
allow_decompose: Whether to allow decomposition of the operation.
Returns:
True if the operation was applied successfully.
Raises:
ValueError: If initial_state shape for type np.ndarray is not equal to 1.
If gate is not one of X, CNOT, SWAP, CCNOT, or a measurement.
"""
if isinstance(self._state.basis, np.ndarray) and len(self._state.basis.shape) != 1:
raise ValueError('initial_state shape for type np.ndarray is not equal to 1')
gate = action.gate if isinstance(action, ops.Operation) else action
mapped_qubits = [self.qubit_map[i] for i in qubits]
if _is_identity(gate):
pass
elif gate == ops.X:
(q,) = mapped_qubits
self._state.basis[q] ^= 1
elif gate == ops.CNOT:
c, q = mapped_qubits
self._state.basis[q] ^= self._state.basis[c]
elif gate == ops.SWAP:
a, b = mapped_qubits
self._state.basis[a], self._state.basis[b] = self._state.basis[b], self._state.basis[a]
elif gate == ops.TOFFOLI:
c1, c2, q = mapped_qubits
self._state.basis[q] ^= self._state.basis[c1] & self._state.basis[c2]
else:
raise ValueError(f'{gate} is not one of X, CNOT, SWAP, CCNOT, or a measurement')
return True


class ClassicalStateStepResult(
sim.StepResultBase['ClassicalBasisSimState'], Generic[TSimulationState]
):
"""The step result provided by `ClassicalStateSimulator.simulate_moment_steps`."""


class ClassicalStateTrialResult(
sim.SimulationTrialResultBase['ClassicalBasisSimState'], Generic[TSimulationState]
):
"""The trial result provided by `ClassicalStateSimulator.simulate`."""


class ClassicalStateSimulator(
sim.SimulatorBase[
ClassicalStateStepResult['ClassicalBasisSimState'],
ClassicalStateTrialResult['ClassicalBasisSimState'],
'ClassicalBasisSimState',
],
Generic[TSimulationState],
):
"""A simulator that accepts only gates with classical counterparts."""

def __init__(
self, *, noise: 'cirq.NOISE_MODEL_LIKE' = None, split_untangled_states: bool = False
):
"""Initializes a ClassicalStateSimulator.
Args:
noise: The noise model used by the simulator.
split_untangled_states: Whether to run the simulation as a product state.
Raises:
ValueError: If noise_model is not None.
"""
if noise is not None:
raise ValueError(f'{noise=} is not supported')
super().__init__(noise=noise, split_untangled_states=split_untangled_states)
shef4 marked this conversation as resolved.
Show resolved Hide resolved

def _create_simulator_trial_result(
self,
params: 'cirq.ParamResolver',
measurements: Dict[str, np.ndarray],
final_simulator_state: 'cirq.SimulationStateBase[ClassicalBasisSimState]',
) -> 'ClassicalStateTrialResult[ClassicalBasisSimState]':
"""Creates a trial result for the simulator.
Args:
params: The parameter resolver for the simulation.
measurements: The measurement results.
final_simulator_state: The final state of the simulator.
Returns:
A trial result for the simulator.
"""
return ClassicalStateTrialResult(
params, measurements, final_simulator_state=final_simulator_state
)

def _create_step_result(
self, sim_state: 'cirq.SimulationStateBase[ClassicalBasisSimState]'
) -> 'ClassicalStateStepResult[ClassicalBasisSimState]':
"""Creates a step result for the simulator.
Args:
sim_state: The current state of the simulator.
Returns:
A step result for the simulator.
"""
return ClassicalStateStepResult(sim_state)

def _create_partial_simulation_state(
self,
initial_state: Any,
qubits: Sequence['cirq.Qid'],
classical_data: 'cirq.ClassicalDataStore',
) -> 'ClassicalBasisSimState':
"""Creates a partial simulation state for the simulator.
Args:
initial_state: The initial state for the simulation.
qubits: The qubits associated with the state.
classical_data: The shared classical data container for this simulation.
Returns:
A partial simulation state.
"""
return ClassicalBasisSimState(
initial_state=initial_state, qubits=qubits, classical_data=classical_data
)
Loading