From 220e2347d483093747283ff5114ba27e3a7e6744 Mon Sep 17 00:00:00 2001 From: Jagan Kalsi <102557286+jkalsi1@users.noreply.github.com> Date: Mon, 20 Apr 2026 20:13:27 -0700 Subject: [PATCH 01/12] inital flag propagation --- .../cirq/sim/state_vector_simulation_state.py | 48 +++++++++++++++---- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/cirq-core/cirq/sim/state_vector_simulation_state.py b/cirq-core/cirq/sim/state_vector_simulation_state.py index 0e611a4c415..bffbda9c1ec 100644 --- a/cirq-core/cirq/sim/state_vector_simulation_state.py +++ b/cirq-core/cirq/sim/state_vector_simulation_state.py @@ -33,7 +33,7 @@ class _BufferedStateVector(qis.QuantumStateRepresentation): """Contains the state vector and buffer for efficient state evolution.""" - def __init__(self, state_vector: np.ndarray, buffer: np.ndarray | None = None): + def __init__(self, state_vector: np.ndarray, buffer: np.ndarray | None = None, should_preserve_initial_state: bool = True): """Initializes the object with the inputs. This initializer creates the buffer if necessary. @@ -41,14 +41,31 @@ def __init__(self, state_vector: np.ndarray, buffer: np.ndarray | None = None): Args: state_vector: The state vector, must be correctly formatted. The data is not checked for validity here due to performance concerns. - buffer: Optional, must be same shape as the state vector. If not provided, a buffer - will be created automatically. + buffer: Optional, must be same shape as the state vector. If not provided and + should_preserve_initial_state is True, a buffer will be created automatically. + If should_preserve_initial_state is False, the buffer is allocated lazily on + first use. + should_preserve_initial_state: If False, skips eager buffer allocation to avoid + the memory overhead of an extra state-vector-sized array. """ self._state_vector = state_vector - if buffer is None: - buffer = np.empty_like(state_vector) - self._buffer = buffer self._qid_shape = state_vector.shape + if buffer is not None: + self.__buffer: np.ndarray | None = buffer + elif should_preserve_initial_state: + self.__buffer = np.empty_like(state_vector) + else: + self.__buffer = None + + @property + def _buffer(self) -> np.ndarray: + if self.__buffer is None: + self.__buffer = np.empty_like(self._state_vector) + return self.__buffer + + @_buffer.setter + def _buffer(self, value: np.ndarray) -> None: + self.__buffer = value @classmethod def create( @@ -58,6 +75,7 @@ def create( qid_shape: tuple[int, ...] | None = None, dtype: type[np.complexfloating] | np.dtype[np.complexfloating] | None = None, buffer: np.ndarray | None = None, + should_preserve_initial_state: bool = True ): """Initializes the object with the inputs. @@ -84,10 +102,10 @@ def create( state_vector = initial_state.reshape(qid_shape) else: state_vector = initial_state - if np.may_share_memory(state_vector, initial_state): + if np.may_share_memory(state_vector, initial_state) and should_preserve_initial_state: state_vector = state_vector.copy() state_vector = state_vector.astype(dtype, copy=False) - return cls(state_vector, buffer) + return cls(state_vector, buffer, should_preserve_initial_state) def copy(self, deep_copy_buffers: bool = True) -> _BufferedStateVector: """Copies the object. @@ -97,9 +115,19 @@ def copy(self, deep_copy_buffers: bool = True) -> _BufferedStateVector: Returns: A copy of the object. """ + if self.__buffer is None: + buf = None + preserve = False + elif deep_copy_buffers: + buf = self.__buffer.copy() + preserve = True + else: + buf = self.__buffer + preserve = True return _BufferedStateVector( state_vector=self._state_vector.copy(), - buffer=self._buffer.copy() if deep_copy_buffers else self._buffer, + buffer=buf, + should_preserve_initial_state=preserve, ) def kron(self, other: _BufferedStateVector) -> _BufferedStateVector: @@ -327,6 +355,7 @@ def __init__( initial_state: np.ndarray | cirq.STATE_VECTOR_LIKE = 0, dtype: type[np.complexfloating] | np.dtype[np.complexfloating] = np.complex64, classical_data: cirq.ClassicalDataStore | None = None, + should_preserve_initial_state: bool = True, ): """Inits StateVectorSimulationState. @@ -354,6 +383,7 @@ def __init__( qid_shape=tuple(q.dimension for q in qubits) if qubits is not None else None, dtype=dtype, buffer=available_buffer, + should_preserve_initial_state=should_preserve_initial_state ) super().__init__(state=state, prng=prng, qubits=qubits, classical_data=classical_data) From 700348ff519b65c5ef4f59be654cc0ad5b034a42 Mon Sep 17 00:00:00 2001 From: Jagan Kalsi <102557286+jkalsi1@users.noreply.github.com> Date: Tue, 21 Apr 2026 10:22:25 -0700 Subject: [PATCH 02/12] tests added --- .../cirq/sim/state_vector_simulation_state.py | 11 ++- .../sim/state_vector_simulation_state_test.py | 83 +++++++++++++++++++ 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/cirq-core/cirq/sim/state_vector_simulation_state.py b/cirq-core/cirq/sim/state_vector_simulation_state.py index bffbda9c1ec..fcc57aece37 100644 --- a/cirq-core/cirq/sim/state_vector_simulation_state.py +++ b/cirq-core/cirq/sim/state_vector_simulation_state.py @@ -33,7 +33,12 @@ class _BufferedStateVector(qis.QuantumStateRepresentation): """Contains the state vector and buffer for efficient state evolution.""" - def __init__(self, state_vector: np.ndarray, buffer: np.ndarray | None = None, should_preserve_initial_state: bool = True): + def __init__( + self, + state_vector: np.ndarray, + buffer: np.ndarray | None = None, + should_preserve_initial_state: bool = True, + ): """Initializes the object with the inputs. This initializer creates the buffer if necessary. @@ -75,7 +80,7 @@ def create( qid_shape: tuple[int, ...] | None = None, dtype: type[np.complexfloating] | np.dtype[np.complexfloating] | None = None, buffer: np.ndarray | None = None, - should_preserve_initial_state: bool = True + should_preserve_initial_state: bool = True, ): """Initializes the object with the inputs. @@ -383,7 +388,7 @@ def __init__( qid_shape=tuple(q.dimension for q in qubits) if qubits is not None else None, dtype=dtype, buffer=available_buffer, - should_preserve_initial_state=should_preserve_initial_state + should_preserve_initial_state=should_preserve_initial_state, ) super().__init__(state=state, prng=prng, qubits=qubits, classical_data=classical_data) diff --git a/cirq-core/cirq/sim/state_vector_simulation_state_test.py b/cirq-core/cirq/sim/state_vector_simulation_state_test.py index a738aa086c9..272a89f4598 100644 --- a/cirq-core/cirq/sim/state_vector_simulation_state_test.py +++ b/cirq-core/cirq/sim/state_vector_simulation_state_test.py @@ -274,3 +274,86 @@ def test_measured_mixture() -> None: def test_qid_shape_error() -> None: with pytest.raises(ValueError, match="qid_shape must be provided"): cirq.sim.state_vector_simulation_state._BufferedStateVector.create(initial_state=0) + + +def test_should_preserve_initial_state_false_skips_copy() -> None: + state_vector = np.array([1.0, 0.0], dtype=np.complex64) + bsv = cirq.sim.state_vector_simulation_state._BufferedStateVector.create( + initial_state=state_vector, + qid_shape=(2,), + dtype=np.complex64, + should_preserve_initial_state=False, + ) + assert np.shares_memory(bsv._state_vector, state_vector) + + +def test_should_preserve_initial_state_true_copies() -> None: + state_vector = np.array([1.0, 0.0], dtype=np.complex64) + bsv = cirq.sim.state_vector_simulation_state._BufferedStateVector.create( + initial_state=state_vector, + qid_shape=(2,), + dtype=np.complex64, + should_preserve_initial_state=True, + ) + assert not np.shares_memory(bsv._state_vector, state_vector) + + +def test_should_preserve_initial_state_false_no_eager_buffer() -> None: + bsv = cirq.sim.state_vector_simulation_state._BufferedStateVector.create( + initial_state=0, qid_shape=(2,), dtype=np.complex64, should_preserve_initial_state=False + ) + assert bsv._BufferedStateVector__buffer is None + + +def test_should_preserve_initial_state_false_lazy_buffer_on_access() -> None: + bsv = cirq.sim.state_vector_simulation_state._BufferedStateVector.create( + initial_state=0, qid_shape=(2,), dtype=np.complex64, should_preserve_initial_state=False + ) + assert bsv._BufferedStateVector__buffer is None + buf = bsv._buffer + assert bsv._BufferedStateVector__buffer is not None + assert buf.shape == bsv._state_vector.shape + assert buf.dtype == bsv._state_vector.dtype + + +def test_should_preserve_initial_state_false_correct_simulation() -> None: + state_vector = cirq.one_hot(shape=(2, 2), dtype=np.complex64) + args = cirq.StateVectorSimulationState( + qubits=cirq.LineQubit.range(2), + initial_state=state_vector, + dtype=np.complex64, + should_preserve_initial_state=False, + ) + cirq.act_on(cirq.X, args, [cirq.LineQubit(0)]) + np.testing.assert_allclose( + args.target_tensor, cirq.one_hot(index=(1, 0), shape=(2, 2), dtype=np.complex64) + ) + + +def test_copy_with_unallocated_buffer_preserves_lazy_state() -> None: + bsv = cirq.sim.state_vector_simulation_state._BufferedStateVector.create( + initial_state=0, qid_shape=(2,), dtype=np.complex64, should_preserve_initial_state=False + ) + copy = bsv.copy() + assert copy._BufferedStateVector__buffer is None + + +def test_shallow_copy_with_unallocated_buffer() -> None: + bsv = cirq.sim.state_vector_simulation_state._BufferedStateVector.create( + initial_state=0, qid_shape=(2,), dtype=np.complex64, should_preserve_initial_state=False + ) + copy = bsv.copy(deep_copy_buffers=False) + assert copy._BufferedStateVector__buffer is None + + +def test_deep_copy_with_allocated_buffer() -> None: + bsv = cirq.sim.state_vector_simulation_state._BufferedStateVector.create( + initial_state=0, qid_shape=(2,), dtype=np.complex64, should_preserve_initial_state=True + ) + _ = bsv._buffer + copy = bsv.copy(deep_copy_buffers=True) + assert copy._BufferedStateVector__buffer is not None + assert copy._BufferedStateVector__buffer is not bsv._BufferedStateVector__buffer + np.testing.assert_array_equal( + copy._BufferedStateVector__buffer, bsv._BufferedStateVector__buffer + ) From 34f67aa50ead14a066eb547795eaa5d10944b544 Mon Sep 17 00:00:00 2001 From: Jagan Kalsi <102557286+jkalsi1@users.noreply.github.com> Date: Tue, 21 Apr 2026 10:52:48 -0700 Subject: [PATCH 03/12] readability improvements and documentation --- .../cirq/sim/state_vector_simulation_state.py | 30 +++++++------------ .../sim/state_vector_simulation_state_test.py | 18 +++++------ 2 files changed, 19 insertions(+), 29 deletions(-) diff --git a/cirq-core/cirq/sim/state_vector_simulation_state.py b/cirq-core/cirq/sim/state_vector_simulation_state.py index fcc57aece37..c1c223f13f1 100644 --- a/cirq-core/cirq/sim/state_vector_simulation_state.py +++ b/cirq-core/cirq/sim/state_vector_simulation_state.py @@ -55,22 +55,19 @@ def __init__( """ self._state_vector = state_vector self._qid_shape = state_vector.shape - if buffer is not None: - self.__buffer: np.ndarray | None = buffer - elif should_preserve_initial_state: - self.__buffer = np.empty_like(state_vector) - else: - self.__buffer = None + self._raw_buffer: np.ndarray | None = buffer + if self._raw_buffer is None and should_preserve_initial_state: + self._raw_buffer = np.empty_like(state_vector) @property def _buffer(self) -> np.ndarray: - if self.__buffer is None: - self.__buffer = np.empty_like(self._state_vector) - return self.__buffer + if self._raw_buffer is None: + self._raw_buffer = np.empty_like(self._state_vector) + return self._raw_buffer @_buffer.setter def _buffer(self, value: np.ndarray) -> None: - self.__buffer = value + self._raw_buffer = value @classmethod def create( @@ -93,6 +90,8 @@ def create( dtype: The dtype of the state vector, if the initial state is provided as an int. buffer: Optional, must be length 3 and same shape as the state vector. If not provided, a buffer will be created automatically. + should_preserve_initial_state: If False, skips copying the initial state and defers + buffer allocation to first use, reducing memory overhead for large state vectors. Raises: ValueError: If initial state is provided as integer, but qid_shape is not provided. """ @@ -120,15 +119,8 @@ def copy(self, deep_copy_buffers: bool = True) -> _BufferedStateVector: Returns: A copy of the object. """ - if self.__buffer is None: - buf = None - preserve = False - elif deep_copy_buffers: - buf = self.__buffer.copy() - preserve = True - else: - buf = self.__buffer - preserve = True + preserve = self._raw_buffer is not None + buf = self._raw_buffer.copy() if preserve and deep_copy_buffers else self._raw_buffer return _BufferedStateVector( state_vector=self._state_vector.copy(), buffer=buf, diff --git a/cirq-core/cirq/sim/state_vector_simulation_state_test.py b/cirq-core/cirq/sim/state_vector_simulation_state_test.py index 272a89f4598..42ece5800ff 100644 --- a/cirq-core/cirq/sim/state_vector_simulation_state_test.py +++ b/cirq-core/cirq/sim/state_vector_simulation_state_test.py @@ -302,16 +302,16 @@ def test_should_preserve_initial_state_false_no_eager_buffer() -> None: bsv = cirq.sim.state_vector_simulation_state._BufferedStateVector.create( initial_state=0, qid_shape=(2,), dtype=np.complex64, should_preserve_initial_state=False ) - assert bsv._BufferedStateVector__buffer is None + assert bsv._raw_buffer is None def test_should_preserve_initial_state_false_lazy_buffer_on_access() -> None: bsv = cirq.sim.state_vector_simulation_state._BufferedStateVector.create( initial_state=0, qid_shape=(2,), dtype=np.complex64, should_preserve_initial_state=False ) - assert bsv._BufferedStateVector__buffer is None + assert bsv._raw_buffer is None buf = bsv._buffer - assert bsv._BufferedStateVector__buffer is not None + assert bsv._raw_buffer is not None assert buf.shape == bsv._state_vector.shape assert buf.dtype == bsv._state_vector.dtype @@ -335,7 +335,7 @@ def test_copy_with_unallocated_buffer_preserves_lazy_state() -> None: initial_state=0, qid_shape=(2,), dtype=np.complex64, should_preserve_initial_state=False ) copy = bsv.copy() - assert copy._BufferedStateVector__buffer is None + assert copy._raw_buffer is None def test_shallow_copy_with_unallocated_buffer() -> None: @@ -343,7 +343,7 @@ def test_shallow_copy_with_unallocated_buffer() -> None: initial_state=0, qid_shape=(2,), dtype=np.complex64, should_preserve_initial_state=False ) copy = bsv.copy(deep_copy_buffers=False) - assert copy._BufferedStateVector__buffer is None + assert copy._raw_buffer is None def test_deep_copy_with_allocated_buffer() -> None: @@ -352,8 +352,6 @@ def test_deep_copy_with_allocated_buffer() -> None: ) _ = bsv._buffer copy = bsv.copy(deep_copy_buffers=True) - assert copy._BufferedStateVector__buffer is not None - assert copy._BufferedStateVector__buffer is not bsv._BufferedStateVector__buffer - np.testing.assert_array_equal( - copy._BufferedStateVector__buffer, bsv._BufferedStateVector__buffer - ) + assert copy._raw_buffer is not None + assert copy._raw_buffer is not bsv._raw_buffer + np.testing.assert_array_equal(copy._raw_buffer, bsv._raw_buffer) From 1faa160289f67ee1ebfb0944cc00c918c1c3aac6 Mon Sep 17 00:00:00 2001 From: Jagan Kalsi <102557286+jkalsi1@users.noreply.github.com> Date: Tue, 21 Apr 2026 11:00:31 -0700 Subject: [PATCH 04/12] change to use of np.may_share_memory --- cirq-core/cirq/sim/state_vector_simulation_state_test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cirq-core/cirq/sim/state_vector_simulation_state_test.py b/cirq-core/cirq/sim/state_vector_simulation_state_test.py index 42ece5800ff..f4a335a3c8e 100644 --- a/cirq-core/cirq/sim/state_vector_simulation_state_test.py +++ b/cirq-core/cirq/sim/state_vector_simulation_state_test.py @@ -284,7 +284,7 @@ def test_should_preserve_initial_state_false_skips_copy() -> None: dtype=np.complex64, should_preserve_initial_state=False, ) - assert np.shares_memory(bsv._state_vector, state_vector) + assert np.may_share_memory(bsv._state_vector, state_vector) def test_should_preserve_initial_state_true_copies() -> None: @@ -295,7 +295,7 @@ def test_should_preserve_initial_state_true_copies() -> None: dtype=np.complex64, should_preserve_initial_state=True, ) - assert not np.shares_memory(bsv._state_vector, state_vector) + assert not np.may_share_memory(bsv._state_vector, state_vector) def test_should_preserve_initial_state_false_no_eager_buffer() -> None: From 44fd47235175cd33af0c2fc642f4c449c5853483 Mon Sep 17 00:00:00 2001 From: Jagan Kalsi <102557286+jkalsi1@users.noreply.github.com> Date: Tue, 21 Apr 2026 11:02:18 -0700 Subject: [PATCH 05/12] fix mypy type inference of none in copy() --- cirq-core/cirq/sim/state_vector_simulation_state.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/sim/state_vector_simulation_state.py b/cirq-core/cirq/sim/state_vector_simulation_state.py index c1c223f13f1..343d08375c5 100644 --- a/cirq-core/cirq/sim/state_vector_simulation_state.py +++ b/cirq-core/cirq/sim/state_vector_simulation_state.py @@ -120,7 +120,10 @@ def copy(self, deep_copy_buffers: bool = True) -> _BufferedStateVector: A copy of the object. """ preserve = self._raw_buffer is not None - buf = self._raw_buffer.copy() if preserve and deep_copy_buffers else self._raw_buffer + if self._raw_buffer is not None and deep_copy_buffers: + buf = self._raw_buffer.copy() + else: + buf = self._raw_buffer return _BufferedStateVector( state_vector=self._state_vector.copy(), buffer=buf, From 2810f764ca6e6f04a3fc3e83ea607a7f640644ea Mon Sep 17 00:00:00 2001 From: Jagan Kalsi <102557286+jkalsi1@users.noreply.github.com> Date: Tue, 21 Apr 2026 11:03:56 -0700 Subject: [PATCH 06/12] switch conditional check for perf --- cirq-core/cirq/sim/state_vector_simulation_state.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/sim/state_vector_simulation_state.py b/cirq-core/cirq/sim/state_vector_simulation_state.py index 343d08375c5..f7f30534213 100644 --- a/cirq-core/cirq/sim/state_vector_simulation_state.py +++ b/cirq-core/cirq/sim/state_vector_simulation_state.py @@ -106,7 +106,7 @@ def create( state_vector = initial_state.reshape(qid_shape) else: state_vector = initial_state - if np.may_share_memory(state_vector, initial_state) and should_preserve_initial_state: + if should_preserve_initial_state and np.may_share_memory(state_vector, initial_state) : state_vector = state_vector.copy() state_vector = state_vector.astype(dtype, copy=False) return cls(state_vector, buffer, should_preserve_initial_state) From 4d745ef9cb8807835eb3e840857ceeb1acef0ef3 Mon Sep 17 00:00:00 2001 From: Jagan Kalsi <102557286+jkalsi1@users.noreply.github.com> Date: Tue, 21 Apr 2026 11:05:25 -0700 Subject: [PATCH 07/12] declare type in copy() --- cirq-core/cirq/sim/state_vector_simulation_state.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cirq-core/cirq/sim/state_vector_simulation_state.py b/cirq-core/cirq/sim/state_vector_simulation_state.py index f7f30534213..efd902755fc 100644 --- a/cirq-core/cirq/sim/state_vector_simulation_state.py +++ b/cirq-core/cirq/sim/state_vector_simulation_state.py @@ -106,7 +106,7 @@ def create( state_vector = initial_state.reshape(qid_shape) else: state_vector = initial_state - if should_preserve_initial_state and np.may_share_memory(state_vector, initial_state) : + if should_preserve_initial_state and np.may_share_memory(state_vector, initial_state): state_vector = state_vector.copy() state_vector = state_vector.astype(dtype, copy=False) return cls(state_vector, buffer, should_preserve_initial_state) @@ -120,6 +120,7 @@ def copy(self, deep_copy_buffers: bool = True) -> _BufferedStateVector: A copy of the object. """ preserve = self._raw_buffer is not None + buf: np.ndarray | None if self._raw_buffer is not None and deep_copy_buffers: buf = self._raw_buffer.copy() else: From 99e347a383c46bffd8d02340a2424562c6f4b025 Mon Sep 17 00:00:00 2001 From: Jagan Kalsi <102557286+jkalsi1@users.noreply.github.com> Date: Tue, 21 Apr 2026 11:18:05 -0700 Subject: [PATCH 08/12] tests hardened --- cirq-core/cirq/sim/state_vector_simulation_state_test.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cirq-core/cirq/sim/state_vector_simulation_state_test.py b/cirq-core/cirq/sim/state_vector_simulation_state_test.py index f4a335a3c8e..4e8c29e3b19 100644 --- a/cirq-core/cirq/sim/state_vector_simulation_state_test.py +++ b/cirq-core/cirq/sim/state_vector_simulation_state_test.py @@ -324,7 +324,9 @@ def test_should_preserve_initial_state_false_correct_simulation() -> None: dtype=np.complex64, should_preserve_initial_state=False, ) + assert args._state._raw_buffer is None cirq.act_on(cirq.X, args, [cirq.LineQubit(0)]) + assert args._state._raw_buffer is not None np.testing.assert_allclose( args.target_tensor, cirq.one_hot(index=(1, 0), shape=(2, 2), dtype=np.complex64) ) @@ -336,6 +338,8 @@ def test_copy_with_unallocated_buffer_preserves_lazy_state() -> None: ) copy = bsv.copy() assert copy._raw_buffer is None + assert not np.may_share_memory(copy._state_vector, bsv._state_vector) + np.testing.assert_array_equal(copy._state_vector, bsv._state_vector) def test_shallow_copy_with_unallocated_buffer() -> None: @@ -344,14 +348,15 @@ def test_shallow_copy_with_unallocated_buffer() -> None: ) copy = bsv.copy(deep_copy_buffers=False) assert copy._raw_buffer is None + assert not np.may_share_memory(copy._state_vector, bsv._state_vector) + np.testing.assert_array_equal(copy._state_vector, bsv._state_vector) def test_deep_copy_with_allocated_buffer() -> None: bsv = cirq.sim.state_vector_simulation_state._BufferedStateVector.create( initial_state=0, qid_shape=(2,), dtype=np.complex64, should_preserve_initial_state=True ) - _ = bsv._buffer copy = bsv.copy(deep_copy_buffers=True) assert copy._raw_buffer is not None - assert copy._raw_buffer is not bsv._raw_buffer + assert not np.may_share_memory(copy._raw_buffer, bsv._raw_buffer) np.testing.assert_array_equal(copy._raw_buffer, bsv._raw_buffer) From a335c2bbe5bddecec6a8636feeccc8402cf827dd Mon Sep 17 00:00:00 2001 From: Jagan Kalsi <102557286+jkalsi1@users.noreply.github.com> Date: Thu, 28 May 2026 09:05:21 -0700 Subject: [PATCH 09/12] fix: implement @sergeisakov advice; add a flag to bypass final_state_vector call, preventing additional memory overhead --- cirq-core/cirq/sim/state_vector_simulator.py | 22 ++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/cirq-core/cirq/sim/state_vector_simulator.py b/cirq-core/cirq/sim/state_vector_simulator.py index 22bfb2ae083..ee46f8a5302 100644 --- a/cirq-core/cirq/sim/state_vector_simulator.py +++ b/cirq-core/cirq/sim/state_vector_simulator.py @@ -139,12 +139,12 @@ def final_state_vector(self) -> np.ndarray: ) return ret # normalize only if doing so improves the round-off on total probability - ret_norm = ret / norm + ret_norm = ret * (1.0 / norm) round_off_change = abs(np.vdot(ret_norm, ret_norm) - 1) - abs(np.vdot(ret, ret) - 1) result = ret_norm if round_off_change < 0 else ret return result - def state_vector(self, copy: bool = False) -> np.ndarray: + def state_vector(self, copy: bool = False, normalize: bool = True) -> np.ndarray: """Return the state vector at the end of the computation. The state is returned in the computational basis with these basis @@ -172,11 +172,21 @@ def state_vector(self, copy: bool = False) -> np.ndarray: Args: copy: If True, the returned state vector will be a copy of that - stored by the object. This is potentially expensive for large - state vectors, but prevents mutation of the object state, e.g. for - operating on intermediate states of a circuit. - Defaults to False. + stored by the object. This is potentially expensive for large + state vectors, but prevents mutation of the object state, e.g. + for operating on intermediate states of a circuit. + Defaults to False. + normalize: If True, the returned state vector is normalized and + the result is cached. If False, the raw target tensor is + returned directly without any normalization or caching, + avoiding the extra allocation in `final_state_vector`. + Useful for memory-constrained callers such as large-qubit + simulations. + Default to True. """ + if not normalize: + ret = self._get_merged_sim_state().target_tensor.reshape(-1) + return ret.copy() if copy else ret return self.final_state_vector.copy() if copy else self.final_state_vector def _value_equality_values_(self): From 88b22d5ea734cc75bb7b083819dc7f14107b7068 Mon Sep 17 00:00:00 2001 From: Jagan Kalsi <102557286+jkalsi1@users.noreply.github.com> Date: Thu, 28 May 2026 09:22:05 -0700 Subject: [PATCH 10/12] fix: format --- cirq-core/cirq/sim/state_vector_simulator.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cirq-core/cirq/sim/state_vector_simulator.py b/cirq-core/cirq/sim/state_vector_simulator.py index ee46f8a5302..4209ccd3fe1 100644 --- a/cirq-core/cirq/sim/state_vector_simulator.py +++ b/cirq-core/cirq/sim/state_vector_simulator.py @@ -176,11 +176,11 @@ def state_vector(self, copy: bool = False, normalize: bool = True) -> np.ndarray state vectors, but prevents mutation of the object state, e.g. for operating on intermediate states of a circuit. Defaults to False. - normalize: If True, the returned state vector is normalized and - the result is cached. If False, the raw target tensor is + normalize: If True, the returned state vector is normalized and + the result is cached. If False, the raw target tensor is returned directly without any normalization or caching, - avoiding the extra allocation in `final_state_vector`. - Useful for memory-constrained callers such as large-qubit + avoiding the extra allocation in `final_state_vector`. + Useful for memory-constrained callers such as large-qubit simulations. Default to True. """ From 1151621d12fb8308263299e41fae99d101233a8c Mon Sep 17 00:00:00 2001 From: Jagan Kalsi <102557286+jkalsi1@users.noreply.github.com> Date: Thu, 28 May 2026 09:29:07 -0700 Subject: [PATCH 11/12] fix: simplify docstring and fix typo --- cirq-core/cirq/sim/state_vector_simulator.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/cirq-core/cirq/sim/state_vector_simulator.py b/cirq-core/cirq/sim/state_vector_simulator.py index 4209ccd3fe1..4bd64c024a2 100644 --- a/cirq-core/cirq/sim/state_vector_simulator.py +++ b/cirq-core/cirq/sim/state_vector_simulator.py @@ -178,11 +178,9 @@ def state_vector(self, copy: bool = False, normalize: bool = True) -> np.ndarray Defaults to False. normalize: If True, the returned state vector is normalized and the result is cached. If False, the raw target tensor is - returned directly without any normalization or caching, - avoiding the extra allocation in `final_state_vector`. - Useful for memory-constrained callers such as large-qubit - simulations. - Default to True. + returned directly, avoiding the extra allocation in + `final_state_vector`. + Defaults to True. """ if not normalize: ret = self._get_merged_sim_state().target_tensor.reshape(-1) From 3bdd80f8360aebeb6fd4f437d265377b030a9c6c Mon Sep 17 00:00:00 2001 From: Jagan Kalsi <102557286+jkalsi1@users.noreply.github.com> Date: Thu, 28 May 2026 09:32:11 -0700 Subject: [PATCH 12/12] fix: format space --- cirq-core/cirq/sim/state_vector_simulator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cirq-core/cirq/sim/state_vector_simulator.py b/cirq-core/cirq/sim/state_vector_simulator.py index 4bd64c024a2..010d03eb8d5 100644 --- a/cirq-core/cirq/sim/state_vector_simulator.py +++ b/cirq-core/cirq/sim/state_vector_simulator.py @@ -178,7 +178,7 @@ def state_vector(self, copy: bool = False, normalize: bool = True) -> np.ndarray Defaults to False. normalize: If True, the returned state vector is normalized and the result is cached. If False, the raw target tensor is - returned directly, avoiding the extra allocation in + returned directly, avoiding the extra allocation in `final_state_vector`. Defaults to True. """