From dd68ac3e8ebbc18be5023cde7b94ff7aab797821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Silv=C3=A9rio?= <29920212+HGSilveri@users.noreply.github.com> Date: Mon, 7 Oct 2024 18:40:08 +0200 Subject: [PATCH] Remove Simulation() and pulser.simulation (#736) --- docs/source/apidoc/simulation.rst | 4 +- pulser-core/pulser/sampler/samples.py | 2 +- pulser-core/pulser/sequence/_seq_drawer.py | 2 +- pulser-core/pulser/sequence/sequence.py | 4 +- pulser-core/pulser/simulation/__init__.py | 37 ---- .../pulser_simulation/__init__.py | 2 +- .../pulser_simulation/simresults.py | 10 +- .../pulser_simulation/simulation.py | 168 +----------------- tests/test_simulation.py | 53 +----- .../Control-Z Gate Sequence.ipynb | 4 +- ...ting Sequences with Errors and Noise.ipynb | 8 +- 11 files changed, 20 insertions(+), 274 deletions(-) delete mode 100644 pulser-core/pulser/simulation/__init__.py diff --git a/docs/source/apidoc/simulation.rst b/docs/source/apidoc/simulation.rst index 0ce479890..f49194a55 100644 --- a/docs/source/apidoc/simulation.rst +++ b/docs/source/apidoc/simulation.rst @@ -10,13 +10,11 @@ QutipEmulator :class:`QutipEmulator` is the class to simulate :class:`SequenceSamples`, that are samples of a :class:`Sequence`. It is possible to simulate directly a :class:`Sequence` object by using the class method -``QutipEmulator.from_sequence``. Since version 0.14.0, the :class:`Simulation` class is deprecated -in favour of :class:`QutipEmulator`. +``QutipEmulator.from_sequence``. .. autoclass:: pulser_simulation.simulation.QutipEmulator :members: -.. autoclass:: pulser_simulation.simulation.Simulation SimConfig ---------------------- diff --git a/pulser-core/pulser/sampler/samples.py b/pulser-core/pulser/sampler/samples.py index 9b90be669..806d44a79 100644 --- a/pulser-core/pulser/sampler/samples.py +++ b/pulser-core/pulser/sampler/samples.py @@ -518,7 +518,7 @@ def to_nested_dict( ) -> dict: """Format in the nested dictionary form. - This is the format expected by `pulser_simulation.Simulation()`. + This is the format expected by `pulser_simulation.QutipEmulator()`. Args: all_local: Forces all samples to be distributed by their diff --git a/pulser-core/pulser/sequence/_seq_drawer.py b/pulser-core/pulser/sequence/_seq_drawer.py index 7fc8f52f0..6b922462d 100644 --- a/pulser-core/pulser/sequence/_seq_drawer.py +++ b/pulser-core/pulser/sequence/_seq_drawer.py @@ -718,7 +718,7 @@ def phase_str(phi: Any) -> str: alpha=0.3, hatch="////", ) - else: + else: # pragma: no cover ax.plot( t, ys_mod[i][:total_duration], diff --git a/pulser-core/pulser/sequence/sequence.py b/pulser-core/pulser/sequence/sequence.py index f05c74b08..127d3821b 100644 --- a/pulser-core/pulser/sequence/sequence.py +++ b/pulser-core/pulser/sequence/sequence.py @@ -1886,8 +1886,8 @@ def draw( need to set this flag to False. See Also: - Simulation.draw(): Draws the provided sequence and the one used by - the solver. + QutipEmulator.draw(): Draws the provided sequence and the one used + by the solver. """ valid_modes = ("input", "output", "input+output") if mode not in valid_modes: diff --git a/pulser-core/pulser/simulation/__init__.py b/pulser-core/pulser/simulation/__init__.py deleted file mode 100644 index 44bd25122..000000000 --- a/pulser-core/pulser/simulation/__init__.py +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2020 Pulser Development Team -# -# 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 -# -# http://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. -"""Classes for classical emulation of a Sequence.""" - -import warnings - -main_msg = ( - "All features previously located in the 'pulser.simulation' module have " - "been moved to the 'pulser_simulation' package. " -) - -try: - import pulser_simulation - from pulser_simulation.simconfig import SimConfig - from pulser_simulation.simulation import Simulation - - warnings.warn( - main_msg + "It is recommended that all imports of/from " - "'pulser.simulation' are changed to 'pulser_simulation'.", - stacklevel=2, - ) -except ImportError: # pragma: no cover - raise ImportError( - main_msg + "Please install the 'pulser_simulation' package and import" - " all simulation-related objects directly from it. " - ) diff --git a/pulser-simulation/pulser_simulation/__init__.py b/pulser-simulation/pulser_simulation/__init__.py index 9494e5d38..cddf5e04f 100644 --- a/pulser-simulation/pulser_simulation/__init__.py +++ b/pulser-simulation/pulser_simulation/__init__.py @@ -18,7 +18,7 @@ from pulser_simulation._version import __version__ as __version__ from pulser_simulation.qutip_backend import QutipBackend from pulser_simulation.simconfig import SimConfig -from pulser_simulation.simulation import QutipEmulator, Simulation +from pulser_simulation.simulation import QutipEmulator __all__ = [ "EmulatorConfig", diff --git a/pulser-simulation/pulser_simulation/simresults.py b/pulser-simulation/pulser_simulation/simresults.py index 493a28691..acfd65228 100644 --- a/pulser-simulation/pulser_simulation/simresults.py +++ b/pulser-simulation/pulser_simulation/simresults.py @@ -180,7 +180,7 @@ def _get_index_from_time(self, t_float: float, tol: float = 1.0e-3) -> int: return int(np.where(abs(t_float - self._sim_times) < tol)[0][0]) except IndexError: raise IndexError( - f"Given time {t_float} is absent from Simulation times within" + f"Given time {t_float} is absent from simulation times within" + f" tolerance {tol}." ) @@ -229,7 +229,7 @@ class NoisyResults(SimulationResults): Contrary to a CoherentResults object, this object contains a list of Counters describing the state distribution at the time it was created by - using Simulation.run() with a noisy simulation. + using QutipEmulator.run() with a noisy simulation. Contains methods for studying the populations and extracting useful information from them. """ @@ -266,7 +266,7 @@ def __init__( 'all' or 'all_with_error', and to 'ground-rydberg', 'XY', 'digital' if given respectively 'ground-rydberg_with_error', 'XY_with_error' or 'digital_with_error'. - sim_times: Times at which Simulation object returned + sim_times: Times at which QutipEmulator object returned the results. n_measures: Number of measurements needed to compute this result when doing the simulation. @@ -381,7 +381,7 @@ def __init__( basis_name: The basis indicating the addressed atoms after the pulse sequence ('ground-rydberg', 'digital' or 'all' or one of these bases with the suffix "_with_error"). - sim_times: Times at which Simulation object returned the + sim_times: Times at which QutipEmulator object returned the results. meas_basis: The basis in which a sampling measurement is desired (must be in "ground-rydberg" or "digital"). @@ -463,7 +463,7 @@ def get_final_state( tol: float = 1e-6, normalize: bool = True, ) -> qutip.Qobj: - """Returns the final state of the Simulation. + """Returns the final state of the simulation. Args: reduce_to_basis: Reduces the full state vector diff --git a/pulser-simulation/pulser_simulation/simulation.py b/pulser-simulation/pulser_simulation/simulation.py index 4cffff322..3b3061890 100644 --- a/pulser-simulation/pulser_simulation/simulation.py +++ b/pulser-simulation/pulser_simulation/simulation.py @@ -34,7 +34,7 @@ from pulser.register.base_register import BaseRegister from pulser.result import SampledResult from pulser.sampler.samples import ChannelSamples, SequenceSamples -from pulser.sequence._seq_drawer import draw_samples, draw_sequence +from pulser.sequence._seq_drawer import draw_samples from pulser_simulation.hamiltonian import Hamiltonian from pulser_simulation.qutip_result import QutipResult from pulser_simulation.simconfig import SimConfig @@ -805,169 +805,3 @@ def from_sequence( config, evaluation_times, ) - - -class Simulation: - r"""Simulation of a pulse sequence using QuTiP. - - Warning: - This class is deprecated in favour of ``QutipEmulator.from_sequence``. - - Args: - sequence: An instance of a Pulser Sequence that we - want to simulate. - sampling_rate: The fraction of samples that we wish to - extract from the pulse sequence to simulate. Has to be a - value between 0.05 and 1.0. - config: Configuration to be used for this simulation. - evaluation_times: Choose between: - - - "Full": The times are set to be the ones used to define the - Hamiltonian to the solver. - - - "Minimal": The times are set to only include initial and final - times. - - - An ArrayLike object of times in µs if you wish to only include - those specific times. - - - A float to act as a sampling rate for the resulting state. - with_modulation: Whether to simulated the sequence with the programmed - input or the expected output. - """ - - def __init__( - self, - sequence: Sequence, - sampling_rate: float = 1.0, - config: Optional[SimConfig] = None, - evaluation_times: Union[float, str, ArrayLike] = "Full", - with_modulation: bool = False, - ) -> None: - """Instantiates a Simulation object.""" - with warnings.catch_warnings(): - warnings.simplefilter("always") - warnings.warn( - DeprecationWarning( - "The `Simulation` class is deprecated," - " use `QutipEmulator.from_sequence` instead." - ) - ) - self._seq = sequence - self._modulated = with_modulation - self._emulator = QutipEmulator.from_sequence( - self._seq, sampling_rate, config, evaluation_times, self._modulated - ) - - @property - def evaluation_times(self) -> np.ndarray: - """The times at which the results of this simulation are returned. - - Args: - value: Choose between: - - - "Full": The times are set to be the ones used to define the - Hamiltonian to the solver. - - - "Minimal": The times are set to only include initial and - final times. - - - An ArrayLike object of times in µs if you wish to only - include those specific times. - - - A float to act as a sampling rate for the resulting state. - """ - return self._emulator.evaluation_times - - @evaluation_times.setter - def evaluation_times(self, value: Union[str, ArrayLike, float]) -> None: - """Sets times at which the results of this simulation are returned.""" - with warnings.catch_warnings(): - warnings.simplefilter("always") - warnings.warn( - DeprecationWarning( - "Setting `evaluation_times` is deprecated," - " use `set_evaluation_times` instead." - ) - ) - self._emulator.set_evaluation_times(value) - - @property - def initial_state(self) -> qutip.Qobj: - """The initial state of the simulation. - - Args: - state: The initial state. - Choose between: - - - "all-ground" for all atoms in ground state - - An ArrayLike with a shape compatible with the system - - A Qobj object - """ - return self._emulator.initial_state - - @initial_state.setter - def initial_state(self, value: Union[str, np.ndarray, qutip.Qobj]) -> None: - """Sets the initial state of the simulation.""" - with warnings.catch_warnings(): - warnings.simplefilter("always") - warnings.warn( - DeprecationWarning( - "Setting `initial_state` is deprecated," - " use `set_initial_state` instead." - ) - ) - self._emulator.set_initial_state(value) - - def draw( - self, - draw_phase_area: bool = False, - draw_interp_pts: bool = False, - draw_phase_shifts: bool = False, - draw_phase_curve: bool = False, - fig_name: str | None = None, - kwargs_savefig: dict = {}, - ) -> None: - """Draws the input sequence and the one used by the solver. - - Args: - draw_phase_area: Whether phase and area values need - to be shown as text on the plot, defaults to False. - draw_interp_pts: When the sequence has pulses with waveforms - of type InterpolatedWaveform, draws the points of interpolation - on top of the respective waveforms (defaults to False). Can't - be used if the sequence is modulated. - draw_phase_shifts: Whether phase shift and reference - information should be added to the plot, defaults to False. - draw_phase_curve: Draws the changes in phase in its own curve - (ignored if the phase doesn't change throughout the channel). - fig_name: The name on which to save the figure. - If None the figure will not be saved. - kwargs_savefig: Keywords arguments for - ``matplotlib.pyplot.savefig``. Not applicable if `fig_name` - is ``None``. - - See Also: - Sequence.draw(): Draws the sequence in its current state. - """ - if draw_interp_pts and self._modulated: - raise ValueError( - "Can't draw the interpolation points when the sequence is " - "modulated; `draw_interp_pts` must be `False`." - ) - draw_sequence( - self._seq, - self._emulator._sampling_rate, - draw_input=not self._modulated, - draw_modulation=self._modulated, - draw_phase_area=draw_phase_area, - draw_interp_pts=draw_interp_pts, - draw_phase_shifts=draw_phase_shifts, - draw_phase_curve=draw_phase_curve, - ) - if fig_name is not None: - plt.savefig(fig_name, **kwargs_savefig) - plt.show() - - def __getattr__(self, name: str) -> Any: - return getattr(self._emulator, name) diff --git a/tests/test_simulation.py b/tests/test_simulation.py index 88439098a..c9318afec 100644 --- a/tests/test_simulation.py +++ b/tests/test_simulation.py @@ -24,7 +24,7 @@ from pulser.register.register_layout import RegisterLayout from pulser.sampler import sampler from pulser.waveforms import BlackmanWaveform, ConstantWaveform, RampWaveform -from pulser_simulation import QutipEmulator, SimConfig, Simulation +from pulser_simulation import QutipEmulator, SimConfig @pytest.fixture @@ -97,17 +97,6 @@ def matrices(): return pauli -def test_bad_import(): - with pytest.warns( - UserWarning, - match="'pulser.simulation' are changed to 'pulser_simulation'.", - ): - import pulser.simulation # noqa: F401 - - assert pulser.simulation.Simulation is Simulation - assert pulser.simulation.SimConfig is SimConfig - - def test_initialization_and_construction_of_hamiltonian(seq, mod_device): fake_sequence = {"pulse1": "fake", "pulse2": "fake"} with pytest.raises(TypeError, match="sequence has to be a valid"): @@ -569,16 +558,6 @@ def test_run(seq, patch_plt_show): ): sim.set_initial_state(qutip.Qobj(bad_initial)) - with pytest.warns( - DeprecationWarning, match="Setting `initial_state` is deprecated" - ): - with pytest.warns( - DeprecationWarning, match="The `Simulation` class is deprecated" - ): - _sim = Simulation(seq, sampling_rate=0.01) - _sim.initial_state = good_initial_qobj - assert _sim.initial_state == good_initial_qobj - sim.set_initial_state(good_initial_array) sim.run() sim.set_initial_state(good_initial_qobj) @@ -639,20 +618,6 @@ def test_eval_times(seq): sim.set_evaluation_times([0, sim.sampling_times[-1] + 10]) sim = QutipEmulator.from_sequence(seq, sampling_rate=1.0) - with pytest.warns( - DeprecationWarning, match="Setting `evaluation_times` is deprecated" - ): - with pytest.warns( - DeprecationWarning, match="The `Simulation` class is deprecated" - ): - _sim = Simulation(seq, sampling_rate=1.0) - _sim.evaluation_times = "Full" - assert np.array_equal( - _sim.evaluation_times, - np.union1d(_sim.sampling_times, [0.0, _sim._tot_duration / 1000]), - ) - assert _sim._eval_times_instruction == "Full" - sim.set_evaluation_times("Full") assert sim._eval_times_instruction == "Full" np.testing.assert_almost_equal( @@ -1570,7 +1535,7 @@ def test_effective_size_disjoint(channel_type): ) -def test_simulation_with_modulation(mod_device, reg, patch_plt_show): +def test_simulation_with_modulation(mod_device, reg): seq = Sequence(reg, mod_device) seq.declare_channel("ch0", "rydberg_global") seq.config_slm_mask({"control1"}) @@ -1646,19 +1611,5 @@ def pos_factor(qid): np.testing.assert_allclose( rydberg_samples[qid]["phase"][time_slice], float(pulse1.phase) ) - with pytest.warns( - DeprecationWarning, match="The `Simulation` class is deprecated" - ): - _sim = Simulation(seq, with_modulation=True, config=sim_config) - - with pytest.raises( - ValueError, - match="Can't draw the interpolation points when the sequence " - "is modulated", - ): - _sim.draw(draw_interp_pts=True) - - with patch("matplotlib.pyplot.savefig"): - _sim.draw(draw_phase_area=True, fig_name="my_fig.pdf") # Drawing with modulation sim.draw() diff --git a/tutorials/applications/Control-Z Gate Sequence.ipynb b/tutorials/applications/Control-Z Gate Sequence.ipynb index b373c79b1..74ccaa6e8 100644 --- a/tutorials/applications/Control-Z Gate Sequence.ipynb +++ b/tutorials/applications/Control-Z Gate Sequence.ipynb @@ -362,7 +362,7 @@ " state_id\n", " ) # constructs seq, prep_state and prep_time\n", "\n", - " # Construct Simulation instance\n", + " # Construct QutipEmulator instance\n", " simul = QutipEmulator.from_sequence(seq)\n", " res = simul.run()\n", "\n", @@ -483,7 +483,7 @@ " # Get CCZ sequence\n", " prep_state, prep_time = CCZ_sequence(state_id)\n", "\n", - " # Construct Simulation instance\n", + " # Construct QutipEmulator instance\n", " simul = QutipEmulator.from_sequence(seq)\n", "\n", " res = simul.run()\n", diff --git a/tutorials/classical_simulation/Simulating Sequences with Errors and Noise.ipynb b/tutorials/classical_simulation/Simulating Sequences with Errors and Noise.ipynb index e76c96f12..278119206 100644 --- a/tutorials/classical_simulation/Simulating Sequences with Errors and Noise.ipynb +++ b/tutorials/classical_simulation/Simulating Sequences with Errors and Noise.ipynb @@ -230,7 +230,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "To do so, we can create a new `SimConfig` object and assign it to the `config` field of `sim` via the `Simulation.set_config` setter. We pass noise types as a tuple of strings to the `SimConfig` object created." + "To do so, we can create a new `SimConfig` object and assign it to the `config` field of `sim` via the `QutipEmulator.set_config` setter. We pass noise types as a tuple of strings to the `SimConfig` object created." ] }, { @@ -330,7 +330,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "When dealing with a `SimConfig` object with different noise parameters from the config in `Simulation.config`, you can \"add\" both configurations together, obtaining a single `SimConfig` with all noises from both configurations - on the other hand, the `runs` and `samples_per_run` will always be updated. This adds simulation parameters to noises that weren't available in the former `Simulation.config`. Noises specified in both `SimConfigs` will keep the noise parameters in `Simulation.config`. Try it out with `Simulation.add_config`:" + "When dealing with a `SimConfig` object with different noise parameters from the config in `QutipEmulator.config`, you can \"add\" both configurations together, obtaining a single `SimConfig` with all noises from both configurations - on the other hand, the `runs` and `samples_per_run` will always be updated. This adds simulation parameters to noises that weren't available in the former `QutipEmulator.config`. Noises specified in both `SimConfigs` will keep the noise parameters in `QutipEmulator.config`. Try it out with `QutipEmulator.add_config`:" ] }, { @@ -375,7 +375,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We created a noisy simulation using `set_config` and `add_config` methods of the the Simulation class. But it's not the only way, you can directly create a noisy simulation when you create your simulation object." + "We created a noisy simulation using `set_config` and `add_config` methods of the the QutipEmulator class. But it's not the only way, you can directly create a noisy simulation when you create your simulation object." ] }, { @@ -422,7 +422,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "As a `Simulation` field, `evaluation_times` refers to the times at which you sample during your simulation. You can assign it different types of values:\n", + "As a `QutipEmulator` field, `evaluation_times` refers to the times at which you sample during your simulation. You can assign it different types of values:\n", "\n", "* A float between 0 and 1: indicating the fraction of the full time array at which you want to sample.\n", "* The string `'Full'` to evaluate every ns or `'Minimal'` to evaluate only at the start and the end.\n",