From 4eed5f08c5a59792af74adf1c682e17f6924f5b5 Mon Sep 17 00:00:00 2001 From: Noureldin Date: Fri, 22 Mar 2024 23:07:24 +0000 Subject: [PATCH 1/5] Ensure the result of simulation is normalized --- cirq-core/cirq/sim/density_matrix_simulator.py | 5 +++++ .../cirq/sim/density_matrix_simulator_test.py | 16 ++++++++++++++++ cirq-core/cirq/sim/state_vector_simulator.py | 3 ++- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/sim/density_matrix_simulator.py b/cirq-core/cirq/sim/density_matrix_simulator.py index af5da908fbf..5235476dfc9 100644 --- a/cirq-core/cirq/sim/density_matrix_simulator.py +++ b/cirq-core/cirq/sim/density_matrix_simulator.py @@ -367,6 +367,11 @@ def final_density_matrix(self) -> np.ndarray: size = np.prod(protocols.qid_shape(self), dtype=np.int64) tensor = self._get_merged_sim_state().target_tensor self._final_density_matrix = np.reshape(tensor.copy(), (size, size)) + if abs(np.trace(self._final_density_matrix) - 1) < 1e-6: + # Normalize if this is a pure state. + self._final_density_matrix = self._final_density_matrix / np.trace( + self._final_density_matrix + ) return self._final_density_matrix def _value_equality_values_(self) -> Any: diff --git a/cirq-core/cirq/sim/density_matrix_simulator_test.py b/cirq-core/cirq/sim/density_matrix_simulator_test.py index 119bc3f1830..6bdd1b56dcb 100644 --- a/cirq-core/cirq/sim/density_matrix_simulator_test.py +++ b/cirq-core/cirq/sim/density_matrix_simulator_test.py @@ -1571,3 +1571,19 @@ def qubits(self): simulator.simulate_sweep(program=circuit, params=params) assert op1.count == 1 assert op2.count == 2 + + +def test_unitary_simulation_output_has_trace_one(): + q0 = cirq.NamedQubit('q0') + q1 = cirq.NamedQubit('q1') + qc = cirq.Circuit() + qc.append(cirq.CNOT.on(q1, q0)) + qc.append(cirq.H.on(q1)) + qc.append(cirq.measure(q1)) + sim = cirq.DensityMatrixSimulator() + initial_state = None + + for _ in range(100): + output = sim.simulate(qc, initial_state=initial_state) + initial_state = output.final_density_matrix + assert np.trace(initial_state) == 1 diff --git a/cirq-core/cirq/sim/state_vector_simulator.py b/cirq-core/cirq/sim/state_vector_simulator.py index 35eb3d8d5e2..cf1fc1be872 100644 --- a/cirq-core/cirq/sim/state_vector_simulator.py +++ b/cirq-core/cirq/sim/state_vector_simulator.py @@ -124,7 +124,8 @@ def __init__( @cached_property def final_state_vector(self) -> np.ndarray: - return self._get_merged_sim_state().target_tensor.reshape(-1) + ret = self._get_merged_sim_state().target_tensor.reshape(-1) + return ret / np.linalg.norm(ret) def state_vector(self, copy: bool = False) -> np.ndarray: """Return the state vector at the end of the computation. From b9fd43795287e6672637119f8a93404735b678df Mon Sep 17 00:00:00 2001 From: Noureldin Date: Fri, 22 Mar 2024 23:15:38 +0000 Subject: [PATCH 2/5] fix type --- cirq-core/cirq/sim/density_matrix_simulator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cirq-core/cirq/sim/density_matrix_simulator.py b/cirq-core/cirq/sim/density_matrix_simulator.py index 5235476dfc9..d96dac18535 100644 --- a/cirq-core/cirq/sim/density_matrix_simulator.py +++ b/cirq-core/cirq/sim/density_matrix_simulator.py @@ -369,8 +369,8 @@ def final_density_matrix(self) -> np.ndarray: self._final_density_matrix = np.reshape(tensor.copy(), (size, size)) if abs(np.trace(self._final_density_matrix) - 1) < 1e-6: # Normalize if this is a pure state. - self._final_density_matrix = self._final_density_matrix / np.trace( - self._final_density_matrix + self._final_density_matrix = self._final_density_matrix / float( + np.trace(self._final_density_matrix) ) return self._final_density_matrix From 5d6b576319ee4e5e398765177ae1b08a8c07882d Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 25 Mar 2024 11:35:05 -0700 Subject: [PATCH 3/5] store trace --- cirq-core/cirq/sim/density_matrix_simulator.py | 9 ++++----- cirq-core/cirq/sim/density_matrix_simulator_test.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/cirq-core/cirq/sim/density_matrix_simulator.py b/cirq-core/cirq/sim/density_matrix_simulator.py index d96dac18535..058fd155a7f 100644 --- a/cirq-core/cirq/sim/density_matrix_simulator.py +++ b/cirq-core/cirq/sim/density_matrix_simulator.py @@ -367,11 +367,10 @@ def final_density_matrix(self) -> np.ndarray: size = np.prod(protocols.qid_shape(self), dtype=np.int64) tensor = self._get_merged_sim_state().target_tensor self._final_density_matrix = np.reshape(tensor.copy(), (size, size)) - if abs(np.trace(self._final_density_matrix) - 1) < 1e-6: - # Normalize if this is a pure state. - self._final_density_matrix = self._final_density_matrix / float( - np.trace(self._final_density_matrix) - ) + trace = float(np.trace(self._final_density_matrix)) + if abs(trace - 1) < 1e-6: + # Normalize only if this is a pure state. + self._final_density_matrix = self._final_density_matrix / trace return self._final_density_matrix def _value_equality_values_(self) -> Any: diff --git a/cirq-core/cirq/sim/density_matrix_simulator_test.py b/cirq-core/cirq/sim/density_matrix_simulator_test.py index 6bdd1b56dcb..4134a4df19e 100644 --- a/cirq-core/cirq/sim/density_matrix_simulator_test.py +++ b/cirq-core/cirq/sim/density_matrix_simulator_test.py @@ -1583,7 +1583,7 @@ def test_unitary_simulation_output_has_trace_one(): sim = cirq.DensityMatrixSimulator() initial_state = None - for _ in range(100): + for _ in range(10): output = sim.simulate(qc, initial_state=initial_state) initial_state = output.final_density_matrix assert np.trace(initial_state) == 1 From 7856e81b4bb9181d01f34fec54e8571d8e849fff Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Mon, 25 Mar 2024 11:55:10 -0700 Subject: [PATCH 4/5] revert changes to density matrix simulator --- cirq-core/cirq/sim/density_matrix_simulator.py | 4 ---- .../cirq/sim/density_matrix_simulator_test.py | 16 ---------------- 2 files changed, 20 deletions(-) diff --git a/cirq-core/cirq/sim/density_matrix_simulator.py b/cirq-core/cirq/sim/density_matrix_simulator.py index 058fd155a7f..af5da908fbf 100644 --- a/cirq-core/cirq/sim/density_matrix_simulator.py +++ b/cirq-core/cirq/sim/density_matrix_simulator.py @@ -367,10 +367,6 @@ def final_density_matrix(self) -> np.ndarray: size = np.prod(protocols.qid_shape(self), dtype=np.int64) tensor = self._get_merged_sim_state().target_tensor self._final_density_matrix = np.reshape(tensor.copy(), (size, size)) - trace = float(np.trace(self._final_density_matrix)) - if abs(trace - 1) < 1e-6: - # Normalize only if this is a pure state. - self._final_density_matrix = self._final_density_matrix / trace return self._final_density_matrix def _value_equality_values_(self) -> Any: diff --git a/cirq-core/cirq/sim/density_matrix_simulator_test.py b/cirq-core/cirq/sim/density_matrix_simulator_test.py index 4134a4df19e..119bc3f1830 100644 --- a/cirq-core/cirq/sim/density_matrix_simulator_test.py +++ b/cirq-core/cirq/sim/density_matrix_simulator_test.py @@ -1571,19 +1571,3 @@ def qubits(self): simulator.simulate_sweep(program=circuit, params=params) assert op1.count == 1 assert op2.count == 2 - - -def test_unitary_simulation_output_has_trace_one(): - q0 = cirq.NamedQubit('q0') - q1 = cirq.NamedQubit('q1') - qc = cirq.Circuit() - qc.append(cirq.CNOT.on(q1, q0)) - qc.append(cirq.H.on(q1)) - qc.append(cirq.measure(q1)) - sim = cirq.DensityMatrixSimulator() - initial_state = None - - for _ in range(10): - output = sim.simulate(qc, initial_state=initial_state) - initial_state = output.final_density_matrix - assert np.trace(initial_state) == 1 From ad852dd87db8d6c3b10c478174771e079edc611c Mon Sep 17 00:00:00 2001 From: Nour Yosri Date: Tue, 2 Apr 2024 14:45:47 -0700 Subject: [PATCH 5/5] add warning --- cirq-core/cirq/sim/state_vector_simulator.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/sim/state_vector_simulator.py b/cirq-core/cirq/sim/state_vector_simulator.py index cf1fc1be872..e17f101b561 100644 --- a/cirq-core/cirq/sim/state_vector_simulator.py +++ b/cirq-core/cirq/sim/state_vector_simulator.py @@ -16,6 +16,7 @@ import abc from functools import cached_property from typing import Any, Dict, Iterator, Sequence, Type, TYPE_CHECKING, Generic, TypeVar +import warnings import numpy as np @@ -125,7 +126,15 @@ def __init__( @cached_property def final_state_vector(self) -> np.ndarray: ret = self._get_merged_sim_state().target_tensor.reshape(-1) - return ret / np.linalg.norm(ret) + norm = np.linalg.norm(ret) + if abs(norm - 1) > np.sqrt(np.finfo(ret.dtype).eps): + warnings.warn( + f"final state vector's {norm=} is too far from 1," + f" {abs(norm-1)} > {np.sqrt(np.finfo(ret.dtype).eps)}." + "skipping renormalization" + ) + return ret + return ret / norm def state_vector(self, copy: bool = False) -> np.ndarray: """Return the state vector at the end of the computation.