From 19df9b4801c3b64f4ff7f2cdf358557f1bb9c839 Mon Sep 17 00:00:00 2001 From: HGSilveri Date: Fri, 20 Dec 2024 16:44:32 +0100 Subject: [PATCH 01/12] Bump version to 1.3dev0 --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index 867e52437..7526ae8da 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.2.0 \ No newline at end of file +1.3dev0 \ No newline at end of file From 19fb51c5b454f9d876074fa3a2c29cab059e613f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Silv=C3=A9rio?= <29920212+HGSilveri@users.noreply.github.com> Date: Fri, 3 Jan 2025 17:38:14 +0100 Subject: [PATCH 02/12] Fix test broken by scipy 1.15 (#788) --- tests/test_simulation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_simulation.py b/tests/test_simulation.py index 58866bc67..992423b21 100644 --- a/tests/test_simulation.py +++ b/tests/test_simulation.py @@ -287,7 +287,7 @@ def _config(dim): # Check building operator with one operator op_standard = sim.build_operator([("sigma_gg", ["target"])]) op_one = sim.build_operator(("sigma_gg", ["target"])) - assert (op_standard - op_one).norm() < 1e-10 + assert (op_standard - op_one).to("dense").norm() < 1e-10 # Global ground-rydberg seq2 = Sequence(reg, DigitalAnalogDevice) From ba23444f16bc31f14f895f59cc92e0cc34b0a318 Mon Sep 17 00:00:00 2001 From: Vytautas Abramavicius <145791635+vytautas-a@users.noreply.github.com> Date: Tue, 4 Feb 2025 19:26:01 +0200 Subject: [PATCH 03/12] Add tanh function to ParamObj (#798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added tanh function to paramobj * Update tests/test_parametrized.py Co-authored-by: Henrique Silvério <29920212+HGSilveri@users.noreply.github.com> * fixed test --------- Co-authored-by: Henrique Silvério <29920212+HGSilveri@users.noreply.github.com> --- .../pulser/json/abstract_repr/schemas/sequence-schema.json | 3 ++- pulser-core/pulser/json/abstract_repr/signatures.py | 1 + pulser-core/pulser/math/__init__.py | 7 +++++++ pulser-core/pulser/parametrized/paramobj.py | 4 ++++ tests/test_abstract_repr.py | 1 + tests/test_parametrized.py | 6 ++++++ 6 files changed, 21 insertions(+), 1 deletion(-) diff --git a/pulser-core/pulser/json/abstract_repr/schemas/sequence-schema.json b/pulser-core/pulser/json/abstract_repr/schemas/sequence-schema.json index 21cf2dbad..8d829a248 100644 --- a/pulser-core/pulser/json/abstract_repr/schemas/sequence-schema.json +++ b/pulser-core/pulser/json/abstract_repr/schemas/sequence-schema.json @@ -259,7 +259,8 @@ "log", "sin", "cos", - "tan" + "tan", + "tanh" ], "type": "string" }, diff --git a/pulser-core/pulser/json/abstract_repr/signatures.py b/pulser-core/pulser/json/abstract_repr/signatures.py index 66c5510c8..e0e07f05c 100644 --- a/pulser-core/pulser/json/abstract_repr/signatures.py +++ b/pulser-core/pulser/json/abstract_repr/signatures.py @@ -118,4 +118,5 @@ def _index_var(lhs: Variable, rhs: int) -> VariableItem: "sin": np.sin, "cos": np.cos, "tan": np.tan, + "tanh": np.tanh, } diff --git a/pulser-core/pulser/math/__init__.py b/pulser-core/pulser/math/__init__.py index 11a94039a..98c424cbb 100644 --- a/pulser-core/pulser/math/__init__.py +++ b/pulser-core/pulser/math/__init__.py @@ -95,6 +95,13 @@ def tan(a: AbstractArrayLike, /) -> AbstractArray: return AbstractArray(np.tan(a.as_array())) +def tanh(a: AbstractArrayLike, /) -> AbstractArray: + a = AbstractArray(a) + if a.is_tensor: + return AbstractArray(torch.tanh(a.as_tensor())) + return AbstractArray(np.tanh(a.as_array())) + + def pad( a: AbstractArrayLike, pad_width: tuple | int, diff --git a/pulser-core/pulser/parametrized/paramobj.py b/pulser-core/pulser/parametrized/paramobj.py index a3b703872..39cd82258 100644 --- a/pulser-core/pulser/parametrized/paramobj.py +++ b/pulser-core/pulser/parametrized/paramobj.py @@ -92,6 +92,10 @@ def tan(self) -> ParamObj: """Calculates the trigonometric tangent of the object.""" return ParamObj(pm.tan, self) + def tanh(self) -> ParamObj: + """Calculates the hyperbolic tangent of the object.""" + return ParamObj(pm.tanh, self) + # Binary operators def __add__(self, other: Union[int, float], /) -> ParamObj: return ParamObj(operator.add, self, other) diff --git a/tests/test_abstract_repr.py b/tests/test_abstract_repr.py index 890226b34..0aa3853c3 100644 --- a/tests/test_abstract_repr.py +++ b/tests/test_abstract_repr.py @@ -2494,6 +2494,7 @@ def test_deserialize_parametrized_waveform(self, wf_obj): {"expression": "sin", "lhs": {"variable": "var1"}}, {"expression": "cos", "lhs": var1}, {"expression": "tan", "lhs": {"variable": "var1"}}, + {"expression": "tanh", "lhs": {"variable": "var1"}}, {"expression": "index", "lhs": {"variable": "var1"}, "rhs": 0}, { "expression": "index", diff --git a/tests/test_parametrized.py b/tests/test_parametrized.py index 87e555843..9436690f0 100644 --- a/tests/test_parametrized.py +++ b/tests/test_parametrized.py @@ -233,6 +233,12 @@ def check_var_grad(var): ) # Other transcendentals + y = np.tanh(b) + check_var_grad(y) + np.testing.assert_almost_equal( + y.build().as_array(detach=with_diff_tensor), + np.tanh(b.build().as_array(detach=with_diff_tensor)), + ) y = np.exp(b) check_var_grad(y) np.testing.assert_almost_equal( From fcf980463f47b92722901aba0e63bec9a28e01af Mon Sep 17 00:00:00 2001 From: HGSilveri Date: Mon, 10 Feb 2025 19:07:58 +0100 Subject: [PATCH 04/12] Bump to v1.3dev2 --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index 23aa83906..54817c85e 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.2.2 +1.3dev2 From f19c9cf8a178887b6eb879f5ee71a33da4d9bd17 Mon Sep 17 00:00:00 2001 From: Antoine Cornillot <61453516+a-corni@users.noreply.github.com> Date: Wed, 12 Feb 2025 18:59:38 +0100 Subject: [PATCH 05/12] Refactor methods in RemoteConnections and PasqalCloud (#796) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactor methods in RemoteConnections and PasqalCloud for new connections * Move validate_job_params back to QPUBackend, make add_measurement_to_sequence private * Fixing bug --------- Co-authored-by: Henrique Silvério <29920212+HGSilveri@users.noreply.github.com> --- pulser-core/pulser/backend/remote.py | 71 ++++++++++++++++++++- pulser-core/pulser/json/utils.py | 20 ++++++ pulser-pasqal/pulser_pasqal/pasqal_cloud.py | 68 ++------------------ 3 files changed, 96 insertions(+), 63 deletions(-) diff --git a/pulser-core/pulser/backend/remote.py b/pulser-core/pulser/backend/remote.py index 631225914..e2944b602 100644 --- a/pulser-core/pulser/backend/remote.py +++ b/pulser-core/pulser/backend/remote.py @@ -188,7 +188,7 @@ def _query_job_progress( Unlike `_fetch_result`, this method does not raise an error if some jobs in the batch do not have results. - It returns a dictionnary mapping the job ID to its status and results. + It returns a dictionary mapping the job ID to its status and results. """ pass @@ -221,6 +221,75 @@ def supports_open_batch(self) -> bool: """Flag to confirm this class can support creating an open batch.""" pass + @staticmethod + def _add_measurement_to_sequence(sequence: Sequence) -> Sequence: + """Adds a measurement operation to a Sequence if needed and possible. + + Adding a measurement operation to the Sequence is possible if only + one basis is addressed by the Sequence. It also converts all tensors + in the Sequence to arrays. + + Args: + sequence: The Sequence to add the measurement operation to. + + Returns: + The sequence with a measurement operation. + """ + if sequence.is_measured(): + return sequence + bases = sequence.get_addressed_bases() + if len(bases) != 1: + raise ValueError( + "The measurement basis can't be implicitly determined " + "for a sequence not addressing a single basis." + ) + # This is equivalent to performing a deepcopy + # All tensors are converted to arrays but that's ok, it would + # have happened anyway later on + sequence = Sequence.from_abstract_repr( + sequence.to_abstract_repr(skip_validation=True) + ) + sequence.measure(bases[0]) + return sequence + + def update_sequence_device(self, sequence: Sequence) -> Sequence: + """Match the Sequence's device with an available one, update it. + + Args: + sequence: The Sequence to check. + + Returns: + The Sequence, with the latest version for the targeted Device. + """ + available_devices = self.fetch_available_devices() + available_device_names = { + dev.name: key for key, dev in available_devices.items() + } + err_suffix = ( + " Please fetch the latest devices with " + f"`{type(self).__name__}.fetch_available_devices()` and rebuild " + "the sequence with one of the options." + ) + if (name := sequence.device.name) not in available_device_names: + raise ValueError( + "The device used in the sequence does not match any " + "of the devices currently available through the remote " + "connection." + err_suffix + ) + if sequence.device != ( + new_device := available_devices[available_device_names[name]] + ): + try: + sequence = sequence.switch_device(new_device, strict=True) + except Exception as e: + raise ValueError( + "The sequence is not compatible with the latest " + "device specs." + err_suffix + ) from e + # Validate the sequence with the new device + RemoteBackend.validate_sequence(sequence, mimic_qpu=True) + return sequence + class RemoteBackend(Backend): """A backend for sequence execution through a remote connection. diff --git a/pulser-core/pulser/json/utils.py b/pulser-core/pulser/json/utils.py index 09f064619..00fe2902f 100644 --- a/pulser-core/pulser/json/utils.py +++ b/pulser-core/pulser/json/utils.py @@ -15,10 +15,13 @@ from __future__ import annotations +import json import warnings from dataclasses import MISSING, Field from typing import TYPE_CHECKING, Any, Optional, Sequence +import numpy as np + import pulser from pulser.json.exceptions import AbstractReprError @@ -79,6 +82,23 @@ def obj_to_dict( return d +def make_json_compatible(obj: Any) -> Any: + """Makes an object compatible with JSON serialization. + + For now, simply converts Numpy arrays to lists, but more can be added + as needed. + """ + + class NumpyEncoder(json.JSONEncoder): + def default(self, o: Any) -> Any: + if isinstance(o, np.ndarray): + return o.tolist() + return json.JSONEncoder.default(self, o) + + # Serializes with the custom encoder and then deserializes back + return json.loads(json.dumps(obj, cls=NumpyEncoder)) + + def stringify_qubit_ids(qubit_ids: Sequence[QubitId]) -> list[str]: """Converts all qubit IDs into strings and looks for conflicts.""" not_str = [id for id in qubit_ids if not isinstance(id, str)] diff --git a/pulser-pasqal/pulser_pasqal/pasqal_cloud.py b/pulser-pasqal/pulser_pasqal/pasqal_cloud.py index ec7320567..7a4797b39 100644 --- a/pulser-pasqal/pulser_pasqal/pasqal_cloud.py +++ b/pulser-pasqal/pulser_pasqal/pasqal_cloud.py @@ -15,12 +15,10 @@ from __future__ import annotations -import json from dataclasses import fields from typing import Any, Mapping, Type, cast import backoff -import numpy as np import pasqal_cloud from pasqal_cloud.device.configuration import ( BaseConfig, @@ -41,6 +39,7 @@ ) from pulser.devices import Device from pulser.json.abstract_repr.deserializer import deserialize_device +from pulser.json.utils import make_json_compatible from pulser.result import Result, SampledResult EMU_TYPE_TO_CONFIG: dict[pasqal_cloud.EmulatorType, Type[BaseConfig]] = { @@ -55,23 +54,6 @@ ) -def _make_json_compatible(obj: Any) -> Any: - """Makes an object compatible with JSON serialization. - - For now, simply converts Numpy arrays to lists, but more can be added - as needed. - """ - - class NumpyEncoder(json.JSONEncoder): - def default(self, o: Any) -> Any: - if isinstance(o, np.ndarray): - return o.tolist() - return json.JSONEncoder.default(self, o) - - # Serializes with the custom encoder and then deserializes back - return json.loads(json.dumps(obj, cls=NumpyEncoder)) - - class PasqalCloud(RemoteConnection): """Manager of the connection to PASQAL's cloud platform. @@ -109,56 +91,18 @@ def submit( **kwargs: Any, ) -> RemoteResults: """Submits the sequence for execution on a remote Pasqal backend.""" - if not sequence.is_measured(): - bases = sequence.get_addressed_bases() - if len(bases) != 1: - raise ValueError( - "The measurement basis can't be implicitly determined " - "for a sequence not addressing a single basis." - ) - # This is equivalent to performing a deepcopy - # All tensors are converted to arrays but that's ok, it would - # have happened anyway later on - sequence = Sequence.from_abstract_repr( - sequence.to_abstract_repr(skip_validation=True) - ) - sequence.measure(bases[0]) - + sequence = self._add_measurement_to_sequence(sequence) emulator = kwargs.get("emulator", None) - job_params: list[JobParams] = _make_json_compatible( + job_params: list[JobParams] = make_json_compatible( kwargs.get("job_params", []) ) mimic_qpu: bool = kwargs.get("mimic_qpu", False) if emulator is None or mimic_qpu: - available_devices = self.fetch_available_devices() - available_device_names = { - dev.name: key for key, dev in available_devices.items() - } - err_suffix = ( - " Please fetch the latest devices with " - "`PasqalCloud.fetch_available_devices()` and rebuild " - "the sequence with one of the options." + sequence = self.update_sequence_device(sequence) + QPUBackend.validate_job_params( + job_params, sequence.device.max_runs ) - if (name := sequence.device.name) not in available_device_names: - raise ValueError( - "The device used in the sequence does not match any " - "of the devices currently available through the remote " - "connection." + err_suffix - ) - if sequence.device != ( - new_device := available_devices[available_device_names[name]] - ): - try: - sequence = sequence.switch_device(new_device, strict=True) - except Exception as e: - raise ValueError( - "The sequence is not compatible with the latest " - "device specs." + err_suffix - ) from e - # Validate the sequence with the new device - QPUBackend.validate_sequence(sequence, mimic_qpu=True) - QPUBackend.validate_job_params(job_params, new_device.max_runs) if sequence.is_parametrized() or sequence.is_register_mappable(): for params in job_params: vars = params.get("variables", {}) From 8460e15193267b81abc9b1864068ad0e887e0716 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Silv=C3=A9rio?= <29920212+HGSilveri@users.noreply.github.com> Date: Thu, 13 Feb 2025 09:48:28 +0100 Subject: [PATCH 06/12] Upgrades to AnalogDevice specs (#807) --- pulser-core/pulser/devices/_devices.py | 9 +++++---- tests/test_backend.py | 6 +++++- tests/test_sequence.py | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/pulser-core/pulser/devices/_devices.py b/pulser-core/pulser/devices/_devices.py index 51ccbff20..5f7929daa 100644 --- a/pulser-core/pulser/devices/_devices.py +++ b/pulser-core/pulser/devices/_devices.py @@ -71,13 +71,14 @@ name="AnalogDevice", dimensions=2, rydberg_level=60, - max_atom_num=25, - max_radial_distance=35, + max_atom_num=80, + max_radial_distance=38, min_atom_distance=5, - max_sequence_duration=4000, + max_sequence_duration=6000, max_runs=2000, requires_layout=True, - accepts_new_layouts=False, + accepts_new_layouts=True, + optimal_layout_filling=0.45, channel_objects=( Rydberg.Global( max_abs_detuning=2 * np.pi * 20, diff --git a/tests/test_backend.py b/tests/test_backend.py index 358743a57..94b7e70ef 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -13,6 +13,7 @@ # limitations under the License. from __future__ import annotations +import dataclasses import re import typing @@ -162,8 +163,11 @@ def test_qpu_backend(sequence): seq = sequence.switch_device(AnalogDevice) with pytest.raises(ValueError, match="defined from a `RegisterLayout`"): QPUBackend(seq, connection) - seq = seq.switch_register(SquareLatticeLayout(5, 5, 5).square_register(2)) + seq = seq.switch_register(SquareLatticeLayout(5, 5, 5).square_register(2)) + seq = seq.switch_device( + dataclasses.replace(seq.device, accepts_new_layouts=False) + ) with pytest.raises( ValueError, match="does not accept new register layouts" ): diff --git a/tests/test_sequence.py b/tests/test_sequence.py index a5daffd69..38434c692 100644 --- a/tests/test_sequence.py +++ b/tests/test_sequence.py @@ -1797,7 +1797,7 @@ def test_estimate_added_delay(eom, custom_phase_jump_time): match="The sequence's duration exceeded the maximum duration", ): seq.estimate_added_delay( - pulser.Pulse.ConstantPulse(4000, 1, 0, np.pi), "ising" + pulser.Pulse.ConstantPulse(6000, 1, 0, np.pi), "ising" ) var = seq.declare_variable("var", dtype=int) with pytest.raises( From 9ea8a6668968e28f3355213a93f480fe51481f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Silv=C3=A9rio?= <29920212+HGSilveri@users.noreply.github.com> Date: Fri, 14 Feb 2025 09:36:36 +0100 Subject: [PATCH 07/12] Add show option to `RegisterLayout.draw()` (#808) --- pulser-core/pulser/register/register_layout.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pulser-core/pulser/register/register_layout.py b/pulser-core/pulser/register/register_layout.py index 8dd6e5477..7ea41e5ca 100644 --- a/pulser-core/pulser/register/register_layout.py +++ b/pulser-core/pulser/register/register_layout.py @@ -139,6 +139,7 @@ def draw( projection: bool = True, fig_name: str | None = None, kwargs_savefig: dict = {}, + show: bool = True, ) -> None: """Draws the entire register layout. @@ -158,6 +159,9 @@ def draw( kwargs_savefig: Keywords arguments for ``matplotlib.pyplot.savefig``. Not applicable if `fig_name` is ``None``. + show: Whether or not to call `plt.show()` before returning. When + combining this plot with other ones in a single figure, one may + need to set this flag to False. Note: When drawing half the blockade radius, we say there is a blockade @@ -201,7 +205,9 @@ def draw( ) if fig_name is not None: plt.savefig(fig_name, **kwargs_savefig) - plt.show() + + if show: + plt.show() def make_mappable_register( self, n_qubits: int, prefix: str = "q" From ca373175296719272f06e85e62d74fd91647777f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Silv=C3=A9rio?= <29920212+HGSilveri@users.noreply.github.com> Date: Fri, 14 Feb 2025 11:03:20 +0100 Subject: [PATCH 08/12] Fix drawing of interpolated waveform on DMM (#809) --- pulser-core/pulser/sequence/_seq_drawer.py | 20 ++++++++++++++------ tests/test_sequence.py | 4 ++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/pulser-core/pulser/sequence/_seq_drawer.py b/pulser-core/pulser/sequence/_seq_drawer.py index 15fe1880a..ed03a404b 100644 --- a/pulser-core/pulser/sequence/_seq_drawer.py +++ b/pulser-core/pulser/sequence/_seq_drawer.py @@ -1419,14 +1419,22 @@ def draw_sequence( if interp_pts: data[ch].interp_pts = dict(interp_pts) - for ch, axes in ch_axes.items(): - ch_data = data[ch] - - if draw_interp_pts: + if draw_interp_pts: + for ch, axes in ch_axes.items(): + ch_data = data[ch] + # Construct map between quantity and indices + ind_map = {} + ax_ind = 0 + for color_ind, qty in enumerate(CURVES_ORDER): + if ch_data.curves_on[qty]: + ind_map[qty] = (ax_ind, color_ind) + ax_ind += 1 for qty in ("amplitude", "detuning"): if qty in ch_data.interp_pts and ch_data.curves_on[qty]: - ind = CURVES_ORDER.index(qty) + ax_ind, color_ind = ind_map[qty] pts = np.array(ch_data.interp_pts[qty]) - axes[ind].scatter(pts[:, 0], pts[:, 1], color=COLORS[ind]) + axes[ax_ind].scatter( + pts[:, 0], pts[:, 1], color=COLORS[color_ind] + ) return (fig_reg, fig, fig_qubit, fig_legend) diff --git a/tests/test_sequence.py b/tests/test_sequence.py index 38434c692..2d832d708 100644 --- a/tests/test_sequence.py +++ b/tests/test_sequence.py @@ -2039,7 +2039,7 @@ def test_draw_slm_mask_in_ising( mask_time, 0, -100, 0 ) # Possible to modulate dmm_0_1 after slm declaration - seq1.add_dmm_detuning(RampWaveform(300, 0, -10), "dmm_0_1") + seq1.add_dmm_detuning(InterpolatedWaveform(300, (-10, 0)), "dmm_0_1") assert seq1._slm_mask_time == [0, mask_time] # Possible to add pulses afterwards, seq1.declare_channel("ryd_loc", "rydberg_local", ["q0", "q1"]) @@ -2050,7 +2050,7 @@ def test_draw_slm_mask_in_ising( mode, draw_qubit_det=draw_qubit_det, draw_qubit_amp=draw_qubit_amp, - draw_interp_pts=False, + draw_interp_pts=True, draw_register=draw_register, fig_name="local_quantities", ) From 21a82535d3d7469a035b32a67e184f5e65d344a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrique=20Silv=C3=A9rio?= <29920212+HGSilveri@users.noreply.github.com> Date: Fri, 14 Feb 2025 14:46:20 +0100 Subject: [PATCH 09/12] Documentation Upgrade (#811) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Setting up Markdown support * Convert Conventions page to markdown * Attempt to use auto-generated labels * Introduce section about hardware * Redirect api core * Add pulse section * Direct to API core instead of pulse * Add some elements about pulser.devices * Create getting started notebook (#776) * Create initial notebook * Add temporary links * Add programming section * Fix installation of mermaid * Fix figure caption * Taking into account review comments * Fix doc * Try mermaid with mkdown * Address review comments * Fix doc * Add picture * Addressing review comments * Add figure, vector on the bloch sphere, complement to the Device * Make introduction * Add programming.md in ref * Add label * delete ref * Fix programming * Create tutorial Excitation to rydberg state * Address review comments * Add AFM state prep * Fix programming.md * Address review * Delete docs, add hbar * Fix picture * Fixing programming * Transfer cell as raw * Fix sequence * 4-hand fixing * Final fixes * Add paragraph along br * Address review comment * Fix referencing * Fix the notebook * Add note about neighbouring atoms * Creating notebook for hardware section, reorganising specs display in Device, adding the presence of EOM in Channel display * Fix typing * Fix build * fix typing * Fix flake8 * Fix reference * Fix ref * Fix figure * Re-reading myself * Fix rendering * Fix rendering * Fix build * Available devices * Address review comments * Add page on Register creation and Rydberg-Atom interactions + Retire old tutorials (#790) * Deleting tutorials * Adding the Register user manual * Adding links * Undo deleting the tutorials for the repo * Retire tutorials instead * Do the same for the tutorials deleted in #776 * Address review comments * Fix notebook reference * Clear all outputs * Adressing review comments * Address review comments * Add section in conventions on indexed operator notation * Small fixes * Improve formula rendering * Shuffle the description of the arguments * Trying new raw cells * Add remove-input tag to last cell * Fix typing * Add no-index * Delete no-members * Dlete no-index * Reintroduce :noindex: * Improve description of device * Try fixing link to * Fix device and channel description, try hidding the input * Try fixing build * Fix tags to hide_input * Change device descr to "Args", stop trying to mask input * Add link to backend * Move Attributes to Args in Device * Add mention to API doc * Apply some Pasqal branding to the HTML documentation * Use Pulser logo :) * Remove obsolete file * Add seealso sections in programming * Deleting the-channels * Tag The-Channels * referencing to html instead * Going for hardware.ipynb * Add page on Pulses and Waveforms (#793) * Add tutorial on Pulses and Waveforms * Address review comments * Reference fundamentals pages in Programming * Remove signatures from autosummary * API Reference Rework (#722) * Improving autodoc typehints generation * Improving the Register API doc * WIP: New API reference for pulser-core * Try to pass RTD build * Adding `pulser-simulation` module to the API ref * Adding pulser_pasqal ref * Import sorting * Fix UTs * Fix links in new pages * Adding the Sequence page (#802) * WIP * Getting links to work at top-level pulser API * Writing Sequence page * Address review comments * Fix typo * Merge remote-tracking branch 'origin' into docs-v2 * Modifications to the TOC tree (#806) * Reorder pages in Extended Usage * Retire tutorial on AFM state preparation * Slight modifications to the Backend page * Move and update the XY Hamiltonian page * Fix broken links * Fix typo Co-authored-by: Antoine Cornillot <61453516+a-corni@users.noreply.github.com> --------- Co-authored-by: Antoine Cornillot <61453516+a-corni@users.noreply.github.com> * Create introduction to Extended Usage (#800) * Add tanh function to ParamObj (#798) * added tanh function to paramobj * Update tests/test_parametrized.py Co-authored-by: Henrique Silvério <29920212+HGSilveri@users.noreply.github.com> * fixed test --------- Co-authored-by: Henrique Silvério <29920212+HGSilveri@users.noreply.github.com> * Create extended usage doc * Fix doc * Post-rereading correction * Allow switch_device to work when Channel uses BaseEOM (#804) * Allow switch_device to work when Channel uses BaseEOM * Bump to v1.2.2 * Bump to v1.3dev2 * Addressing the confusion btw Weighted Analog and XY * Add more specific links, descr of digital qc * Address review comments * Testing with : * Try with #- * Fix reference * Fix links to sections * Fix hamiltonian * Fix apidoc ref --------- Co-authored-by: Vytautas Abramavicius <145791635+vytautas-a@users.noreply.github.com> Co-authored-by: Henrique Silvério <29920212+HGSilveri@users.noreply.github.com> Co-authored-by: HGSilveri --------- Co-authored-by: a_corni Co-authored-by: Antoine Cornillot <61453516+a-corni@users.noreply.github.com> Co-authored-by: Benjamin Becquet Co-authored-by: Benjamin Becquet Co-authored-by: Vytautas Abramavicius <145791635+vytautas-a@users.noreply.github.com> --- .gitignore | 1 + .readthedocs.yml | 2 +- docs/requirements.txt | 5 +- docs/source/_static/assets/pulser_logo.svg | 1 + docs/source/_static/css/custom.css | 51 ++ docs/source/_static/css/max_width.css | 4 - docs/source/_templates/autosummary/class.rst | 37 ++ .../_templates/autosummary/function.rst | 5 + docs/source/_templates/autosummary/module.rst | 44 ++ docs/source/apidoc/backend.rst | 37 -- docs/source/apidoc/core.rst | 185 ++----- docs/source/apidoc/pasqal.rst | 14 + docs/source/apidoc/pulser.rst | 9 - docs/source/apidoc/simulation.rst | 67 +-- docs/source/conf.py | 30 +- docs/source/conventions.md | 23 + docs/source/extended_usage_intro.md | 74 +++ docs/source/files/AF_Ising_program.png | Bin 0 -> 60022 bytes docs/source/files/bloch_pi_rotation.png | Bin 0 -> 103305 bytes docs/source/files/bloch_rotation_a_b.png | Bin 0 -> 103350 bytes docs/source/files/decision_diagram_device.png | Bin 0 -> 63164 bytes docs/source/files/rydberg_blockade.png | Bin 0 -> 63421 bytes docs/source/hardware.ipynb | 500 ++++++++++++++++++ docs/source/index.rst | 74 ++- docs/source/programming.md | 255 +++++++++ docs/source/pulses.ipynb | 303 +++++++++++ docs/source/register.ipynb | 372 +++++++++++++ docs/source/review.rst | 200 ------- docs/source/sequence.ipynb | 349 ++++++++++++ docs/source/tutorials/1D_crystals.nblink | 3 - docs/source/tutorials/afm_prep.nblink | 3 - docs/source/tutorials/cz_gate.nblink | 3 - docs/source/tutorials/mw_engineering.nblink | 3 - docs/source/tutorials/shadow_est.nblink | 3 - docs/source/tutorials/simulating.nblink | 3 - pulser-core/pulser/__init__.py | 2 + pulser-core/pulser/abstract_repr.py | 38 ++ pulser-core/pulser/backend/__init__.py | 4 +- pulser-core/pulser/backend/config.py | 4 +- pulser-core/pulser/backends.py | 22 +- pulser-core/pulser/channels/__init__.py | 11 +- pulser-core/pulser/channels/base_channel.py | 1 + pulser-core/pulser/channels/eom.py | 4 +- pulser-core/pulser/devices/__init__.py | 11 +- pulser-core/pulser/devices/_device_datacls.py | 183 ++++--- pulser-core/pulser/devices/_devices.py | 2 + pulser-core/pulser/devices/_mock_device.py | 1 + .../pulser/json/abstract_repr/deserializer.py | 2 +- pulser-core/pulser/register/__init__.py | 5 +- pulser-core/pulser/register/base_register.py | 2 +- pulser-core/pulser/register/mappable_reg.py | 2 +- pulser-core/pulser/register/register.py | 9 +- pulser-core/pulser/register/register3d.py | 4 +- .../pulser/register/register_layout.py | 16 +- pulser-core/pulser/register/weight_maps.py | 6 +- pulser-core/pulser/sampler/__init__.py | 10 +- pulser-core/pulser/sequence/__init__.py | 6 +- pulser-core/pulser/sequence/sequence.py | 19 +- pulser-core/pulser/waveforms.py | 2 +- pulser-pasqal/pulser_pasqal/pasqal_cloud.py | 2 +- .../pulser_simulation/__init__.py | 4 +- tests/test_channels.py | 5 +- tests/test_devices.py | 25 +- .../Backends for Sequence Execution.ipynb | 25 +- tutorials/creating_sequences.ipynb | 423 +++++++-------- .../Spin chain of 3 atoms in XY mode.ipynb | 25 +- .../Building 1D Rydberg Crystals.ipynb | 0 .../Control-Z Gate Sequence.ipynb | 0 ...iltonians in arrays of Rydberg atoms.ipynb | 0 ...rromagnetic order in the Ising model.ipynb | 0 tutorials/retired/README.md | 3 + .../Shadow estimation for VQS.ipynb | 0 .../retired}/intro_rydberg_blockade.ipynb | 0 .../{ => retired}/simulating_sequences.ipynb | 0 74 files changed, 2650 insertions(+), 888 deletions(-) create mode 100644 docs/source/_static/assets/pulser_logo.svg create mode 100644 docs/source/_static/css/custom.css delete mode 100644 docs/source/_static/css/max_width.css create mode 100644 docs/source/_templates/autosummary/class.rst create mode 100644 docs/source/_templates/autosummary/function.rst create mode 100644 docs/source/_templates/autosummary/module.rst delete mode 100644 docs/source/apidoc/backend.rst create mode 100644 docs/source/apidoc/pasqal.rst delete mode 100644 docs/source/apidoc/pulser.rst create mode 100644 docs/source/extended_usage_intro.md create mode 100644 docs/source/files/AF_Ising_program.png create mode 100644 docs/source/files/bloch_pi_rotation.png create mode 100644 docs/source/files/bloch_rotation_a_b.png create mode 100644 docs/source/files/decision_diagram_device.png create mode 100644 docs/source/files/rydberg_blockade.png create mode 100644 docs/source/hardware.ipynb create mode 100644 docs/source/programming.md create mode 100644 docs/source/pulses.ipynb create mode 100644 docs/source/register.ipynb delete mode 100644 docs/source/review.rst create mode 100644 docs/source/sequence.ipynb delete mode 100644 docs/source/tutorials/1D_crystals.nblink delete mode 100644 docs/source/tutorials/afm_prep.nblink delete mode 100644 docs/source/tutorials/cz_gate.nblink delete mode 100644 docs/source/tutorials/mw_engineering.nblink delete mode 100644 docs/source/tutorials/shadow_est.nblink delete mode 100644 docs/source/tutorials/simulating.nblink create mode 100644 pulser-core/pulser/abstract_repr.py rename tutorials/{quantum_simulation => retired}/Building 1D Rydberg Crystals.ipynb (100%) rename tutorials/{applications => retired}/Control-Z Gate Sequence.ipynb (100%) rename tutorials/{quantum_simulation => retired}/Microwave-engineering of programmable XXZ Hamiltonians in arrays of Rydberg atoms.ipynb (100%) rename tutorials/{quantum_simulation => retired}/Preparing state with antiferromagnetic order in the Ising model.ipynb (100%) create mode 100644 tutorials/retired/README.md rename tutorials/{quantum_simulation => retired}/Shadow estimation for VQS.ipynb (100%) rename {docs/source => tutorials/retired}/intro_rydberg_blockade.ipynb (100%) rename tutorials/{ => retired}/simulating_sequences.ipynb (100%) diff --git a/.gitignore b/.gitignore index 692760977..5aef95722 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,7 @@ _build __pycache__/ build/ docs/build/ +_autosummary/ dist/ env* *.egg-info/ diff --git a/.readthedocs.yml b/.readthedocs.yml index 4c5cf4bf4..6d09ab078 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -8,7 +8,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.9" + python: "3.10" # Build documentation in the docs/ directory with Sphinx sphinx: diff --git a/docs/requirements.txt b/docs/requirements.txt index 5c9ef500f..59db15eed 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,11 +1,10 @@ # For generating documentation. -Sphinx < 7 +Sphinx < 8 sphinx-rtd-theme # documentation theme -sphinx_autodoc_typehints == 1.21.3 +sphinx_autodoc_typehints == 2.2.1 nbsphinx nbsphinx-link ipython >= 8.10 # Avoids bug with code highlighting myst-parser - # Not on PyPI # pandoc diff --git a/docs/source/_static/assets/pulser_logo.svg b/docs/source/_static/assets/pulser_logo.svg new file mode 100644 index 000000000..830e1bda0 --- /dev/null +++ b/docs/source/_static/assets/pulser_logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/source/_static/css/custom.css b/docs/source/_static/css/custom.css new file mode 100644 index 000000000..dbaa672d4 --- /dev/null +++ b/docs/source/_static/css/custom.css @@ -0,0 +1,51 @@ +/* Limit content width, for large screens only */ +@media screen and (min-width: 1100px) { + .wy-nav-content { + width: 80% !important; + max-width: 1600px !important; + } +} + +/* Apply Pasqal branding colors */ +.wy-nav-content-wrap { + background-color: #fcfcfc; +} + +.wy-nav-top, +.wy-nav-side { + background-color: #243237; +} + +.wy-side-nav-search { + background-color: #00ac85; +} + +.wy-side-nav-search a.icon { + font-size: 0px; + margin-bottom: 1rem; +} + +.wy-side-nav-search a.icon img.logo { + max-width: 200px; +} + +.wy-menu-vertical header, +.wy-menu-vertical p.caption { + color: #00ac85; +} + +.wy-menu-vertical a { + color: white; +} + +.wy-menu-vertical a:hover { + background: #173035; +} + +a { + color: #00ac85; +} + +a:hover { + color: #00c887; +} diff --git a/docs/source/_static/css/max_width.css b/docs/source/_static/css/max_width.css deleted file mode 100644 index 3fa544c06..000000000 --- a/docs/source/_static/css/max_width.css +++ /dev/null @@ -1,4 +0,0 @@ -.wy-nav-content { - width: 80% !important; - max-width: 1600px !important; -} diff --git a/docs/source/_templates/autosummary/class.rst b/docs/source/_templates/autosummary/class.rst new file mode 100644 index 000000000..32085984c --- /dev/null +++ b/docs/source/_templates/autosummary/class.rst @@ -0,0 +1,37 @@ +{{ objname| escape | underline}} + +.. currentmodule:: {{ module }} + +.. autoclass:: {{ objname }} + :members: + :inherited-members: + :show-inheritance: + :member-order: groupwise + + {% block attributes %} + {% if attributes %} + .. rubric:: {{ _('Attributes') }} + + .. autosummary:: + :nosignatures: + {% for item in attributes %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% endif %} + {% endblock %} + + {% block methods %} + {% if methods and methods != ['__init__'] %} + .. rubric:: {{ _('Methods') }} + + .. autosummary:: + :nosignatures: + {% for item in methods %} + {%- if not item.startswith('_') or item in ['__call__'] %} + ~{{ name }}.{{ item }} + {%- endif -%} + {%- endfor %} + + .. rubric:: {{ _('Signatures') }} + {% endif %} + {% endblock %} \ No newline at end of file diff --git a/docs/source/_templates/autosummary/function.rst b/docs/source/_templates/autosummary/function.rst new file mode 100644 index 000000000..7ffb81228 --- /dev/null +++ b/docs/source/_templates/autosummary/function.rst @@ -0,0 +1,5 @@ +{{ objname | escape | underline}} + +.. currentmodule:: {{ module }} + +.. autofunction:: {{ objname }} \ No newline at end of file diff --git a/docs/source/_templates/autosummary/module.rst b/docs/source/_templates/autosummary/module.rst new file mode 100644 index 000000000..fa79bd2c1 --- /dev/null +++ b/docs/source/_templates/autosummary/module.rst @@ -0,0 +1,44 @@ +{% extends "!autosummary/module.rst" %} + +{# This file is almost the same as the default, but adds :toctree: to the autosummary directives. + The original can be found at `sphinx/ext/autosummary/templates/autosummary/module.rst`. #} + + +{% block functions %} +{%- if functions %} + .. rubric:: {{ _('Functions') }} + + .. autosummary:: + :toctree: + + {% for item in functions %} + {{ item }} + {%- endfor %} +{% endif %} +{%- endblock %} + +{% block classes %} +{% if classes %} + .. rubric:: {{ _('Classes') }} + .. autosummary:: + :toctree: + :nosignatures: + + {% for item in classes %} + ~{{ fullname }}.{{ item }} + {%- endfor %} +{% endif %} + {% endblock %} + +{%- block modules %} +{#{%- if modules %} +.. rubric:: Modules + +.. autosummary:: + :toctree: + +{% for item in modules %} + {{ item }} +{%- endfor %} +{% endif %}#} +{%- endblock %} \ No newline at end of file diff --git a/docs/source/apidoc/backend.rst b/docs/source/apidoc/backend.rst deleted file mode 100644 index 5a84baecc..000000000 --- a/docs/source/apidoc/backend.rst +++ /dev/null @@ -1,37 +0,0 @@ -************************ -Backend Interfaces -************************ - -QPU ----- - -.. autoclass:: pulser.QPUBackend - :members: - - -Emulators ----------- - -Configuration -^^^^^^^^^^^^^^ -.. autoclass:: pulser.EmulatorConfig - :members: - -Local -^^^^^^^ -.. autoclass:: pulser_simulation.QutipBackend - :members: - -Remote -^^^^^^^^^^ -.. autoclass:: pulser_pasqal.EmuTNBackend - :members: - -.. autoclass:: pulser_pasqal.EmuFreeBackend - :members: - - -Remote backend connection ---------------------------- - -.. autoclass:: pulser_pasqal.PasqalCloud diff --git a/docs/source/apidoc/core.rst b/docs/source/apidoc/core.rst index fd6e1714d..71eb10c50 100644 --- a/docs/source/apidoc/core.rst +++ b/docs/source/apidoc/core.rst @@ -1,155 +1,68 @@ -************************ -Core Features -************************ +``pulser`` +================= -Sequence ----------------------- +.. automodule:: pulser -.. automodule:: pulser.sequence.sequence - :members: +Classes +----------------- -Register ----------------------- +These are classes that can be imported directly from ``pulser``. They should cover the fundamental +needs for sequence creation. -Register classes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The register classes allow for the creation of arbitrary registers. -.. autoclass:: pulser.register.base_register.BaseRegister - :members: -.. autoclass:: pulser.register.register.Register - :members: - :show-inheritance: +.. autosummary:: + :toctree: _autosummary -.. autoclass:: pulser.register.register3d.Register3D - :members: - :show-inheritance: + ~pulser.waveforms.CompositeWaveform + ~pulser.waveforms.CustomWaveform + ~pulser.waveforms.ConstantWaveform + ~pulser.waveforms.RampWaveform + ~pulser.waveforms.BlackmanWaveform + ~pulser.waveforms.InterpolatedWaveform + ~pulser.waveforms.KaiserWaveform + ~pulser.pulse.Pulse + ~pulser.register.Register + ~pulser.register.Register3D + ~pulser.sequence.Sequence + ~pulser.noise_model.NoiseModel + ~pulser.backend.EmulatorConfig + ~pulser.backend.QPUBackend -Register layout -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -A ``RegisterLayout`` is used to define a register from a set of traps. It is -intended to be given to the user by the hardware provider as a way of showing -which layouts are already available on a given device. In turn, the user -can create a ``Register`` by selecting the traps on which to place atoms, or -even a ``MappableRegister``, which allows for the creation of sequences whose -register can be defined at build time. +Device Examples +----------------- -.. autoclass:: pulser.register.register_layout.RegisterLayout - :members: - :inherited-members: +These are built-in :py:class:`~pulser.devices.Device` and :py:class:`~pulser.devices.VirtualDevice` instances that can be +imported directly from ``pulser``. -.. autoclass:: pulser.register.mappable_reg.MappableRegister - :members: +.. important:: + These instances are **not** descriptions of actual devices. They are just examples that + can be used to enforce different sets of constraints during :py:class:`~pulser.Sequence` creation. -Special cases -"""""""""""""""""" +.. autosummary:: + :toctree: _autosummary -.. automodule:: pulser.register.special_layouts - :members: - :show-inheritance: + AnalogDevice + DigitalAnalogDevice + MockDevice -DetuningMap -------------------- -A ``DetuningMap`` is associated to a ``DMM`` in a ``Sequence``. It links a set -of weights to a set of trap coordinates. It is intended to be defined by the user -from a ``RegisterLayout``, a ``Register`` or a ``MappableRegister`` using -``define_detuning_map``. +Modules +---------- -.. autoclass:: pulser.register.weight_maps.DetuningMap - :members: - :inherited-members: +.. autosummary:: + :toctree: _autosummary -Pulse -------------------- - -.. automodule:: pulser.pulse - :members: - - -Waveforms ----------------------- - -.. automodule:: pulser.waveforms - :members: - :show-inheritance: - -Devices ---------------------- - -Structure of a Device -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The :class:`Device` class sets the structure of a physical device, -while :class:`VirtualDevice` is a more permissive device type which can -only be used in emulators, as it does not necessarily represent the -constraints of a physical device. - -Illustrative instances of :class:`Device` (see `Physical Devices`_) and :class:`VirtualDevice` -(the `MockDevice`) come included in the `pulser.devices` module. - -.. autoclass:: pulser.devices._device_datacls.Device - :members: - :inherited-members: - -.. autoclass:: pulser.devices._device_datacls.VirtualDevice - :members: - :inherited-members: - -.. _Physical Devices: - -Physical Devices -^^^^^^^^^^^^^^^^^^^ -Each `Device`` instance holds the characteristics of a physical device, -which when associated with a :class:`pulser.Sequence` condition its development. - -.. autodata:: pulser.devices.AnalogDevice - -.. autodata:: pulser.devices.DigitalAnalogDevice - -Noise Model --------------- -.. automodule:: pulser.noise_model - :members: - -Channels ---------------------- - -Base Channel -^^^^^^^^^^^^^^^ -.. automodule:: pulser.channels.base_channel - :members: - - -Available Channels -^^^^^^^^^^^^^^^^^^^^^^^ -.. automodule:: pulser.channels.channels - :members: - :show-inheritance: - -.. autoclass:: pulser.channels.dmm.DMM - :members: - :show-inheritance: - -EOM Mode Configuration -^^^^^^^^^^^^^^^^^^^^^^^^ -.. automodule:: pulser.channels.eom - :members: - :show-inheritance: - -Sampler ------------------- -.. automodule:: pulser.sampler.sampler - :members: - -.. automodule:: pulser.sampler.samples - :members: - -Result ------------------- -.. automodule:: pulser.result - :members: - :show-inheritance: \ No newline at end of file + pulser.abstract_repr + pulser.backend + pulser.backends + pulser.channels + pulser.devices + pulser.parametrized + pulser.register + pulser.result + pulser.sampler + pulser.waveforms \ No newline at end of file diff --git a/docs/source/apidoc/pasqal.rst b/docs/source/apidoc/pasqal.rst new file mode 100644 index 000000000..288d19b5b --- /dev/null +++ b/docs/source/apidoc/pasqal.rst @@ -0,0 +1,14 @@ +``pulser_pasqal`` +======================= + +.. automodule:: pulser_pasqal + +Classes +----------------- + +.. autosummary:: + :toctree: _autosummary + + EmuFreeBackend + EmuTNBackend + PasqalCloud diff --git a/docs/source/apidoc/pulser.rst b/docs/source/apidoc/pulser.rst deleted file mode 100644 index 82533cce0..000000000 --- a/docs/source/apidoc/pulser.rst +++ /dev/null @@ -1,9 +0,0 @@ -API Reference -============== - -.. toctree:: - :maxdepth: 3 - - core - simulation - backend diff --git a/docs/source/apidoc/simulation.rst b/docs/source/apidoc/simulation.rst index f49194a55..3f5b4e68e 100644 --- a/docs/source/apidoc/simulation.rst +++ b/docs/source/apidoc/simulation.rst @@ -1,57 +1,24 @@ -********************* -Classical Simulation -********************* +``pulser_simulation`` +======================= -Since version 0.6.0, all simulation classes (previously under the ``pulser.simulation`` module) -are in the ``pulser-simulation`` extension and should be imported from ``pulser_simulation``. +.. automodule:: pulser_simulation -QutipEmulator ----------------------- +Classes +----------------- -: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``. +.. autosummary:: + :toctree: _autosummary -.. autoclass:: pulser_simulation.simulation.QutipEmulator - :members: + QutipBackend + QutipEmulator + SimConfig +Modules +---------- -SimConfig ----------------------- +.. autosummary:: + :toctree: _autosummary -.. autoclass:: pulser_simulation.SimConfig - :members: - -Simulation Results ------------------------ - -Depending on the types of noise involved in a simulation, the results are returned -as an instance of :class:`SimulationResults`, namely :class:`CoherentResults` -(when each result can still be represented as a :class:`QutipResult` i.e. a state vector -or a density matrix, before being measured) or :class:`NoisyResults` (when each of them can only -be represented as :class:`SampledResult` i.e. a probability distribution over the basis states). - -Both classes feature methods for processing and displaying the results stored -within them. - -QutipResult -^^^^^^^^^^^^^^^^ - -.. autoclass:: pulser_simulation.qutip_result.QutipResult - :members: - :inherited-members: - -CoherentResults -^^^^^^^^^^^^^^^^ - -.. autoclass:: pulser_simulation.simresults.CoherentResults - :members: - :inherited-members: - - -NoisyResults -^^^^^^^^^^^^^^^^ - -.. autoclass:: pulser_simulation.simresults.NoisyResults - :members: - :inherited-members: + pulser_simulation.qutip_backend + pulser_simulation.qutip_result + pulser_simulation.simresults \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index e40503e2a..577a3882c 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -11,6 +11,7 @@ # documentation root, use os.path.abspath to make it absolute, like shown here. # import os +import shutil import sys sys.path.insert(0, os.path.abspath("../../")) @@ -38,8 +39,10 @@ "nbsphinx", "nbsphinx_link", "sphinx.ext.autodoc", + "sphinx.ext.autosummary", "sphinx.ext.mathjax", "sphinx.ext.napoleon", + "sphinx.ext.autosummary", "sphinx_autodoc_typehints", ] @@ -60,7 +63,7 @@ # "tasklist", ] -myst_heading_anchors = 3 +myst_heading_anchors = 5 # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] @@ -70,14 +73,31 @@ # This pattern also affects html_static_path and html_extra_path. exclude_patterns = [] +show_warning_types = True +# Supress specific warnings +suppress_warnings = [ + "config.cache", + # TODO: Remove these once the API ref is complete + "autosummary.import_cycle", +] + # Source file suffixes source_suffix = [".rst", ".md"] # Autodoc config autosummary_generate = True +autosummary_ignore_module_all = False autodoc_member_order = "bysource" -autodoc_typehints = "none" +autodoc_typehints = "signature" typehints_defaults = "comma" +always_use_bars_union = True +autodoc_type_aliases = { + "ArrayLike": "ArrayLike", +} + +# Delete autogenerated content before rebuilding +shutil.rmtree("apidoc/_autosummary", ignore_errors=True) + # -- Options for HTML output ------------------------------------------------- @@ -90,7 +110,5 @@ # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ["_static"] - -html_css_files = [ - "css/max_width.css", -] +html_logo = "_static/assets/pulser_logo.svg" +html_css_files = ["css/custom.css"] diff --git a/docs/source/conventions.md b/docs/source/conventions.md index 73c7e3b34..34dcd6a19 100644 --- a/docs/source/conventions.md +++ b/docs/source/conventions.md @@ -122,6 +122,11 @@ sample ## Hamiltonians +:::{tip} +This section uses formulas that rely on the [Indexed Operator](#indexed-operator) +notation. +::: + Independently of the mode of operation, the Hamiltonian describing the system can be written as @@ -258,3 +263,21 @@ $$ :::{note} The definitions given for both interaction Hamiltonians are independent of the chosen state vector convention. ::: + +## Notation + +### Indexed Operator + +Whenever an arbitrary operator is written with an index (typically $i$ or $j$), e.g. $\hat{O}_i$, it is implicit that $\hat{O}$ is applied *only* to qudit $i$ while the rest of the qudits are applied the identity operator, $\hat{I}$. Put another way, + +$$ \hat{O}_i = \underset{(1)}{\hat{I}} \otimes \underset{(2)}{\hat{I}} \otimes ... \otimes\ \underset{(i)}{\hat{O}}\ \otimes ... \otimes \underset{(N)}{\hat{I}},$$ + +where $1 \leq i \leq N$. + +This notation is extendable to multiple indices. Take for instance the case with two indices, $\hat{O}_{ij}$ – here, $\hat{O}$ is a two-qudit operator. A good example is the [interaction Hamiltonian](#interaction-hamiltonian) in the `ground-rydberg` basis, which we write as + +$$H^\text{int}_{ij} = \frac{C_6}{R_{ij}^6} \hat{n}_i \hat{n}_j = \frac{C_6}{R_{ij}^6} \left( \underset{(1)}{\hat{I}} \otimes ... \otimes \ \underset{(j)}{\hat{n}}\ \otimes ... \otimes \ \underset{(i)}{\hat{n}} \ \otimes ... \otimes \underset{(N)}{\hat{I}}\right),$$ + +where $1 \leq j < i \leq N$. + +Note that, generally, we cannot write $\hat{O}_{ij}$ in the form used above because $\hat{O}$ might not be separable in a tensor product of two single-qudit operators, but the operator is valid nonetheless. diff --git a/docs/source/extended_usage_intro.md b/docs/source/extended_usage_intro.md new file mode 100644 index 000000000..77df36b1b --- /dev/null +++ b/docs/source/extended_usage_intro.md @@ -0,0 +1,74 @@ +# Introduction to Extended Usage + +In the "Fundamentals" section, we introduced the basic tools for Analog quantum computing with the Ising Hamiltonian. In this section, we present more tools for Analog quantum computing with the Ising Hamiltonian, as well as introduce other tools to program in other quantum computing paradigms: Weighted Analog with the Ising Hamiltonian, Analog with the XY Hamiltonian and Digital. + +:::{important} +To program in a specific quantum computing paradigm, ensure the needed features are in your chosen Device's specifications. +::: + +## Extending Analog Quantum Computing with the Ising Hamiltonian + +Analog Quantum Computing with the Ising Hamiltonian refers to quantum programs using only one `Channel`, the `Rydberg.Global` channel. It enables the programming of the [Ising Hamiltonian](./programming.md#ising-hamiltonian): + +$$\frac{H}{\hbar}(t) = \sum_{k=1}^N \left (\frac{\Omega(t)}{2} e^{-i\phi(t)} |g\rangle\langle r|_k + \frac{\Omega(t)}{2} e^{i\phi(t)} |r\rangle\langle g|_k - \delta(t) |r\rangle\langle r|_k(t) + \sum_{j`: Each channel has a _modulation bandwidth_, that defines how the pulses that will be added to it will be affected by the modulation phenomenon. +- {ref}`EOM `: Some channels support an "EOM" mode, a mode in which the pulses are less impacted by the modulation phenomenon, but have to be of square shape. + +### 4. Add the Pulses + +- [Arbitrary Phase Waveforms](./apidoc/_autosummary/pulser.pulse.Pulse) enables the definition of a Pulse with an arbitrary phase. + +### Other extended usage + +- [Parametrized Sequences](./tutorials/paramseqs.nblink) enable the generation of multiple Sequences that vary only in a few parameters. +- [Serialization/Deserialization](./tutorials/serialization.nblink) enable the import/export of Sequences between different locations. + +## Weighted Analog Quantum Computing + +Weighted Analog with the Ising Hamiltonian designates quantum programs combining the `Rydberg.Global` channel with a `DMM` channel. It enables the definition of an Ising Hamiltonian with local control over the detuning: + +$$\frac{H}{\hbar}(t) = \sum_{k=1}^N \left (\frac{\Omega(t)}{2} e^{-i\phi(t)} |g\rangle\langle r|_k + \frac{\Omega(t)}{2} e^{i\phi(t)} |r\rangle\langle g|_k - (\delta(t)\mathbf{+\epsilon_k\delta_{DMM}(t)}) |r\rangle\langle r|_k + \sum_{j`. + +## Analog with the XY Hamiltonian + +One can also perform Analog Quantum Computing with the [XY Hamiltonian](./programming.md#xy-hamiltonian). The `Channel` associated with this is the `Microwave.Global` Channel: + +$$\frac{H}{\hbar}(t) = \sum_{k=1}^N \left (\frac{\Omega(t)}{2} e^{-i\phi(t)} |g\rangle\langle r|_k + \frac{\Omega(t)}{2} e^{i\phi(t)} |r\rangle\langle g|_k - \delta(t) |r\rangle\langle r|_k(t) + \sum_{j$ and $\left|1\right>$. + +## Digital Quantum Computing + +Digital Quantum Computing is a paradigm in which a system's state evolves through a series of discrete manipulation of its qubits' states, known as quantum gates. This is the underlying approach in quantum circuits, and can be replicated in neutral-atom devices at the pulse level. + +To achieve this, the qubit states are encoded in the states of the "digital" basis $\left|g\right>$ and $\left|h\right>$. Digital Quantum Computing is thus associated with the `Raman` channel. When adding a pulse to the `Raman` channel, the Hamiltonian you program is: + +$$\frac{H}{\hbar}(t) = \sum_{k=1}^N \left (\frac{\Omega_k(t)}{2} e^{-i\phi_k(t)} |h\rangle\langle g|_k + \frac{\Omega_k(t)}{2} e^{i\phi_k(t)} |g\rangle\langle h|_k - \delta_k(t) |g\rangle\langle g|_k(t) \right) +$$ + +- {ref}`Local pulses and target operations ` enable to define gates applying on only specific qubits, by defining a driving Hamiltonian for a set of targeted atoms specifically (the quantities $\Omega_k$, $\delta_k$ and $\phi_k$ in the Hamiltonian above depend on the atoms). +- [Virtual Z gates and phase shifts](tutorials/phase_shifts_vz_gates.nblink): phase shift is an operation that can be programmed in between two pulses to program a _virtual-Z gate_, a phase gate. This tutorial presents how to use it to perform an Hadamard gate. diff --git a/docs/source/files/AF_Ising_program.png b/docs/source/files/AF_Ising_program.png new file mode 100644 index 0000000000000000000000000000000000000000..980486dfab2636c95d14d3f8d003d2a965fe50b2 GIT binary patch literal 60022 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 literal 0 HcmV?d00001 diff --git a/docs/source/files/bloch_pi_rotation.png b/docs/source/files/bloch_pi_rotation.png new file mode 100644 index 0000000000000000000000000000000000000000..b2f02390f45a89a3d2253748a69d277d3360591e GIT binary patch literal 103305 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%0AytXkB`2=zj0y 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 literal 0 HcmV?d00001 diff --git a/docs/source/files/bloch_rotation_a_b.png b/docs/source/files/bloch_rotation_a_b.png new file mode 100644 index 0000000000000000000000000000000000000000..9a374eaa7272a3dd792a93e2b0cee928ed2a7523 GIT binary patch literal 103350 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%0D@uoDh`bQ=tz}FZ>hwz48{X>X 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%0DDE4SX>cPRHF2%mO|U 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%0AIp8Qf2 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

U0&3{yhI1*n?%^GeO& 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 literal 0 HcmV?d00001 diff --git a/docs/source/files/rydberg_blockade.png b/docs/source/files/rydberg_blockade.png new file mode 100644 index 0000000000000000000000000000000000000000..84c12c63c05d129fe19c4ddb06953a16ee24af36 GIT binary patch literal 63421 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 Up?fvh6G;qWmxUdzi0X6pzjb-\n", + "\n", + "**Important note**:\n", + "\n", + "\n", + "The `Device` represents the physics of a neutral-atom QPU but is not the QPU itself. A QPU must be accessed via a `Backend`, which is presented [in this section](./tutorials/backends.nblink).\n", + "\n", + "" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "### Choosing a `Device`" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "

\n", + "\"Decision\n", + "
\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "To choose a `Device` for your `Sequence`, the first question you should ask yourself is:\n", + "\n", + "
\"How close do I want the physical constraints I program with to be to the QPU's?\"
\n", + "\n", + "**If you want to program with the physical constraints of a QPU**: Each QPU has an associated `Device`, which you can [get from the cloud provider you use to access this QPU](tutorials/backends.nblink#1.2.-Preparation-for-execution-on-QPUBackend).\n", + "\n", + "There are several reasons for which you might want to feel less constrained by the features currently supported by real QPUs. For instance, you might want to design an algorithm for a QPU having better performances (supporting more qubits, longer sequences, ...) or hardware components that have not been installed.\n", + "\n", + "Pulser enables you to define your own devices, but a `Device` object takes as input lots of parameters that have all to be defined. Therefore, for user convenience, `Pulser` provides:\n", + "\n", + "- **Examples of typical physical devices** in `pulser.devices`. Notably, `pulser.AnalogDevice` is an example of a QPU implementing an [Ising Hamiltonian](./programming.md#ising-hamiltonian).\n", + "\n", + "- **The possibility to define a device without some physical constraints** using the `VirtualDevice` class. An example of such a virtual device is the `MockDevice` provided in the `pulser.devices`, which gives full liberty to write a quantum program. `VirtualDevice` is detailed in [an advanced tutorial](./tutorials/virtual_devices.nblink)." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "
\n", + "\n", + "**Note**:\n", + "\n", + "The selection of a device in a Pulser program does not enforce any choice on the [backend](tutorials/backends.nblink). No matter the device you used to program your `Sequence`, you can always submit it to any QPU: if the values of the `Sequence` match the constraints of the `QPU`, it will be executed. \n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "
\n", + "\n", + "\n", + "**Tip**:\n", + "\n", + "It is possible to change the device with which a `Sequence` was built, by using `Sequence.switch_device`. This is especially useful to check if the values of the `Sequence` match the constraints of the `QPU` prior to submitting to the `QPU`. For instance, you could have built your `Sequence` with an example of a `Device` like `AnalogDevice`, and now want to run it on a QPU, or the specifications of your QPU might have changed between your design and submission.\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "### Reading through the `Device`'s specifications" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "The second question you should ask yourself to choose your `Device` is: \n", + "\n", + "
\"Do its constraints allow me to program my `Sequence` ?\"
\n", + "\n", + "The device specifications are here to guide your choice. Here are all the parameters in a `Device`:" + ] + }, + { + "cell_type": "raw", + "metadata": { + "editable": true, + "raw_mimetype": "text/restructuredtext", + "slideshow": { + "slide_type": "" + }, + "tags": [], + "vscode": { + "languageId": "raw" + } + }, + "source": [ + ".. autoclass:: pulser.devices._device_datacls.Device\n", + " :noindex:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "**Note**:\n", + "\n", + "The `Device` object has many useful properties and methods, that you can check in the [API documentation](./apidoc/_autosummary/pulser.devices.Device.rst#pulser.devices.Device). For instance, it is possible to display some of the specifications of the `Device` with `Device.specs`. See an example with `AnalogDevice.specs` [in the section below](#the-analogdevice).\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "### Tips on `Device` selection" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "The `Device` is going to constrain the next steps of your [quantum program](./programming.md#writing-a-pulser-program):\n", + "\n", + "1) Some parameters are going to constrain [the creation of your Register](./programming.md#create-the-register), and therefore, the [interaction strength in the interaction Hamiltonian](programming.md#interaction-hamiltonian). Some of these parameters are:\n", + " - `dimensions`\n", + " - `max_atom_num`\n", + " - `max_radial_distance`\n", + " - `min_atom_distance`\n", + "\n", + "2) The `rydberg_level` determines the [Ising interaction coefficient](./programming.md#ising-hamiltonian) $C_6$ of the Ising Hamiltonian. The quantity $\\frac{C_6}{\\hbar}$ is accessible via the `interaction_coeff` attribute of the `Device`.\n", + "\n", + "3) The `Channels` in the `channel_objects` parameter are going to determine what [Channels are available for the computation](programming.md#pick-the-channels). Knowing what states you want to use in your computation, you can first check that they are among the `Device.supported_states`, then find the bases and their associated channel that enable to use these states using [the conventions page](conventions.md#bases).\n", + "\n", + "4) The `max_sequence_duration` constrains the duration of the [Pulses you can add](programming.md#add-the-pulses), and therefore the Hamiltonian describing the system can at most be defined between 0 and this value.\n", + "\n", + "5) The `max_runs` limits the number of runs a quantum program can be executed on the QPU. See [the section on Backends](./tutorials/backends.nblink) to read more about this." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "**Note**:\n", + "\n", + "If the Device associated with a QPU has `requires_layout=True`, then you have to define the `Register` from a layout. This adds more constraints to the creation of your `Register`, and is [presented in an advanced tutorial](./tutorials/reg_layouts.nblink).\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## The `Channels`" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "The third step to writing a Pulser program is [the selection of Channels among the Device](programming.md#pick-a-device).\n", + "\n", + "As a reminder, the selection of a `Channel` defines the [interaction Hamiltonian](programming.md#interaction-hamiltonian) and [the driving Hamiltonian](programming.md#driving-hamiltonian) $H^D$.\n", + "\n", + "$$\n", + "H^D(t) / \\hbar = \\frac{\\Omega(t)}{2} e^{-i\\phi} |a\\rangle\\langle b| + \\frac{\\Omega(t)}{2} e^{i\\phi} |b\\rangle\\langle a| - \\delta(t) |b\\rangle\\langle b|\n", + "$$\n", + "\n", + "The `Channels` available for selection are stored in the `channels` property of the `Device`, a dictionnary associating a `channel_id` to each `Channel` in `channel_objects`. For instance, `AnalogDevice` only contains one channel, the `rydberg_global` channel, which can be accessed with `AnalogDevice.channels[\"rydberg_global\"]`. " + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "### Reading through the `Channel`'s specifications" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "The `Channel` is defined by:" + ] + }, + { + "cell_type": "raw", + "metadata": { + "editable": true, + "raw_mimetype": "text/restructuredtext", + "slideshow": { + "slide_type": "" + }, + "tags": [], + "vscode": { + "languageId": "raw" + } + }, + "source": [ + ".. autoclass:: pulser.channels.base_channel.Channel\n", + " :noindex:" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "### Tips on `Channel` selection" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "The `Channel` is going to determine the computational basis used in the driving Hamiltonian, and what is the Hamiltonian each atom sees:\n", + "\n", + "- The type of the `Channel` defines the [states](conventions.md#bases) that can be addressed by the [driving Hamiltonian](programming.md#driving-hamiltonian) if this channel is picked. All the child classes of `Channel` can be found [here](./apidoc/_autosummary/pulser.channels.rst).\n", + "- The addressing of the `Channel` determines what atoms experience the driving Hamiltonian. In general, physical `Channels` have a `Global` addressability, which means that a Pulse added to this channel will implement the same driving Hamiltonian on all the atoms.\n", + "\n", + "The `Channel` also set constraints on the next stage of your quantum program, the addition of Pulses:\n", + "- the **duration** of the pulse is constrained by `min_duration` and `max_duration`, as well as `clock_period` (it has to be a multiple of the clock period).\n", + "- the **amplitude** is limited by the maximum amplitude `max_amp` and `min_avg_amp`.\n", + "- the **detuning** is limited by the maximum absolute detuning `max_abs_det`. It has to be between -`max_abs_det` and `max_abs_det`." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "
\n", + "\n", + "**Note**:\n", + "\n", + "The modulation bandwidth `mod_bandwidth` impacts the duration, the amplitude, the detuning and the phase of the Pulses. It is a more advanced feature explained [in this tutorial](./tutorials/output_mod_eom.nblink).\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## The `AnalogDevice`" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "import pulser\n", + "\n", + "print(pulser.AnalogDevice.specs)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "The `pulser.AnalogDevice` only supports the $\\left|r\\right>$ and $\\left|g\\right>$ states, because it only supports one channel of type \"Rydberg\" (that can be declared using its name \"rydberg_global\"). It implements the [Ising Hamiltonian](programming.md#ising-hamiltonian): \n", + "\n", + "$$\\frac{H}{\\hbar}(t) = \\sum_{k=1}^N \\left (\\frac{\\Omega(t)}{2} e^{-i\\phi(t)} |g\\rangle\\langle r|_k + \\frac{\\Omega(t)}{2} e^{i\\phi(t)} |r\\rangle\\langle g|_k - \\delta(t) |r\\rangle\\langle r|_k(t) + \\sum_{j$ and $\left|b\right>$, then the state of an atom is described by $\left|\psi\right> = \alpha \left|a\right> + \beta \left|b\right>$, with $|\alpha|^2 + |\beta|^2 = 1$. + +
+ + Definition of a quantum state with multiple atoms + +When multiple atoms are used, the state of the system is described by a linear combination of the _eigenstates_ of the multi-atom system, whose set is obtained by making the cross product of the set of eigenstate of each atom. If each atom is described by $d$ eigenstates labelled ${\left|a_1\right>, \left|a_2\right>...\left|a_d\right>}$ (each atom is a _qudit_), and if there are $N$ atoms in the system, then the state of the whole system is provided by + +$$ +\begin{align} +\left|\Psi\right> &= \sum_{i_1, ..., i_N \in [1, ..., d]} c_{i_1, ..., i_N} \left|a_{i_1}...a_{i_N}\right> \\ +&= c_{1, 1, ..., 1}\left|a_{1}a_{1}...a_{1}\right> + ... + c_{1, 1, ..., d}\left|a_{1}a_{1}...a_{d}\right> + ... + c_{d, d, ..., d}\left|a_{d}a_{d}...a_{d}\right> +\end{align} +$$ + +where $\sum_{i_1, ..., i_N \in [1, ..., d]} |c_{i_1, ..., i_N}|^2 = 1$. If $d=2$, then this becomes + +$$ +\left|\Psi\right> = c_{1, 1, ..., 1}\left|a_{1}a_{1}...a_{1}\right> + c_{1, 1, ..., 2}\left|a_{1}a_{1}...a_{2}\right> + ... + c_{2, 2, ..., 2}\left|a_{2}a_{2}...a_{2}\right> +$$ + +If $d=2$ and $N=1$, you have the state of a qubit as above $\left|\Psi\right> = c_{1}\left|a_{1}\right> + c_{2}\left|a_{2}\right>$. + +
+ +
+ +### 2. Hamiltonian evolves the state + +In quantum physics, the state of a quantum system evolves along time following the Schrödinger equation: + +$$i\frac{d\left|\Psi\right>(t)}{dt} = \frac{H(t)}{\hbar} \left|\Psi\right>(t)$$ + +Here $H(t)$ is the Hamiltonian describing the evolution of the system. For a system of atoms in the initial state $\left|\Psi_0\right>$, the final state of the system after a time $\Delta t$ is: + +$$ \left|\Psi_f\right> = \exp\left(-\frac{i}{\hbar}\int_0^{\Delta t} H(t) dt\right)\left|\Psi_0\right>$$ + +:::{tip} +The equation below uses the [Indexed Operator](conventions.md#indexed-operator) notation. +::: + +The Hamiltonian describing the evolution of the system can be written as + +$$ +H(t) = \sum_i \left (H^D_i(t) + \sum_{j + + Rotations on the Bloch sphere + + +In the Bloch sphere representation, this Hamiltonian describes a rotation around the axis $\overrightarrow{\Omega}(t) = (\Omega(t)\cos(\phi), -\Omega(t)\sin(\phi), -\delta(t))^T$, with angular velocity $\Omega_{eff}(t) = |\overrightarrow{\Omega}(t)| = \sqrt{\Omega^2(t) + \delta^2(t)}$. + +For a resonant pulse ($\delta(t)=0$) of duration $\Delta t$, we rotate of an angle $\int_0^{\Delta t} \Omega (t) dt$ around the fixed axis $(\cos(\phi), -\sin(\phi), 0)$ (on the equator of the Bloch sphere). + +:::{figure} files/bloch_rotation_a_b.png +:align: center +:alt: Representation of the drive Hamiltonian's dynamics as a rotation in the Bloch sphere. +:width: 300 + +Representation of the drive Hamiltonian's dynamics as a rotation in the Bloch sphere. The coherent excitation is driven between a lower energy level, $|a\rangle$, and a higher energy level, +$|b\rangle$, with Rabi frequency $\Omega(t)$, detuning $\delta(t)$ and phase $\phi$. +::: + + + +
+ +:::{important} +With Pulser, you program the driving Hamiltonian by setting $\Omega(t)$, $\delta(t)$ and $\phi(t)$, all the while Pulser ensures that you respect the constraints of your chosen device. +::: + +#### 2.2. Interaction Hamiltonian + +The interaction Hamiltonian depends on the distance between the atoms $i$ and $j$, $R_{ij}$, and the energy levels in which the information is encoded in these atoms, that define the interaction between the atoms $\hat{U}_{ij}$ + +$$ +H^\text{int}_{ij} = \hat{U}_{ij}(R_{ij}) +$$ + +The interaction operator $\hat{U}_{ij}$ is composed of an entangling operator and an interaction strength. + +:::{note} +The interaction Hamiltonian is constant over time. It is always on, no matter the values of the drive Hamiltonian (even if the values of the parameters $\Omega$, $\delta$, $\phi$ are equal to $0$ over a time $\Delta t$). +::: + +##### Ising Hamiltonian + +If the Rydberg state $\left|r\right>$ is involved in the computation, then + +$$ +\hat{U}_{ij}(R_{ij}) = \frac{C_6}{R_{ij}^6} \hat{n}_i \hat{n}_j +$$ +
+ + Interaction strength and entangling operator + +- The interaction strength is $\frac{C_6}{R_{ij}^6}$, with $C_6$ the Ising interaction coefficient that depends on the principal quantum number of the Rydberg state. +- The entangling operator between atom $i$ and $j$ is $\hat{n}_i\hat{n}_j = |r\rangle\langle r|_i |r\rangle\langle r|_j$. + +
+ +Together with the driving Hamiltonian, this interaction encodes the _Ising Hamiltonian_ and is the **most common choice in neutral-atom devices.** + +##### XY Hamiltonian + +If the information is stored in the Rydberg states $\left|0\right>$ and $\left|1\right>$ (the so-called `XY` basis), then + +$$ +\hat{U}_{ij}(R_{ij}) =\frac{C_3}{R_{ij}^3} (|1\rangle\langle 0|_i |0\rangle\langle 1|_j + |0\rangle\langle 1|_i |1\rangle\langle 0|_j) +$$ +
+ + Interaction strength and entangling operator + +- The interaction strength is $\frac{C_3}{R_{ij}^3}$, with $C_3$ a coefficient that depends on the energy levels used to encode $\left|0\right>$ and $\left|1\right>$. +- The entangling operator between atom $i$ and $j$ is $\hat{\sigma}_i^{+}\hat{\sigma}_j^{-} + \hat{\sigma}_i^{-}\hat{\sigma}_j^{+} = |1\rangle\langle 0|_i |0\rangle\langle 1|_j + |0\rangle\langle 1|_i |1\rangle\langle 0|_j$. + +
+ +This interaction hamiltonian is associated with the _XY Hamiltonian_ and is a less common mode of operation, usually accessible only in select neutral-atom devices. + +:::{important} +With Pulser, you program the interaction Hamiltonian by setting the distance between atoms, $R_{ij}$. Additionally, the choice of eigenstates used in the computation and the Rydberg level(s) targeted by the device fully determine the interaction strength. Most commonly, the ground and Rydberg states ($\left|g\right>$ and $\left|r\right>$) are used, such that + +$$H^\text{int}_{ij} = \frac{C_6}{R_{ij}^6} \hat{n}_i\hat{n}_j$$ + +When providing the distance between atoms, Pulser ensures that you respect the constraints of your chosen device. +::: + +## Writing a Pulser program + +As outlined above, Pulser lets you program an Hamiltonian ([the Hamiltonian $H$](programming.md#2-hamiltonian-evolves-the-state)) so that you can manipulate the quantum state of a system of atoms. The series of necessary instructions is encapsulated in the so-called Pulser `Sequence`. Here is a step-by-step guide to create your own Pulser `Sequence`. + +### 1. Pick a Device + + +:::{figure} files/decision_diagram_device.png +:align: center +:alt: Decision Diagram to select a Device for the computation +:width: 600 +::: + +The `Device` you select will dictate some parameters and constrain others. For instance, the value of the $C_6$ and $C_3$ coefficients of the [interaction Hamiltonian](programming.md#22-interaction-hamiltonian) are defined by the device. Notably, the `Device` defines the list of `Channels` that can be used in the computation, which have a direct impact on the Hamiltonian that can be implemented. + +:::{seealso} +For a complete view of the constraints introduced by the device, [check its description](./hardware.ipynb). +::: + +### 2. Create the Register + +The `Register` defines the position of the atoms. This determines: + +- the number of atoms to use in the quantum computation, i.e, the size of the system (let's note it $N$). +- the distance between the atoms, the $R_{ij}\ (1\le i, j\le N)$ parameters in the [interaction Hamiltonian](programming.md#22-interaction-hamiltonian). + +:::{seealso} +For an in-depth view on Register creation, check out [this page](register.ipynb). +::: + +### 3. Pick the Channels + +A `Channel` targets the transition between two energy levels. Therefore, picking channels defines the energy levels that will be used in the computation. The channels must be picked from the `Device.channels`, so your device selection should take into account the channels it supports. + +Picking the channel will initialize the state of the system, and fully determine the [interaction Hamiltonian](programming.md#22-interaction-hamiltonian): + +- If the selected Channel is the `Rydberg` or the `Raman` channel, the system is initialized in $\left|gg...g\right>$ and the interaction Hamiltonian is the [Ising Hamiltonian](programming.md#ising-hamiltonian) + +$$H^\text{int}_{ij} =\frac{C_6}{R_{ij}^6}|r\rangle\langle r|_i |r\rangle\langle r|_j$$ + +- If the selected Channel is the `Microwave` channel, the system is initialized in $\left|00...0\right>$ and the interaction Hamiltonian is the [XY Hamiltonian](programming.md#xy-hamiltonian) + +$$H^\text{int}_{ij} =\frac{C_3}{R_{ij}^3}|1\rangle\langle 0|_i |0\rangle\langle 1|_j + |0\rangle\langle 1|_i |1\rangle\langle 0|_j$$ + +:::{important} +At this stage, the [interaction Hamiltonian](programming.md#22-interaction-hamiltonian) is fully determined. +::: + +A `Channel` is also characterized by its addressing, which defines the number of atoms that are going to be targeted by a pulse. If the addressing of a `Channel` is `Global`, all the atoms will experience the same pulse targetting the same transition. In the [Hamiltonian $H$](programming.md#2-hamiltonian-evolves-the-state), all the driving Hamiltonians $H^D_i$ are expressed as + +$$ +H^D_i(t) / \hbar = \frac{\Omega(t)}{2} e^{-j\phi(t)} |a\rangle_i \langle b|_i + \frac{\Omega(t)}{2} e^{j\phi(t)} |b\rangle_i\langle a|_i - \delta(t) |b\rangle_i\langle b|_i +$$ + +If the addressing of a `Channel` is `Local`, then only certain atoms (the "targets") will experience the pulse and have their evolution follow $H^D_i$. The driving Hamiltonian of the other atoms is $H^D_i = \hat{0}_i$. The Hamiltonian $H$ can also be rewritten: + +$$ +H(t) = \sum_{i \in targets} H^D_i(t) + \sum_i \sum_{j$ and $\left|b\right>$ of the driving Hamiltonian. + +By applying a series of pulses and delays, one defines the entire driving Hamiltonian of each atom over time. + +:::{seealso} +For an in-depth view on Pulse creation, check out [this page](pulses.ipynb). +::: + +## Conclusion + +We have successfully defined the [Hamiltonian](programming.md#2-hamiltonian-evolves-the-state) $H$ describing the evolution of the system over time, by: +- Picking a `Device`, which defines the value of the $C_6$ or $C_3$ coefficients. +- Creating a `Register` of atoms, which defines the number of atoms used and the distance between them, $R_{ij}$. +- Selecting the `Channels` of the `Device` to use, which define the energy levels of the atoms to use - this step completely defines the [interaction Hamiltonian](programming.md#22-interaction-hamiltonian). The addressing property of each `Channel` also dictates the atoms that will be targeted by the `Pulse`. +- Adding `Pulse` and delays to the `Channel`s defines the [driving Hamiltonian](programming.md#21-driving-hamiltonian) of each atom along time. + +You can now simulate your first Hamiltonian by programming your first `Sequence`! [In this tutorial](tutorials/creating.nblink), you will simulate the evolution of the state of an atom initialized in $\left|g\right>$ under a Hamiltonian $H(t)=\frac{\Omega(t)}{2} |g\rangle \langle r|+\frac{\Omega(t)}{2} |r\rangle\langle g|$, with $\Omega$ chosen such that the final state of the atom is the excited state $\left|r\right>$. + +Many concepts have been introduced here and you might want further explanations. +- The `Device` object contains all the constraints and physical quantities that are defined in a QPU. [This section in the fundamentals](./hardware.ipynb) details these and provides examples of `Devices`. The `VirtualDevices` were also mentioned in this document ([here](programming.md#1-pick-a-device)), which is a more advanced feature described [here](tutorials/virtual_devices.nblink). +- There are multiple ways of defining a `Register`, as is further detailed [in this section](tutorials/reg_layouts.nblink). +- The energy levels associated with each `Channel` and the interaction Hamiltonian they implement are summed up in [the conventions page](conventions.md). The channels contain lots of constraints and physical informations, they are detailed in [the same section as the `Device`](./hardware.ipynb). +- The quantities in a `Pulse` are defined using `Waveform`s, you can read more about these [on this page](tutorials/composite_wfs.nblink). \ No newline at end of file diff --git a/docs/source/pulses.ipynb b/docs/source/pulses.ipynb new file mode 100644 index 000000000..52b4524a0 --- /dev/null +++ b/docs/source/pulses.ipynb @@ -0,0 +1,303 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Pulses and Waveforms\n", + "\n", + "*What you will learn:*\n", + "\n", + "- how `Pulse`s are used to define the [driving Hamiltonian](programming.md#driving-hamiltonian);\n", + "- what are `Waveform`s and which options you can choose from;\n", + "- how to create a `Pulse`;\n", + "- some helpful tips to keep in mind when creating a `Pulse`." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Programming the driving Hamiltonian\n", + "\n", + "To program the [driving Hamiltonian](programming.md#driving-hamiltonian) of a system with $N$ atoms in Pulser, one needs to combine two objects:\n", + "\n", + "- The `Channel`, which defines \n", + " - the [addressed basis](conventions.md#bases), i.e. the states $|a\\rangle$ and $|b\\rangle$) and\n", + " - which atoms are targeted. i.e. for which atom(s) in $i \\in \\{1,...,N\\}$ is $\\Omega_i$, $\\delta_i$ and $\\phi_i$ defined.\n", + "- The `Pulse`, which defines, over a given duration $\\Delta t$, \n", + " - the Rabi frequency, $\\Omega(t \\rightarrow t+\\Delta t)$ (given as a `Waveform`);\n", + " - the detuning, $\\delta(t \\rightarrow t+\\Delta t)$ (also given as a `Waveform`);\n", + " - the phase, $\\phi$, which is constant from $t$ to $t+\\Delta t$.\n", + "\n", + "
\n", + "\n", + "By adding **pulses** to **channels**, the full driving Hamiltonian is defined over the entire duration of the `Sequence`.\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "source": [ + "## Waveforms\n", + "\n", + "### The `Waveform` base class\n", + "\n", + "In Pulser, a [Waveform](apidoc/_autosummary/pulser.waveforms.Waveform.rst) defines some time-dependent parameter over a certain duration. Every `Waveform` has two key properties:\n", + "- its `duration`, which is an integer value in $ns$;\n", + "- its `samples`, which *define the Waveform's value at each* $ns$. \n", + "\n", + "
\n", + "\n", + "In Pulser, samples are *always* defined with a time step of **1 ns**. This means that, to access a value at `t=x #ns`, one can simply get `samples[x]`.\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "source": [ + "### Available Waveforms" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "To create a `Waveform`, one must use one of its subclasses:" + ] + }, + { + "cell_type": "raw", + "metadata": { + "editable": true, + "raw_mimetype": "text/restructuredtext", + "slideshow": { + "slide_type": "" + }, + "tags": [], + "vscode": { + "languageId": "raw" + } + }, + "source": [ + ".. currentmodule:: pulser.waveforms\n", + "\n", + ".. autosummary::\n", + "\n", + " ~pulser.waveforms.ConstantWaveform\n", + " ~pulser.waveforms.RampWaveform\n", + " ~pulser.waveforms.BlackmanWaveform\n", + " ~pulser.waveforms.InterpolatedWaveform\n", + " ~pulser.waveforms.CustomWaveform\n", + " ~pulser.waveforms.KaiserWaveform\n", + " ~pulser.waveforms.CompositeWaveform" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "vscode": { + "languageId": "plaintext" + } + }, + "source": [ + "## Pulses\n", + "\n", + "### Standard definition\n", + "\n", + "To create a `Pulse`, one must define $\\Omega(t)$, $\\delta(t)$ and $\\phi$ over a duration $\\Delta t$. While the phase $\\phi$ must be constant in each `Pulse`, $\\Omega$ and $\\delta$ are time-dependent; as such, they are defined as waveforms.\n", + "\n", + "
\n", + "\n", + "In a `Pulse`, $\\Omega(t)$ and $\\delta(t)$ are always in units of $rad/\\mu s$, while $\\phi$ is in $rad$.\n", + "\n", + "
\n", + "\n", + "\n", + "As an example, below is a 500 $ns$ `Pulse`, with amplitude given by a `BlackmanWaveform` of area $\\pi$, detuning given by a `RampWaveform` from -10 to 10 $rad/\\mu s$ and a phase of $\\pi/2$.\n", + "\n", + "
\n", + "\n", + "In Pulser, **Rabi frequency** and **amplitude** are equivalent terms for $\\Omega$ and are used interchangeably.\n", + "\n", + "
\n", + "\n", + "
\n", + "\n", + "In the same Pulse, the amplitude and detuning waveforms **must have the same duration**.\n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pulser\n", + "\n", + "pulse = pulser.Pulse(\n", + " amplitude=pulser.BlackmanWaveform(500, np.pi),\n", + " detuning=pulser.RampWaveform(500, -10, 10),\n", + " phase=np.pi / 2,\n", + ")\n", + "pulse.draw() # Draws the Pulse" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [], + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "### Shortcuts for constant parameters\n", + "\n", + "When the `amplitude` or `detuning` are constant, these class methods avoid having to use `ConstantWaveform`:" + ] + }, + { + "cell_type": "raw", + "metadata": { + "editable": true, + "raw_mimetype": "text/restructuredtext", + "slideshow": { + "slide_type": "" + }, + "tags": [], + "vscode": { + "languageId": "raw" + } + }, + "source": [ + ".. currentmodule:: pulser.pulse\n", + "\n", + ".. autosummary::\n", + " :nosignatures:\n", + "\n", + " Pulse.ConstantAmplitude\n", + " Pulse.ConstantDetuning\n", + " Pulse.ConstantPulse" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "- In `Pulse.ConstantAmplitude()` and `Pulse.ConstantDetuning()`, the pulse `duration` is taken from the `Waveform` parameter.\n", + "- In `Pulse.ConstantPulse()`, `duration` must be explicitly given.\n", + "\n", + "
\n", + "\n", + "Below is an example of these methods in action, all of them creating the same 1000 $ns$ pulse with $\\Omega=1~rad/\\mu s$, $\\delta=-1~rad/\\mu s$ and $\\phi=0$." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import pulser\n", + "\n", + "const1 = pulser.ConstantWaveform(1000, 1) # A constant waveform of 1000 ns\n", + "pulse1 = pulser.Pulse.ConstantAmplitude(\n", + " amplitude=1, # float\n", + " detuning=-const1, # Waveform\n", + " phase=0, # float\n", + ")\n", + "pulse2 = pulser.Pulse.ConstantDetuning(\n", + " amplitude=const1, # Waveform\n", + " detuning=-1, # float\n", + " phase=0, # float\n", + ")\n", + "pulse3 = pulser.Pulse.ConstantPulse(\n", + " duration=1000, # int\n", + " amplitude=1, # float\n", + " detuning=-1, # float\n", + " phase=0, # float\n", + ")\n", + "assert pulse1 == pulse2 == pulse3" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Tips for Pulse creation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Keep the `Channel` constraints in mind\n", + "\n", + "Some `Channel` parameters dictate what is allowed in a `Pulse` once it's added to the `Sequence`, so it is often useful to have these limitations in mind when first designing the `Pulse`. In particular, you should keep in mind:\n", + "\n", + "- `Channel.max_abs_detuning`, the maximum absolute value of detuning allowed;\n", + "- `Channel.max_amp`, the maximum amplitude allowed;\n", + "- `Channel.min_avg_amp`, the minimum average amplitude allowed (when not zero);\n", + "- `Channel.min_duration`, the minimum pulse duration allowed;\n", + "- `Channel.clock_period`, which dictates that every pulse's duration must be a multiple of this value.\n", + "\n", + "### Remember that waveforms can be concatenated\n", + "\n", + "When programming $\\Omega(t)$ and $\\delta(t)$ with Pulser, it's usually preferable to divide these quantities into a stream of simple pulses. However, this is not always convenient, as the natural breaking point in the `amplitude` and `detuning` waveforms may not always match. In these cases, the `CompositeWaveform` allows for the creation of a more complex waveform by concatenation of multiple, smaller waveforms. Take a look a [this page](tutorials/composite_wfs.nblink) to see how `CompositeWaveform` might help you." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "__venv__", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/source/register.ipynb b/docs/source/register.ipynb new file mode 100644 index 000000000..ec22e64eb --- /dev/null +++ b/docs/source/register.ipynb @@ -0,0 +1,372 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "2a6852ac-a3b9-4114-85ac-2be21c6633b0", + "metadata": {}, + "source": [ + "# Register and Rydberg-Atom Interactions" + ] + }, + { + "cell_type": "markdown", + "id": "e6b26e9c", + "metadata": {}, + "source": [ + "*What you will learn:*\n", + "- what is a `Register` and how to create it;\n", + "- why the relative position of the atoms in a `Register` is important;\n", + "- what is the Rydberg blockade;\n", + "- how the design of the `Register` may be influenced by the rest of the `Sequence`;\n", + "- tips on how to design a `Register` for different applications." + ] + }, + { + "cell_type": "markdown", + "id": "979ebffc-2017-4b9c-8aa3-27bceda01d38", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## The `Register`\n", + "\n", + "The `Register` is a group of cold atoms placed in space according to a user-defined configuration. Each atom is in a quantum state encoded in [specific electronic levels](conventions.md#states-and-bases). Usually these are two-level systems and we refer to them as **qubits**.\n", + "\n", + "### Standard definition\n", + "\n", + "There are multiple ways to define a `Register`, the most customizable one being to create a dictionary that associates a name (the key) to a cooordinate (the value).\n", + "\n", + "
\n", + "\n", + "Despite being given as a mapping to `Register`, **the order of the qubits matters** and is preserved. In particular, this order is respected in the [representation of multi-partite quantum states](conventions.md#multi-partite-states). When in doubt, it can be accessed via `Register.qubit_ids`. \n", + "\n", + "
" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6b039af2-22d5-47ad-bc8a-97ef57737b08", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "outputs": [], + "source": [ + "import pulser\n", + "\n", + "# Manually generate the IDs and coordinates of a 2x2 square with 5μm spacing\n", + "qubits = {\"q0\": [0, 0], \"q1\": [5, 0], \"q2\": [0, 5], \"q3\": [5, 5]}\n", + "reg = pulser.Register(qubits)\n", + "reg.draw()" + ] + }, + { + "cell_type": "markdown", + "id": "66df3519-7d33-4557-8d6d-ac9fb179b380", + "metadata": {}, + "source": [ + "### From coordinates\n", + "\n", + "When it is convenient to label the qubits automatically, the `Register` can also be created from a list of coordinates (using the `Register.from_coordinates` class method). In this case, the qubit ID's are just numbered, starting from 0, in the order they are provided in, with the option of adding a common prefix before each number. Also, it automatically centers the entire array around the origin, an option that can be disabled if desired." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0a265ebd-d379-4ba3-b735-bae87216c176", + "metadata": {}, + "outputs": [], + "source": [ + "import pulser\n", + "\n", + "reg2 = pulser.Register.from_coordinates(\n", + " [[0, 0], [5, 0], [0, 5], [5, 5]], # Takes just the coordinates\n", + " prefix=\"q\", # All qubit IDs will start with 'q'\n", + " center=True,\n", + ")\n", + "print(\"(Centered) qubits:\", reg2.qubits)" + ] + }, + { + "cell_type": "markdown", + "id": "5fac062c-b653-4c32-84c9-f72b3b8e8334", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "### From common patterns\n", + "\n", + "Furthermore, there are built-in class methods for creation of common array patterns - for instance, `Register.square()` offers a convenient shortcut to make a register in a centered square configuration, so it does not have to be done manually as done above." + ] + }, + { + "cell_type": "raw", + "id": "38dd64e4-472b-46f7-8617-dbdbcbadb160", + "metadata": { + "editable": true, + "raw_mimetype": "text/restructuredtext", + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + ".. currentmodule:: pulser.register.register\n", + "\n", + ".. autosummary::\n", + "\n", + " Register.square\n", + " Register.rectangle\n", + " Register.rectangular_lattice\n", + " Register.triangular_lattice\n", + " Register.hexagon" + ] + }, + { + "cell_type": "markdown", + "id": "a05f8e2d", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "For more information on all the `Register` methods, please refer to the [Register API reference](apidoc/_autosummary/pulser.register.Register.rst#register)." + ] + }, + { + "cell_type": "markdown", + "id": "cd280505", + "metadata": {}, + "source": [ + "## Rydberg-Atom Interactions\n", + "\n", + "When an atom is excited to a Rydberg state, nearby atoms interact according to the [interaction Hamiltonian](programming.md#interaction-hamiltonian). The interaction strength is always dependent on the distance between atoms and is stronger the closer they are. Therefore, appropriately selecting the atoms' relative positions is a crucial step in the programming of neutral-atom QPUs. \n", + "\n", + "In the most common case of the [Ising Hamiltonian](programming.md#ising-hamiltonian), the interaction operator is given by\n", + "\n", + "$$\n", + "\\hat{U}_{ij} = \\frac{C_6}{R_{ij}^6} \\hat{n}_i \\hat{n}_j,\n", + "$$\n", + "\n", + "where \n", + "\n", + "- the interaction strength is $\\frac{C_6}{R_{ij}^6}$, where $C_6$ is a coefficient that depends on the principal quantum number of the Rydberg state the atoms are excited to;\n", + "- the entangling operator between atom $i$ and $j$ is $\\hat{n}_i\\hat{n}_j = |r\\rangle\\langle r|_i |r\\rangle\\langle r|_j$. \n", + "\n", + "Note that:\n", + "\n", + "1. The interaction strength scales with $R_{ij}^{-6}$, so it decays rapidly when the distance between the atoms increases.\n", + "2. There is only an interaction when both atoms are excited to their respective Rydberg states, $|r\\rangle_i$ and $|r\\rangle_j$." + ] + }, + { + "cell_type": "markdown", + "id": "7023920c", + "metadata": { + "editable": true, + "slideshow": { + "slide_type": "" + }, + "tags": [] + }, + "source": [ + "## The Rydberg Blockade \n", + "\n", + "Consider a system of two atoms, $R$ distance apart, that we want to excite from the $|gg\\rangle$ state to the $|rr\\rangle$ state. To keep things simple, we'll use a global resonant pulse (i.e.$\\delta=0$) with constant Rabi frequency $\\Omega(t) = \\Omega$ and phase $\\phi=0$, so that the [full Hamiltonian](programming.md#hamiltonian-evolves-the-state) is\n", + "\n", + "$$H = \\frac{\\hbar\\Omega}{2}\\left[\\mathbb{I} \\otimes \\sigma_x + \\sigma_x \\otimes \\mathbb{I} \\right] + \\frac{C_6}{R^6} |rr\\rangle\\langle rr|,$$\n", + "\n", + "where $\\sigma_x = |g\\rangle\\langle r| + |r\\rangle\\langle g|$.\n", + "\n", + "The interaction Hamiltonian dictates that there is an additional energy of $C_6/R^6$ for being in the $|rr\\rangle$ state - that is to say, the energy of the $|rr\\rangle$ state is shifted by this amount.\n", + "\n", + "When we try to drive the transition to the $|rr\\rangle$ state, the excitation does not occur when $\\hbar\\Omega \\ll C_6/R^6$, i.e the energy of the drive is not sufficient to overcome the extra cost of having the system in in the $|rr\\rangle$ state - this is the so-called *Rydberg blockade*. Instead, the system is excited to $(|gr\\rangle + |rg\\rangle)/\\sqrt{2}$ (notably, an entangled state) with effective Rabi frequency $\\sqrt{2}\\Omega$. \n", + "\n", + "From the Rydberg blockade condition, we define the **Rydberg blockade radius** as\n", + "\n", + "$$R_b = \\left(\\frac{C_6}{\\hbar\\Omega}\\right)^{(1/6)}$$\n", + "\n", + "For any pair of atoms $i$ and $j$ in a system under a global resonant drive:\n", + "- When $R_{ij} \\ll R_b$, the excitation to $|rr\\rangle$ is suppressed.\n", + "- When $R_{ij} \\gg R_b$, the excitation to $|rr\\rangle$ occurs.\n", + "\n", + "
\n", + "\n", + "
\n", + "\n", + "**Important notes**:\n", + "\n", + "- The Rydberg blockade radius is only a useful approximation to reason about whether two atoms interact significantly; it *should not* be interpreted as a discrete threshold beyond which there are no interactions.\n", + "- In fact, the approximation is least adequate for values of $R_{ij} \\approx R_b$, so placing atoms at distances close to $R_b$ should be done extra carefully.\n", + "- Furthermore, $R_b$ depends on the Rabi frequency $\\Omega$; as such, **fixing** $R_b$ **also determines** $\\Omega$ and vice-versa. \n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "id": "935fb6c0", + "metadata": {}, + "source": [ + "### Estimating the Rydberg blockade radius\n", + "\n", + "The `Device` class includes methods to calculate the Rydberg blockade radius for a given value of Rabi frequency and vice-versa." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "164cde0c", + "metadata": {}, + "outputs": [], + "source": [ + "import pulser\n", + "\n", + "# Blockade radius from Rabi frequency\n", + "omega = 1 # rad/μs\n", + "rb = pulser.AnalogDevice.rydberg_blockade_radius(omega) # μm\n", + "print(f\"Rydberg blockade radius for Ω={omega} rad/μs: {rb} μm\")\n", + "\n", + "# Rabi frequency from Blockade radius\n", + "rb = 8 # μm\n", + "omega = pulser.AnalogDevice.rabi_from_blockade(rb) # rad/μs\n", + "print(f\"Rydberg blockade radius for Ω={omega} rad/μs: {rb} μm\")" + ] + }, + { + "cell_type": "markdown", + "id": "4c39956e", + "metadata": {}, + "source": [ + "### Visualising interactions\n", + "\n", + "The `Register.draw()` method includes options to plot the Rydberg blockade radius and identifiy interacting atoms. By specifying a value for `blockade_radius`,\n", + "- `draw_half_radius=True` draws a circle with **half** the Rydberg blockade radius on each atom; when two circles overlap, the atoms are within a blockade radius of each other.\n", + "- `draw_graph=True` draws connections between the atoms within a blockade radius of each other." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6d1aa660", + "metadata": {}, + "outputs": [], + "source": [ + "from pulser import Register\n", + "\n", + "# 4x3 triangular lattice with 6μm spacing\n", + "tri_reg = Register.triangular_lattice(\n", + " rows=4, atoms_per_row=3, spacing=6.0, prefix=\"q\"\n", + ")\n", + "# Draw the interactions for Rb=7 μm\n", + "tri_reg.draw(\n", + " blockade_radius=7, # μm\n", + " draw_half_radius=True, # Draws circles with radius Rb/2\n", + " draw_graph=True, # Draws edges between interacting atoms\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "ce1defb6", + "metadata": {}, + "source": [ + "## Tips for `Register` design" + ] + }, + { + "cell_type": "markdown", + "id": "12f85b91", + "metadata": {}, + "source": [ + "Choosing the best position for the atoms in a `Register` is generally a hard problem and depends heavily on the application. In this section, we provide some strategies that, while far from exhaustive, may help in the `Register` creation process in specfic cases." + ] + }, + { + "cell_type": "markdown", + "id": "40247508", + "metadata": {}, + "source": [ + "### Think of the full Hamiltonian\n", + "\n", + "When using a neutral-atom QPU to simulate a quantum many-body system, it is important to remember that the interaction Hamiltonian is but one part of the full Hamiltonian. In particular, the strength of the interaction terms must always be considered in relation to the driving Hamiltonian terms (i.e. $\\Omega$ and $\\delta$) - in fact, the interdependence between $R_b$ and $\\Omega$ is itself a prime example as to why these terms should not be designed in isolation.\n", + "\n", + "Take the example of [AFM state preparation](tutorials/creating.nblink#Adiabatic-preparation-of-an-Anti-Ferromagnetic-State), where the interaction strength must be balanced with the appropriate value of $\\delta>0$; without taking the full Hamiltonian into account, we could end up with:\n", + "- $\\delta$ too low, which would not promote atoms to the $|r\\rangle$ state, or\n", + "- $\\delta$ too high, which would make all atoms go to the $|r\\rangle$ state, regardless of their nearest neighbours being also in $|r\\rangle$.\n", + "\n", + "In these cases, it is only by first considering the full Hamiltonian that we are able to correctly design the register." + ] + }, + { + "cell_type": "markdown", + "id": "c05632f6", + "metadata": {}, + "source": [ + "### Encode a cost function\n", + "\n", + "Akin to a penalty term in a cost function, the interaction Hamiltonian makes some quantum states energetically less favourable. By appropriately adjusting the distances between atoms, the penalty of specific candidate solutions can sometimes be replicated in the interaction Hamiltonian.\n", + "\n", + "Examples where this approach is useful include:\n", + "- some instances of [QUBO](https://en.wikipedia.org/wiki/Quadratic_unconstrained_binary_optimization) problems,\n", + "- other optimization problems where the ground-state of the Hamiltonian encodes a minimizer of the cost function.\n" + ] + }, + { + "cell_type": "markdown", + "id": "bc0b100b", + "metadata": {}, + "source": [ + "### Start from a connectivity graph\n", + "\n", + "In the formulation of some problems, the exact value of the interaction strength between two atoms is not relevant; instead, all that matters is the presence or absence of interactions. As such, we can express these interactions through a so-called *connectivity graph*, where a node is an atom and an edge connects interacting atoms (as drawn in [this section](#Visualising-interactions)).\n", + "\n", + "In these cases, the [Rydberg blockade radius](#The-Rydberg-Blockade) provides a useful approximation by allowing us to place interacting atoms well within a blockade radius of each other and non-interacting atoms well outside it. Turning a connectivity graph into a `Register` is particularly straigthfoward when the connectivity graph can be represented as a [Unit-Disk graph](https://en.wikipedia.org/wiki/Unit_disk_graph). \n", + "\n", + "Examples where this approach is useful include:\n", + "- finding the Maximum Independent Set of a Unit-Disk graph,\n", + "- placing atoms for execution of multi-qubit gates." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/source/review.rst b/docs/source/review.rst deleted file mode 100644 index fa8d2ad62..000000000 --- a/docs/source/review.rst +++ /dev/null @@ -1,200 +0,0 @@ -**************************************** -Quantum Computing with Neutral Atoms -**************************************** - -Programmable arrays of Rydberg atoms -#################################### - -With the rise in our capacity to control and explore synthetic quantum -systems, the opportunity to use them to address questions of scientific and -industrial interest has attracted the efforts of a large community of -researchers. Today, the control of single atoms as well as the tuning of their -interactions has been achieved to a high degree in several laboratories. -One of the leading architectures for constructing these *programmable* devices -consists in arranging ensembles of individual (trapped) atoms separated by a -few micrometers. In order to generate interactions between them, they are -excited by a resonant laser field to a **Rydberg level**, which has a large -principal quantum number. At the Rydberg state, the atoms present long -lifetimes and large interaction strengths and are briefly called -**Rydberg atoms**. - -This page gives brief insight into the concepts behind **Pulser** and -the use of neutral-atom devices for quantum computation and simulation. For -more details, see `Quantum 4, 327 (2020) `_ -for a comprehensive overview of quantum computing with neutral atoms, -`Nature Physics volume 16, pages132–142(2020) `_ -for a review that's more focused on their use as a quantum simulation, or even -`M Saffman 2016 J. Phys. B: At. Mol. Opt. Phys. 49 202001 `_. - - -Implementation and Theoretical Details -###################################### - -Hardware Characteristics -************************** -In a nutshell, neutral atom devices feature two main components: - -* The **Register**, a group of trapped atoms in a defined (but reconfigurable) - configuration. Each atom holds a specific quantum state encoded in specific - electronic levels. Usually these are two-level systems and we refer to them - as *qubits*. -* The **Channels**, responsible for manipulating the state of the atoms by - addressing specific electronic transitions. These consist most often, but not - exclusively, of lasers. - -Each **Device** will impose specific restrictions on these components -- they define things -like how many atoms a Register can hold and in what configurations they can be -arranged in; what channels are available and what values they can reach, among others. -For this reason, a **Sequence** is closely dependent on the **Device** it -is meant to run on and should be created with it in mind from the start. - -This **Sequence** is the central object in Pulser and it consists essentially -of a series of **Pulses** (and other instructions) that are sequentially -allocated to channels. - -Each **Pulse** describes, over a finite duration, the modulation of a -channel's output *amplitude*, *detuning* and *phase*. While the phase is constant -throughout a pulse, the amplitude and detuning are described by **Waveforms**, -which define these quantities values throughout the pulse. - -.. figure:: files/pulser_animation.gif - :align: center - :alt: pulser_animation - :figclass: align-center - - A pulse sequence and its execution on a given device. - -In the animation above, we find an example of a Sequence composed of three -channels, each containing different pulses played at specific times. Upon -execution, the channels emit this coordinated stream of pulses, which manipulate -the state of the atoms in the register. - -Now, what is left to know is *how* these channels manipulate the state the atoms -so that we can program them to do meaningful things. To do this, we have to dive -into the physics and try to understand the underlying **Hamiltonian** of our -systems. - - -Driving two-level transitions -****************************** - -Each channel is tuned such that each of its pulses coherently drives a -specific electronic transition between two energy levels of an atom. -For instance, when addressing the ``ground-rydberg`` transition, we have that -our targeted levels are the ground state, :math:`|g\rangle`, and the Rydberg -state, :math:`|r\rangle`. These two states can be thought of as the two levels -of a quantum spin. - -.. image:: files/ground_rydberg.png - :align: center - :width: 400 - :alt: The ground and Rydberg levels become the states of a spin system. - -In this system, a pulse acting on an atom :math:`i`, with **Rabi frequency** -:math:`\Omega(t)`, **detuning** :math:`\delta(t)` and a fixed phase :math:`\phi`, -will have the Hamiltonian terms: - -.. math:: \frac{\hbar\Omega(t)}{2} (\cos(\phi)\sigma_i^x - \sin(\phi)\sigma_i^y) - \frac{\hbar}{2} \delta(t) \sigma_i^z, - -where :math:`\sigma^\alpha` for :math:`\alpha = x,y,z` are the Pauli matrices. -Alternatively, one can rewrite this term as: - -.. math:: \frac{\hbar}{2} \mathbf{\Omega}(t)\cdot \boldsymbol{\sigma}_i, - -where :math:`\mathbf{\Omega}(t) = (\Omega(t) \cos(\phi),-\Omega(t) \sin(\phi),-\delta(t))^T` -and :math:`\boldsymbol{\sigma}` is the vector of Pauli matrices. In the Bloch sphere representation, -for each instant :math:`t`, this Hamiltonian describes a rotation around the axis -:math:`\mathbf{\Omega}` with angular velocity :math:`\Omega_{eff} = |\mathbf{\Omega}| = \sqrt{\Omega^2 + \delta^2}`, -as illustrated in the figure below. - -.. image:: files/bloch_rotation.png - :align: center - :width: 400 - :alt: Representation of the dynamics induced by a pulse as a rotation in the - Bloch sphere. - -Rydberg states -****************************** - -In neutral atom devices, atoms are driven to Rydberg states as a way to make -them interact over large distances. The interaction between two atoms at distance -:math:`R` and at the same Rydberg level is described by the **Van der Waals force**, -which scales as :math:`R^{-6}`. This interaction can be exploited to create fast and -robust quantum gates, using the so-called **Rydberg Blockade Effect** between -them. This effect consists on the shift in energy between the doubly excited -Rydberg state of nearby atoms and their ground state, making it non-resonant -with an applied laser field coupling the ground and Rydberg levels. - -.. image:: files/ryd_block.png - :align: center - :width: 400 - :alt: There is no simultaneous transition to the doubly excited state inside the blockade radius. - -Because of the Rydberg blockade, an atom cannot be excited to the Rydberg level -if a nearby atom is already in such state. To represent this interaction as -operators in a Hamiltonian, we write them as a penalty for the state in which -both atoms are excited: - -.. math:: U_{ij} n_i n_j, - -where :math:`n = (1+\sigma^z)/2` is the projector on the Rydberg state, -:math:`U_{ij} \propto R_{ij}^{-6}` and :math:`R_{ij}` is the distance -between the atoms :math:`i` and :math:`j`. The proportionality constant is set -by the chosen Rydberg level. If the atoms are excited simultaneously, only the -entangled state :math:`(|gr\rangle + |rg\rangle)/\sqrt 2` is obtained. - -An entire array of interacting atoms, acted on by the same pulse, can be -represented as an Ising-like Hamiltonian: - -.. math:: - H = \frac{\hbar}{2} \sum_i \Omega_i(t) \sigma_i^x - \frac{\hbar}{2} \sum_i - \delta(t) \sigma_i^z + \sum_{i\n", + "\n", + "See Also\n", + " \n", + "All the `Sequence` properties and methods can be found in [its API reference](apidoc/_autosummary/pulser.sequence.Sequence.rst).\n", + "\n", + "\n", + "\n", + "### Building" + ] + }, + { + "cell_type": "raw", + "metadata": { + "editable": true, + "raw_mimetype": "text/restructuredtext", + "slideshow": { + "slide_type": "" + }, + "tags": [], + "vscode": { + "languageId": "raw" + } + }, + "source": [ + ".. currentmodule:: pulser.sequence\n", + "\n", + ".. autosummary::\n", + "\n", + " Sequence.declare_channel\n", + " Sequence.add\n", + " Sequence.delay\n", + " Sequence.measure" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "These four method are all you need to build a basic Pulser `Sequence`. As shown in [this tutorial](tutorials/creating.nblink), they should be used in this order:\n", + "\n", + "1. Pick a channel from the `Device` and declare it with `Sequence.declare_channel()`.\n", + "2. Add pulses and delays to the channel with `Sequence.add()` and `Sequence.delay()`.\n", + "3. Terminate the sequence with `Sequence.measure()`.\n", + "\n", + "
\n", + "\n", + "Tip\n", + " \n", + "For more details on each of these methods, click on them in the table above.\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Inspecting\n", + "\n", + "During and after the sequence building process, you migh want to inspect or access its contents. These properties and methods allow you to do so:" + ] + }, + { + "cell_type": "raw", + "metadata": { + "editable": true, + "raw_mimetype": "text/restructuredtext", + "slideshow": { + "slide_type": "" + }, + "tags": [], + "vscode": { + "languageId": "raw" + } + }, + "source": [ + ".. currentmodule:: pulser.sequence\n", + "\n", + ".. rubric:: Properties\n", + "\n", + ".. autosummary::\n", + " :nosignatures:\n", + "\n", + " Sequence.declared_channels\n", + " Sequence.device\n", + "\n", + ".. rubric:: Methods\n", + "\n", + ".. autosummary::\n", + "\n", + " Sequence.draw\n", + " Sequence.get_addressed_bases\n", + " Sequence.get_addressed_states\n", + " Sequence.get_duration\n", + " Sequence.get_measurement_basis\n", + " Sequence.get_register\n", + " Sequence.is_measured" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Drawing and Printing\n", + "\n", + "In particular, it is often useful to visualize a `Sequence`'s contents, which we can do either by **drawing** or **printing**. \n", + "Let's exemplify these two options with the very simple sequence in [this tutorial](tutorials/creating.nblink#Preparing-an-atom-in-the-Rydberg-state)." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Channel: rydberg_global\n", + "t: 0 | Initial targets: q0 | Phase Reference: 0.0 \n", + "t: 0->1000 | Pulse(Amp=3.14 rad/µs, Detuning=0 rad/µs, Phase=0) | Targets: q0\n", + "\n", + "\n" + ] + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABp0AAAEvCAYAAAC35glfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuNSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/xnp5ZAAAACXBIWXMAAA9hAAAPYQGoP6dpAACKy0lEQVR4nOzdd3xUdb7/8feZJJM+6Y0AAUSKdLBhAwXbYkF3XXVdsay6+rMuey24ay94r+uiq177iu7q2q5iLyyCqKBSRCBAqEkgyaQnkzpJZs7vj2yicYAQJpMpeb3ug4dm5nvO85urSc7m45kxTNM0RURERERERERERERERORFFn9vgIiIiIiIiIiIiIiIiII/hk5ERERERERERERERETkdQydiIiIiIiIiIiIiIiIyOsYOhEREREREREREREREZHXMXQiIiIiIiIiIiIiIiIir2PoRERERERERERERERERF7H0ImIiIiIiIiIiIiIiIi8jqETEREREREREREREREReR1DJyIiIiIiIiIiIiIiIvI6hk5ERERERERERERERETkdQydiIiIiIiIiIiIiIiIyOsYOhEREREREREREREREZHXhft7A+SfCgoK9NZbb+m7775TbU2NTNPt9TktljAlJiXpuOOO0y9/+UtlZmb2wk6JiIiIiIiIiIiIiCgYMkzTNP29CfJtbrdbxcXFio+Pl2mauvnmm/X8888rMsKiqYdKybFuWQzvHZdbKq+z6Jvtplxu6dZbb9Vtt90mw+iFkxMRERERERERERERUa9kmqbq6uo0YMAAWSy996J4DJ36QXv27NGgQYM6PzYkPfJb6YrpUnx073tV9dKCj6X7F/X+uYmIiIiIiIiIiIiIqHfavXu3Bg4c2Gvn4+X1+kHx8fGSpO+//15TpkzWg7829YfTfeclx0n3nSfVN0vPfxmt7Tt2KTr6wKdbb775ps477zzfbZCIiIiIiIiIiIiIqB/ncDg0aNCgzvlBb8XQqR/U8fJ2S5culWFIv5veN+7vZ0iPftKklStXavbs2Qd8XExMjGw2m+82RkREREREREREREREvf72OL33Qn0U8P3www+akBOm1N4dXO6zUQOkrORwrV27tkfHHX/88T7aERERERERERERERER+SqGTv2o+vp6JUa7+9RMijXkcDh6dIzdbvfRboiIiIiIiIiIiIiIyFcxdOpHmaYpSx//E7cY7W5P2rZtm492Q0REREREREREREREvoqhEwVcvf0akkRERERERERERERE5PsYOlGPe2aJdNjNvjv/BRdc4LuTExERERERERERERGRT2LoRD2uok7KK/Hd+RctWuS7kxMRERERERERERERkU9i6EQBV1NTk7+3QEREREREREREREREPYyhEwVcgwYN8vcWiIiIiIiIiIiIiIiohzF0ooBr1KhR/t4CERERERERERERERH1MIZOfdxTTz2l8ePHy2azyWazaerUqfr444/3uX7hwoUyDKPLn6ioqD7ccd+3ePFif2+BiIiIiIiIiIiIiIh6WLi/N9DfGjhwoB566CEdeuihMk1TL730ks4++2x9//33GjNmzF6PsdlsysvL6/zYMIxe39dfPzrwtV9v7XWeiIiIiIiIiIiIiIiCPIZOfdyZZ57Z5eMHHnhATz31lL755pt9Dp0Mw1BmZqZP9/Vfr/Zsfe+PvX7smGOO8eHZiYiIiIiIiIiIiIjIFzF08mMul0tvvvmmGhoaNHXq1H2uq6+vV05OjtxutyZPnqwHH3xwnwMqSXI6nXI6nZ0fOxyObvey9E8927svq6qqUk5Ojr+3QUREREREREREREREPYihkx/asGGDpk6dqubmZsXFxemdd97RYYcdtte1I0eO1N///neNHz9etbW1+stf/qJjjjlGubm5Gjhw4F6PmT9/vu65554e7Wna6B5/Gj5ry5YtmjRpkr+3QUREREREREREREREPcji7w30x0aOHKl169bp22+/1TXXXKNLLrlEmzZt2uvaqVOnas6cOZo4caKmTZumt99+W2lpaXrmmWf2ef558+aptra288/u3bt7df/f7ZCufqFXT0lEREREREREREREREEedzr5IavVquHDh0uSpkyZolWrVumxxx7b7yCpo4iICE2aNEnbt2/f55rIyEhFRkb22n4lqbJOevkr6e/LpE1F7Y89/bteJTr79a9/7ZsTExERERERERERERGRz+JOpwDI7XZ3eQ+m/eVyubRhwwZlZWX5eFeSaUofr5N+9aiUfb30x1ekBqc09xfS13f5zv3oo498d3IiIiIiIiIiIiIiIvJJ3OnUx82bN0+nn366Bg8erLq6Or366qtatmyZPv30U0nSnDlzlJ2drfnz50uS7r33Xh199NEaPny4ampq9PDDD6ugoEBXXHGFz/a4s6z9jqaXvpSKq6W4KKm1TXriEun/newztrP6+nrfI0RERERERERERERE1KsxdOrjysrKNGfOHJWUlCghIUHjx4/Xp59+qpNPbp/mFBYWymL58Qa06upqXXnllbLb7UpKStKUKVO0YsUKHXbYYb26r+YW6c1vpb9/IS3fIoWHSbMmSpeeIB2aKY25VcpM7FVynw0YMKBvICIiIiIiIiIiIiIi6rUYOvVxL7zwwn6fX7ZsWZePFyxYoAULFvhwR+1lXivVNUkTc6RHL5Z+c4yUEt/+3I5Sn/NdmjBhQt+CRERERERERERERETkdbynE0mSHE3SIRnt79d0xYk/Dpz80ccff+w/nIiIiIiIiIiIiIiIDiqGTiRJevJSKSFGuvgpKfP/SVc8J325xd+7IiIiIiIiIiIiIiKiYImX1yNJ0jUz2//8UCC9sEx6dYX04hfSkDTplHGS0Yd7OfLII/tQIyIiIiIiIiIiIiKi3og7nahLE3Kkv10iFT8pvXqtdEi69NxSyZT0wCLpsU+kwgrf7qGxsdG3ABERERERERERERER9XoMnWivWcOl86dKn82Tdi6Q7jxHqmqQ/vBPaehN0pF3+M7euHGj705OREREREREREREREQ+qUcvr/fyyy/3GjxnzpxeOxf5tsGp0t2/bP+zeEP7y++9u8bfuyIiIiIiIiIiIiIiokCqR0OnSy+9VIbh/bv7GIbB0ClIO3lc+5/qBt8Z5557ru9OTkREREREREREREREPqlHQ6fBgwf3ytCJgqvqBunb7VJdszR5iHRIhpQU6ztvyZIl+sUvfuE7gIiIiIiIiIiIiIiIer0eDZ3y8/N9tA0KhB77RFq4vP39nK6eIV02TXppuXT9S1KD88d1V50kPXW57/ZRW1vru5MTEREREREREREREZFP6tHQiUK311dKf/inFBEmRUVIVzwnudzS1X+XDsuWZoyRWl3Sxz9Iz34uTRrSPnzyRenp6b45MRERERERERERERER+SyGTiRJemqJNDJL+vqu9pfOu/xZ6cZ/SNNGS5/dJoVZ2tc1t0hH3im9sMx3Q6cjjzzSNycmIiIiIiIiIiIiIiKfZfH3BigwyiuRLjleSo6TDEO66TSpqUW69PgfB06SFGWVLjpW2lTku7188MEHvjs5ERERERERERERERH5pF6902nPnj1asWKF9uzZo4aGBpmmuc+1d955Z2/S5GVltVJ20o8fZyW2/3VAkufa7CSp0en5OBERERERERERERER9d96ZehUUVGhq6++WosWLdrvoEmSTNOUYRgMnQIsU13vaOr4e8PwXLu3x3qzyZMn+xYgIiIiIiIiIiIiIqJez+uhU0NDg6ZPn67NmzfLarVqwoQJ+u6772S1WnXkkUfKbrdr+/btkqTk5GSNGzfO602Tb9rrgKmP91BZX6mPtnyk9XXr+1gm+jG36Zbb7ZZb//mr6ZZpmjJlyu12y5TZ/vF/HpP049+b+vGx/3zc+fc/qbsBfU/6+bmJiIiIiIiIKHQzevE3dsbPfiHYeW7jx7832j+QIaNzvSFDhsWQ5T/v3mKxWGTI6PJXiywKs4R5GB1FhkfqjPFnyGLhHWCIQimvh05PPvmkNm3apFGjRmnJkiXKysqSxWJRcnKyli9fLkkqKCjQrbfeqrfeekunnXaabr31Vq83Tr3fba9J899r/3uXu33gdMXzUmxk13W1jb7bQ3FNsR5f9rjCYsJ8h1D/y5Tccv84KPrPgMg0zS4DpZ/+VVLn85JkMSw/Od2PQx6X2yXTNBVmCftxStvxtKHOoZXFsHQ5x09rc7fJkNF+jr3kcrtkylS4Ze/fst0mBgYGBgYGBgYGBgYGBgYGRi8Ye/tdRuccqv1vTNOUy3R1Gl2GYP/5W7fbLcMwZDEsMgyjc2D104/3VO/RYxc8pnMnn6toa7SiI6IVFRG1zyEVEQVHXg+d3nnnHRmGofnz5ysrK2uva3JycvTaa6/pN7/5jW6//XYdfvjhmjFjhrc09WKDU9rvdKpr/sljqZLb7PqYJFks7c/5onEDx+mGUTeodUirkmL28oZSRPvJbbrlbHWqqbVJTa1NamxpVH1zvZytTrW4WtTmbuscEtU761XXXKf4yHglxCR0Xkh1XPy0udpUXl8ua5hV6fHpCg9r/3bZcWFkmqbK6srU4mpRpi1TURFRnc93VNNYo+rGaiXFJCkxJtFjv85Wp+wOu6xhVmXYMjz+yx63261SR2mnERkR6XEODAwMDAwMDAwMDAwMDAwMDF8aP321lubW5nbDYlW6LV2GYXT5D3jbXG3tvy9pa1FqXKoiwiM6B2UdryxT21Sreme9SmpL9MXWL+Ryu2QNt8oablV0RLQSoxOVGp8qW7RNtiibEqITFBsZyzCKKEjyeui0ZcsWSdJpp53W5fHW1laPtQ888IBee+01Pf744wydAqz8x/y9gx8bMmmIihuKlWHL8PdWKMBzuV2dw6OaxhpV1leqoaVB5XXlamxpVEpsimIjYxVtjZYtzKaIsAiFh4WrprFGhmFoaOpQJccme5y3ubVZe6r3KCshS9mJ2Xu9SCuqKVJkRKQOST+kc+D006oaqtTialFOSs4+jYr6CiXHJu/XiAiP0NC0oRgYGBgYGBgYGBgYGBgYGBh+NyobKpUal7pfIy4qTgOTBu7TMGVqWNowfZf/ndLj0zUkdYicbU61tLWosqFSX+/4WuFGuAYmD5Q1zKrYyFglRCcoOylbaXFpSo1PVVJMEkMoogDN66FTc3OzkpKSFBn542Q8KipK9fX1HmuHDh2qhIQEfffdd96yFMLZt9mlAf7eBQVqra7W9gFTQ6VKa0tV76xXS1uLLIZF1nCrahpqFGYJ05gBY/Z5cVNRX6HUuNT9DpwiwyP3ewHlbHPu9wIKAwMDAwMDAwMDAwMDAwMDA2PfhiFDbtOtMEuYYqwxcptuVTVUKSclRyMzRirMEqY2V5saWhpU3VitwqpC2R12xUfFa2TGSA1PH64BiQOUYcvY50sJElHf5/XQKSMjQ5WVlV0eS0tL0549e7Rnzx4NHDiw83GXy6WGhgY1NvrwTYGoV9pdKW3Y3f7+TQkx0rhB0qCUvrGb65tllbVvMAqK3KZbtY21sjvs2lW+S61trTINU1ERUbJF2xQZHtl5cSNDGpw0OGAuoDAwMDAwMDAwMDAwMDAwMDAwPA3DMDpflq/eWa88e56irdGdAydJCg8LV0J0guIi41TTVKNYa6wGJA5QTWONluUtkzXMqrT4NI3IGKGclBylxvvoPUGI6ICzdL9k/w0ePFiNjY0qKyvrfGzixImS2t/v6ae99957amtrU3p6urds0PbUU09p/Pjxstlsstlsmjp1qj7++OP9HvPmm29q1KhRioqK0rhx4/TRRx/5bH+f50pH3iENuVE68y/Sb/+3/a9Dbmx//PNcn9GdRcZ6vp4s9c/aXG0qrinWtzu+1YfrP9SXW7+UxbAozZamAYkDlByb3GXgFIgXUBgYGBgYGBgYGBgYGBgYGBgYezfcpnufA6eOXG6X8krz1NTSpFFZo5Rpy1R2UrZGZo7UgMQBcjQ79Nbat/TE50/oo/UfKb8iXy63y2MfRNQ3eX2n09SpU/X111/ryy+/1C9/+UtJ0vnnn6/3339f8+bNU3NzsyZOnKgffvhB999/vwzD0Omnn+71xoO1gQMH6qGHHtKhhx4q0zT10ksv6eyzz9b333+vMWPGeKxfsWKFLrzwQs2fP19nnHGGXn31Vc2ePVtr167V2LFje3VvzyyRrl0omaY09VBpylDJFi05mqS1+dKKrdIpD0n/e5l01Um9Sncpa2SWiuuLfQdQwNfmalNJbYnyK/Nlr7WrrK5MyTHJGj9wfFBeQGFgYGBgYGBgYGBgYGBgYGBgdDUMGXK2OQ944DQyc6TiIuO6PB8Z0f4fr4dbwhUfHa88e562lm1VTnKOxg8cryGpQ3jpPaI+zjA77mE8yL799ltNnTpVZ511lhYtWiRJMk1TM2bM0LJly7q8oZtpmsrMzNTq1as1YABv2tNRcnKyHn74Yf3ud7/zeO78889XQ0ODPvjgg87Hjj76aE2cOFFPP/30AZ3f4XAoISFB5557rhzbFmnxbW6PNT8USIffIY3Jll69VjpsoOd5Nu2RfvtU+8vurblfGj+4e3vcvAidNPsaPfbYYwe0V0l69JlHZR1h1aDkQQd8DIVGbtMte237S+iV1bXfPVnvrFeMNSZoL6AwMDAwMDAwMDAwMDAwMDAwMDyNRz57RMcNP04zDptxUAMnSSquKdae6j0amDRQAxIHdO69pLZELrdLQ1OGasqQKRqYNLDL76mJ6Me5QW1trWw2W6+d19L9kv131FFHye12dw6cpPbX4/zwww81b948DR06VOHh4UpJSdFvf/tbffPNNwyc/pPL5dJrr72mhoYGTZ06da9rVq5cqZkzZ3Z57NRTT9XKlSv3eV6n0ymHw9HlT3c98pGUEid9/qe9D5yk9sf/Pa993V999wp/1E+rbarV94Xfa1X+KlU3VishJkFNrU0MnDAwMDAwMDAwMDAwMDAwMDBC0DBNUxFhEb06cJKkqIgoDU0dqsSYRH288WO98OUL+iLvCzmauv8dKRF5n9cvr7evoqOj9cADD+iBBx7wFRG0bdiwQVOnTlVzc7Pi4uL0zjvv6LDDDtvrWrvdroyMjC6PZWRkyG637/P88+fP1z333NOjPX2xWbrsBCnZ8/t3l5LjpEtPkP61oken71Epg1JUpzrfARRQudwuFVQWaFvpNjW1NiklLkWmaYbMBRQGBgYGBgYGBgYGBgYGBgYGhqchQ0qNS+3VgVNH9c56FVQWaFj6MGUlZGlV/ioVVBXoqKFHaUTGCI/Pi4h6L766/NDIkSO1bt06ffvtt7rmmmt0ySWXaNOmTb12/nnz5qm2trbzz+7du7s9ptQhjcg6sPOPzGpf76ssYfxr2V+qb67X2oK1+mHPD7JYLBqQOICBEwYGBgYGBgYGBgYGBgYGBkY/MMIt4dLPXvGutwZOP32fqKSYJI3IHKGmliZ9vPFjLctbpgZng8dxRNQ7+eROJ5fLpaqqKknt71cUFsabtf00q9Wq4cOHS5KmTJmiVatW6bHHHtMzzzzjsTYzM1OlpaVdHistLVVmZuY+zx8ZGanIyMge7SkuUqqqP7C1VfXt631VeX65rCOsvgMoILLX2pVbnCtHs0Pp8emKCIsIyQsoDAwMDAwMDAwMDAwMDAwMDAxPwzAMuc0f33veFwOnjruoLIZF2UnZcjQ59M737yi/Ml+njz1dWYkH+F/hE9EBZ+l+yYHV0NCgRx55REcccYRiYmKUmZmpzMxMxcTE6IgjjtAjjzyi+voDnGr0s9xut5xO516fmzp1qpYsWdLlscWLF+/zPaAOtgk50v+tOrC1b6+Sxg/uVZ76UW7Tre1l27WmYI2aW5s1IGEAAycMDAwMDAwMDAwMDAwMDAyMfmiYpinJtwOnjlxul/bU7FFSTJLstXZ9sP4DbSnZ0rkHIuqdemXotG7dOo0dO1a33HKL1qxZo9bWVpmmKdM01draqjVr1uiWW27RuHHj9P333/cGGbTNmzdPy5cvV35+vjZs2KB58+Zp2bJluuiiiyRJc+bM0bx58zrX33jjjfrkk0/0yCOPaMuWLbr77ru1evVqXXfddb26rznHSd9sl+58a//r7v6/9nWXHN+rfJcGT2CiFaq1ulqVW5SrDUUbFBURpbT4NBmGEfIXUBgYGBgYGBgYGBgYGBgYGBgYXQ1DhkzT7LOBU4cxesBojRs4TqZMfZb7mVbtWiW32+1xTiI6uLx+eb2SkhLNnDlTVVVVslqt+tWvfqWTTjpJ2dnZkqSioiItXbpUb731lgoKCnTyySdr/fr1GjDA85tDf6isrExz5sxRSUmJEhISNH78eH366ac6+eSTJUmFhYVdvrkfc8wxevXVV/XnP/9Zt99+uw499FAtWrRIY8eO7dV9zTleeuVr6YFF0pKN0hUnSpNypIQYqbZRWpsvvbCsfeB00pj29b6qfFe5lOm785N/crY5lVuUq/zKfKXGpXZepIT6BRQGBgYGBgYGBgYGBgYGBgYGhqdhGIbcbnefDpx+amQlZKm6oVpfbvtSTS1NOmb4MYoIj/A4PxH1LK+HTvfee6+qqqqUk5Ojjz/+WKNGjfJYc/nll+vPf/6zTjvtNBUWFuq+++7TU0895S0dlL3wwgv7fX7ZsmUej5133nk677zzfLSj9gxDWjRX+v0L0isr2odLP8+UdOFU6Znfta/3VU2OJlkzeU+nUKq5tVnr96zXnuo9So9PlzXc2vl4qF9AYWBgYGBgYGBgYGBgYGBgYGB4GoYMldeX+2Xg1FFSbJIq6iv0+urX1epq1bSR0xg8EXmZ10Onjz76SIZh6LnnntvrwKmjkSNH6rnnntMpp5yiDz/80FuWfFBMpPSP/yfdckb7+ztt3C05miRbtDR2kHTuEX3zXk7WaAZOodRPB06ZtkyFh4V3Pu7vixsMDAwMDAwMDAwMDAwMDAwMDP8Ybe42tbha/DZw6jCqG6s1ImOEvt/9vQyLoRMOPYHBE5EXeT10Ki0tVXR0tGbOnNnt2pkzZyomJkbl5eXesuTDxg1u/7OvXlspvfyl9NEtvvEHjhmoPY49vjk59WnONqc27Nmg3VW7lZWQxcAJAwMDAwMDAwMDAwMDAwMDA0NFNUWSpOSYZL8OnH5qNLY0am3BWllk0fEjju/8PRYR9SxL90v2X1pamsLCwrpf2AFaLEpLS/OWJT+2o1T6dL3vzr9z9U7fnZz6rFZXq3KLchk4YWBgYGBgYGBgYGBgYGBgYGB4GBFhEYoI87yjyB8DJ0mKscZoUPIgrSlco5U7V8rtdnscR0Td5/XQacaMGaqvr9eaNWu6Xbt69WrV19drxowZ3rJEFMC53C7l2fOUX5mvdFs6AycMDAwMDAwMDAwMDAwMDAwMjC6GYRgyZXZZ46+BU0exkbGyRdv09pq3tbZwrUzT9DieiPaf10OnP//5z4qNjdWVV16pysrKfa6rqqrSVVddJZvNpj/96U/eshTCJWUn+XsL5GU7y3dqW9k2pcSlyBre/h5dgXhxg4GBgYGBgYGBgYGBgYGBgYHhH8OQ0WWo4++BU4dRXFOsmMgYfb39a20t3eqxhoj2X49emLKwsNDjMavVqueff16///3vNXr0aF1zzTU68cQTlZ2dLUkqKirS0qVL9fTTT6u1tVXPPfecrFZr7+yeQrKIKN6oL5grqi7SFvsW2aJsio6IlhS4FzcYGBgYGBgYGBgYGBgYGBgYGP4xDMOQW+0vYRcoA6cOY2LGRJU6SrV863LFRcYpOynbYz0R7b0eDZ2GDh3a7Zr7779f999//z6fv+CCC2QYhtra2npCUz+qbEeZrCMYTAZj1Y3Vyi3OVbglXPFR8ZIC++IGAwMDAwMDAwMDAwMDAwMDA8M/hiFDptsMuIFThzEgcYB2lu/U8q3LNWv8LNmibR7HEZFnPRo69dZrWPJamIHX26sOfG3uHt/tg4I3Z6tTm4o2qam1SZm2TEmBf3GDgYGBgYGBgYGBgYGBgYGBgeEfwzAM1TXXBeTAqaOclBxttW/Vsi3LdPq40xURzis0EXVXj4ZOu3bt8tU+yM/96jHJOMC1pg587cE0aNwglTpLfShQb+c23dpi36LSulJlJWTJMIyguLjBwMDAwMDAwMDAwMDAwMDAwPCP4XK7VOesC9iBU0dOl1OLNy9WZkKmjhx2pMfzRNS1Hg2dcnJyfLUP8nMvXuXvHfxY1Z4qKc3fu6CetKdqj3ZV7lJqXKrCLGFBc3GDgYGBgYGBgYGBgYGBgYGBgeEfw+V2KdYaG7ADpw7D5XbpsAGHaVXBKmUkZCgnhd+RE+2vHg2dKHS75AR/7+DHGqobZE3jPZ2CpdqmWm22b1Z0RLSiIqKC6uIGAwMDAwMDAwMDAwMDAwMDA8M/RnhYuGIjYz2eD6SB00+N/Ip8fb39a6XEpiguytMkovYs3S/peQUFBVq1apVWrVqlgoICXxAUwoVHMgsNllpdrdpSskWNLY1KikkKuosbDAwMDAwMDAwMDAwMDAwMDAz/GBFhEXKb7i7PB+rASZIGJQ9SUU2Rvt31rdxut8d5iai9XvvtfklJiebPn6/XXntNlZWVXZ5LSUnRb37zG916663KysrqLZJ6uZJqyTCkzMT2j5tbpP/9t+e6QSnSeUf5bh85E3O0u3q37wDqtQoqC1RUU6RMW2ZQXtxgYGBgYGBgYGBgYGBgYGBgYPjHMGTIlNn5fCAPnCQpzBKmgUkD9fW2rzUwaaBGZo70OD8R9dLQ6euvv9bs2bNVVVUl0zQ9nq+oqNDjjz+uV199VYsWLdIxxxzTGyz1YnnF0tjbpPvPk249s/2xBqf0X69KhqSf/lMNt0gTc6RDM32zlx3f7pB1BC+vF+hVN1Rre9l22aJtanO3BeXFDQYGBgYGBgYGBgYGBgYGBgaGnwxDnb9LDvSBU0eOJofsDru+yPtCWQlZskXbPNYQ9fe8HjqVlZXprLPOUnV1tWw2m66++mqdfPLJGjhwoCRpz549+ve//61nnnlGFRUVOuuss7Rp0yalp6d7vXnqvV5cLiXHSn843fO5v1wkTR7S/vdut/Srv0l/XybNv6Avd0iBVJurTVtKt8jZ5lSMNSZ4L24wMDAwMDAwMDAwMDAwMDAwMPxiGDJkmmbQDJw6jHEDx6m2qVar8lfppFEnyTAMj7VE/Tmvh06PPPKIqqurNWrUKC1evFjZ2dldnh85cqRmzJih66+/XjNnzlReXp7++te/6qGHHvKWpl7s81zprMmSdS//RkwYLE0b/ePH5x8tLcn13V4SMxPVqEbfAeR1u6t3y15jV0J0QlBf3GBgYGBgYGBgYGBgYGBgYGBg+McwDEMtbS3aUrJFMZExQTFw6jDqnfXaWLRRQ1KG6JD0QzzWE/XnLN0v2X8ffvihDMPQc8895zFw+mkDBgzQc889J9M09cEHH3jLHlRLly7Vn//8Z5144ok69NBDlZSUpOzsbE2cOFEXX3yxnn/+edntdr/szd9ts7e/ZN6BNCpL2l7qu71ExXv+gKLAqa65TttKtyk8LFx2hz2oL24wMDAwMDAwMDAwMDAwMDAwMPxjmKapmsYaxViDa+AkSXGRcQq3hOu7Xd+p0cl/PE/007y+0yk/P1+xsbE69thju1177LHHKjY2VgUFBd6yB1xDQ4P+9re/6bnnnlNBQUHn64RGRUUpOTlZTU1N2rhxo9avX69XXnlFEREROvPMM/WHP/zhgD6nUKnBKcX97GdDUqy04SFpaFrXx23R7et9lX2bnfd0CtDcplvby7arqqFKLrdLURFRQX1xg4GBgYGBgYGBgYGBgYGBgYHhH6OlrUXhYeE6NP3QoBo4dZSdlK31e9brh90/aOrwqR7HEvXXLN0v6f06Bj++7umnn9bw4cP1pz/9STabTffdd5+WLFmi2tpaNTY2as+ePaqsrFRra6u2bNmil156Sb/+9a/12Wef6YQTTtC5556rXbt29cle/V1irFRS0/Uxi0UaM1CKiez6uL1WSojps61RAFXmKNOuil1qbm1m4ISBgYGBgYGBgYGBgYGBgYGBcdCGxWJRfFR8UA6cJKmptUm1TbX6YusXKnX48GWhiIIsr4dOQ4YMUUNDg7755ptu165cuVINDQ0aMmSIt+wBdf311+vkk0/W+vXrtW7dOt1+++068cQTFR8f32WdYRgaMWKELr74Yv3jH/9QaWmpnnnmGf3www/6xz/+0Sd79XfjBkmfbTiwtZ9taF/vq7IP2/fLNJL/anW1Ks+eJ3utXfFR8SFxcYOBgYGBgYGBgYGBgYGBgYGB4R8jKjxKhgyZ+vEGhWAZOHUY6bZ0hYWFaW3BWrndbo91RP0xr4dOp59+ukzT1FVXXaXy8vJ9risrK9NVV10lwzD0i1/8wlv2gMrNzdXLL7+ssWPH9ui46OhoXXHFFdq6dasuvvjiXt3T/PnzdcQRRyg+Pl7p6emaPXu28vLy9nvMwoULZRhGlz9RUZ7f1L3pl0dIX2yW3luz/3WLVrev+9WRvcp3qba01ncnp4Nud9VubSrZpLS4tJC5uMHAwMDAwMDAwMDAwMDAwMDA8I9hGME9cOowBicPVl5pnnZW7PRYS9Qf83ro9F//9V9KTExUbm6uRo8erTvuuEPLli3Ttm3btG3bNi1dulR//vOfNWbMGOXm5iohIUF//OMfe2Pv3TZixAivjg8LC9PQoUN7aTftffHFF7r22mv1zTffaPHixWptbdUpp5yihoaG/R5ns9lUUlLS+ae33xfrd9OlkVnSrx+X7nxLKvjZ/LCgXLrjTemCJ6TR2dLl03qV71J9Zb3vTk4HVYOzQT/s/kExETEalDwoZC5uMDAwMDAwMDAwMDAwMDAwMDD8Y/x06BSsA6cwS5hirDEKt4RrbcFaOVudHscQ9bfCvT1BRkaG3nnnHZ1zzjmqqqrSgw8+qAcffNBjnWmaSkxM1KJFi5SRkeEt26uZpqnt27crKipKgwb58HXjJH3yySddPl64cKHS09O1Zs0anXDCCfs8zjAMZWZmHpDhdDrldP74Dc7hcEiSLBaL2lzGXo+JjJA+uFma9bB0/yLpgUWSLbr9j6Op/Y8paVSW9MF/ta8/kNrc7cO7nhQW3rP15Pt2lu9UeV25xmWPC6mLGwwMDAwMDAwMDAwMDAwMDAwM/xiGDJmmKZfbpe3l24Ny4NTRgMQB2l66XXmleRo/cLzHsUT9KUv3S7pv2rRpWr9+vX7/+98rKSlJpml2+ZOUlKRrrrlGGzZs2O9gxde9/fbbmjNnjqqrqzsfy8/P1/jx4zVq1CgNGTJEF1xwgVwuV5/tqba2/aXkkpM9vyH/tPr6euXk5GjQoEE6++yzlZubu8+18+fPV0JCQuefjkFaYmKiSh37/kc+LF36/gHpsYul40ZKYRappKb9r8ePkv42R1r7gDQk7cA+N7dbKqtt/+ffk4Ye3rt3l5F3VTdWa3PJZg1LG+YxQAz2ixsMDAwMDAwMDAwMDAwMDAwMDP8YhmHI7XYrzx6cdzj9NIthUUVDhT7f8rnqm3kVJ+rfGaZpmt0v61m7du1SWVmZJCk9Pb3XX6LuYDv55JNVWlqq9evXdz52zjnn6N1339VJJ52kyspKrV+/Xk8//bSuvPJKn+/H7XbrrLPOUk1Njb766qt9rlu5cqW2bdum8ePHq7a2Vn/5y1+0fPly5ebmauDAgR7r93an06BBg7Rw4UJdeuml2v5X6ZA+uNns6zzpuHulL7/8Uscdd9wBH/fYs48p4tAIDUr27V1n1H2maWpNwRoVVhZqYHLXf9dC4eIGAwMDAwMDAwMDAwMDAwMDA8M/xqvfvqq4qDidNf4sHZZ9WNAOnDqMRmejwi3hOmXMKTpq2FEe5yEKtBwOhxISElRbWyubzdZr5/X65fX21tChQwNm0PTTNm3apNNPP73z47q6On344Yc6//zz9a9//Uutra2aNGmS/v73v/fJ0Onaa6/Vxo0b9ztwkqSpU6dq6tSpnR8fc8wxGj16tJ555hndd999HusjIyMVGRnp8fgpp5yi6OhI3feOU3+/SvrZ9/lerbVNmv++oQFZaTrmmGN6dKwP5qB0kFXUV6i4plip8aldHg+VixsMDAwMDAwMDAwMDAwMDAwMDP8YLW0tanO3aUTmiKAfODW1NGlU1ig5W51av2e9Ds04dK//fyHqD/lw7BB4VVVVdXlfpK+++kptbW268MILJUkRERE6+eSTtWPHDp/v5brrrtMHH3ygpUuX7vVupf0VERGhSZMmafv27T06LjY2Vk8++ZRe/srQBU9IyzdLLnePTtFtrW3SZ+ulMx8x9NnGMD319HMeP1S6y5bee1NVOvjcplu7ynfJlNnl4iKULm4wMDAwMDAwMDAwMDAwMDAwMPxjuE23bFabYiNjPc4RbAOnDiM5Nlk1jTXaWLTR43xE/SWf3OkUqNlsNlVWVnZ+vHTpUlksFh1//PGdj0VERKihocFnezBNU9dff73eeecdLVu27KDuCHO5XNqwYYN+8Ytf9PjYyy67TOHh4bp93i168367oiMtSowNk8Xo8ak89+WWqutdcra6dciwwXr77Sd0xhln9Pg8sUmxajabvd8QeVVZXZnsDrtSYlM6Hwu1ixsMDAwMDAwMDAwMDAwMDAwMDP8Y0dbo9vcP/9mLHgXrwElqf5+qjIQMbSrepNFZo5UWn+ZxbqJQr0dDp7CwsO4XHUCGYaitra1XztWTRo0apffff1/333+/wsLC9Oqrr2rKlClKSkrqXFNQUKCMDN+94dG1116rV199Ve+++67i4+Nlt9slSQkJCYqOjpYkzZkzR9nZ2Zo/f74k6d5779XRRx+t4cOHq6amRg8//LAKCgp0xRVXHNQeLr74Yl100UX69ttv9e2336q2trZXXtLOYrEoMTFRxx9/vCZOnCjDOLhJVkleiawjrF7vhw4+l9ulXRW7ZMiQNbz9n0UoXtxgYGBgYGBgYGBgYGBgYGBgYPjHWFu41uN3ksE8cOooKSZJm4s3a13hOp085mSP8xOFej0aOgX7e+3ccMMNOu+88zRw4MDOO5ruv//+Lmu++eYbTZ482Wd7eOqppyRJ06dP7/L4iy++qEsvvVSSVFhY2OWbcnV1ta688krZ7XYlJSVpypQpWrFihQ477LCD3ofFYvF4ryiijsrqylRWW9b5Xk6henGDgYGBgYGBgYGBgYGBgYGBgeEfw2JYZP7n/6TQGDh1GM42p9btXqfxg8Yrw+a7GxyIArEeDZ2WLl3qq330Sb/85S/15JNP6oUXXpAkXXDBBZ2DHkn64osv5HA4dNppp/lsDwcyuFu2bFmXjxcsWKAFCxb4aEeB14BRA1ThrvD3NvptLrdL+RX5slgsigiLCOmLGwwMDAwMDAwMDAwMDAwMDAwM/xmhOHDaU71HIzNHqt5Zr41FGxk6Ub+rR0OnadOm+WoffdY111yja665Zq/PTZs2TdXV1X28I/p59ZX1UlL368g3ldeVq8zRfpeTvy88MDAwMDAwMDAwMDAwMDAwMDBC0zAMQzLbBzVldWUhM3DqMBxNDm21b9XY7LEMnqhfZel+Sf/qT3/6ky644AJ/b6Nf5yh3+HsL/TaX26VdlbtksVgUZoSF/MUNBgYGBgYGBgYGBgYGBgYGBoZ/DEOGGlsaVVRTFHIDJ0myRdvU0NKgTUWbPNYThXI9utMp2Lv33nv3+3xbW5tee+012e32PtoR7S3DYvh7C/228vpylTvKlRST5PcLDwwMDAwMDAwMDAwMDAwMDAyM0DVaXa1qdbUqOzE75AZOHWUmZGpD0QaNHThWafFpHscShWJeD50KCwt7tD4qKkqJiYmyWq3e0j3u7rvv3u/zhtE+7Ljwwgv7YDe0rw458hAVVvXs3yvyPrfpVmFloUy3qbK6Mr9feGBgYGBgYGBgYGBgYGBgYGBghK7hbHMqISpBmQmZHs+HwsBJksIsYdpYvFG5RbmaPmq6x/NEoZjXQ6ehQ4ce1HHDhg3T6aefrhtuuEHDhw/3dhsH1NKlS/f6eHNzs7Zu3aonn3xSbW1teuSRR/pkP7T3dq7eqfBh/eomvICosr5SJbUlamprktt0+/3CAwMDAwMDAwMDAwMDAwMDAwMjdI2o8ChFRkR6PB8qA6cOI92Wri32LRo/aPxe/39KFGoZpmma3pzg599YeoQbhqKiorRw4UKdd9553myjV3I4HBo3bpyOOuoovfHGG/7eTq/lcDiUkJCg2tpa2Ww2f2+n2x595lFZR1g1KHmQv7fSbzJNU98Xfq/V+asVFxUXEBceGBgYGBgYGBgYGBgYGBgYGBiha3y57UtZw6y69fRbO9eG2sAp2hqtEekjtL1su44/9HgdfcjRHmuJ/JWv5gYHPzH6T7t27dIbb7yhpKQkpaam6o477tCSJUu0ZcsWbdmyRUuWLNEdd9yhtLQ0JScn66233tLatWv1zDPPaPTo0WpqatKcOXO0ffv23vh8vMpms2nWrFn65JNP/L2Vfl18Sry/t9Dvqm6sVp49T9Zwa8BceGBgYGBgYGBgYGBgYGBgYGBghK5hyJApUx33RITiwGlkxkiFh4UrNT5VucW5cjQ5PNYThVpeD53a2tp05ZVXKicnR5s3b9Y999yjE088USNGjNCIESN04okn6p577tGmTZs0ePBgXXXVVUpKStKVV16pNWvW6Oijj1ZLS4see+yx3vh8vC4iIkIRERH+3ka/zpYe+HdjhVqFlYWqaqjSsLRhAXPhgYGBgYGBgYGBgYGBgYGBgYERuoZhtA+dpNAdOHUYybHJqmqo0vYy/994QeTrvB46PfDAA3I4HHruueeUkpKyz3UpKSl69tlnVVVVpQceeECSFBkZqYceekimaerzzz/3dite53Q69dFHH2n27Nn+3kq/rmhzkb+30K9yNDmUV5rHwAkDAwMDAwMDAwMDAwMDAwMDo88MwzAkU2pwNoT0wEmSLIZF8VHxWrljpZpamjyOJQqlwr09weLFixUXF6cpU6Z0u/bwww9XXFycPv30087Hjj32WFmtVu3evdvbrXTbyy+/vNfHTdNUcXGxXnnlFdXW1mrKlCkea+fMmePz/RH5o6KaIpmmqZQ4z6FxqF/cYGBgYGBgYGBgYGBgYGBgYGD4xzBkqM3VprzSPCXFJoXswKnDqGqsUklNiXaW79SY7DEe5yAKlbweOpWXlys8/MBP43a7VVZW1vlxWFiY4uLi1NjY6O1Wuu3SSy9tn6D/rI7XDe3o+uuv7/KcYRgMnfqwzEMzVaUqf2+jX9TU0qSCygIGThgYGBgYGBgYGBgYGBgYGBgYfWq4TJfqW+oVExET8gOnvNI8tbS1KCc1R7nFuRqRMUIR4bzFC4VmXg+d0tLSVFxcrOXLl+uEE07Y79rly5ersbFR2dnZnY+1traqurq6y2O+6u9///teh04UWDXVNUm8rVOfZHfY1djSqOzErl9/gXDhgYGBgYGBgYGBgYGBgYGBgYERukajs1EWw6Lh6cNDfuDUYVjDrCqsKtTu6t0aljbM43xEoZDXQ6dTTjlFL774oq644gp9+umnGjp06F7X7dq1S1dccYUMw9Cpp57a+fjWrVtlmqZycnK83cpe+9///V+dffbZys7O1qWXXuoTg3q3WnutrDarv7cR8rW6WlVQWaAYa0yXYWygXHhgYGBgYGBgYGBgYGBgYGBgYISuEWYJU0R4hIcRqgOnDsNiWLSpeJOGpAzx+NyJQiGvh0533XWX3nrrLe3YsUNjx47V+eefr2nTpmnAgAEyDEPFxcVatmyZXn/9dTU1NSk+Pl533HFH5/GvvfaaJGn69OnebmWvXXfddbr++us1efJkzZ49W2eddZbGjRvnE4somCqvK1d1Y7UybZmdjwXShQcGBgYGBgYGBgYGBgYGBgYGRugacVFxcrY5ZerHtz4J9YGTJGUmZCq/Ml92h32v5yYK9rweOg0ePFiffPKJfvnLX8put+ull17SSy+95LHONE1lZGTorbfe6nJX09ixY7VgwQKdccYZ3m5lr61atUqLFi3Se++9pzvuuEN33nmnhgwZ0jmAOv7445koB1jDjx6uwqpCf28jpHObbhVWFSrcEt75QzHQLjwwMDAwMDAwMDAwMDAwMDAwMELX2FSyST+ZN/l9GNRXRow1RsXVxdpcvJmhE4VkhmmaZvfLuq+2tlaPP/643nrrLeXm5srlckmSwsLCNGbMGP3qV7/Sddddp8TExN7gDqr8/Hy98847eu+99/TVV1/J7XYrOTlZZ5xxhs4++2ydcsopiomJ8dv+fJXD4VBCQoJqa2tlswX+myU98eITsgyxaFDyIH9vJWSrrK/Uih0rlBiTqMjwyIC88MDAwMDAwMDAwMDAwMDAwMDACF3js02fqaG5QX+a9SdFW6P9PgzqS2NLyRblpObo0mMuVWJMoscaor7IV3ODXhs6/bTW1lZVVVVJkpKTkxUREdHbhNdVVVXpgw8+0KJFi7R48WI1NDQoKipKM2bM0DnnnKMzzjhD6enp/t5mrxRsQ6dHn3lU1hFWhk4+bP3u9dpZsVMDEgcE7IUHBgYGBgYGBgYGBgYGBgYGBkboGos3LVZdc53mnT5PRbVFATEM6isjOzFbdc46zRg1Q5NzJnusI+qLfDU3sHS/pOdFREQoIyNDGRkZBzxw+uUvf6kZM2b4Yjt7LTk5WXPmzNHbb7+tiooKvffee7rooou0evVqXXHFFRowYICOO+64PtsP/Vhscqy/txDS1TfXq7i2WLZoW0BfeGBgYGBgYGBgYGBgYGBgYGBghK5hGIZM09S2sm0BMwzqKyM7KVuJ0YnaVLxJzlanx1qiYM7r93TqrVasWKGysjK/2JGRkTrjjDN0xhlnyDRNffPNN53vA0V9X9KAJJW1+Offhf6Q3WFXY0ujTNNUZUNlwF54YGBgYGBgYGBgYGBgYGBgYGCEriFTamxpVFNrkyYNnhQQw6C+NFLjUrWzYqcKKgs0InOExzFEwZql+yX9K8MwNHXqVP33f/+3Nm/e3Ovnnz9/vo444gjFx8crPT1ds2fPVl5eXrfHvfnmmxo1apSioqI0btw4ffTRR72+t0Bpz8Y9/t5CyNbS1qLdVbvV6mpl4ISBgYGBgYGBgYGBgYGBgYGB4Tej3lkvl+nS8LThATUM6isjPCxcEWER2li8UW632+M4omCNoVMf98UXX+jaa6/VN998o8WLF6u1tVWnnHKKGhoa9nnMihUrdOGFF+p3v/udvv/+e82ePVuzZ8/Wxo0b+3DnFAqV15WruKZYzlZnwF94YGBgYGBgYGBgYGBgYGBgYGCEruFyuxQdEa3YSM+32gj1gVNHcZFxWp2/WnaH3eM5omAtYF5ezxddfvnlB3WcYRh64YUXenk37X3yySddPl64cKHS09O1Zs0anXDCCXs95rHHHtNpp52mm2++WZJ03333afHixXriiSf09NNP+2Sf/izjkAxVq9rf2wi53KZbBZUFqm6s1vD04QF/4YGBgYGBgYGBgYGBgYGBgYGBEbpGQnSCqhqrPM4RCMOgvjIKqwpV31yvbaXb9rqGKBgL6aHTwoUL9/p4x5vU7etxXw6dfl5tba0kKTnZ85t0RytXrtTcuXO7PHbqqadq0aJFe13vdDrldP74BnQOh8P7jfZhLU0tkuf3afKy6oZqbS/brsHJg4PiwgMDAwMDAwMDAwMDAwMDAwMDI3SNouoij9/RBsowqC+NiYMnalvpNk0aPEm2aJvHWqJgy9L9kuBt165dXf7s2LFDZ5xxhlJSUnTfffdp2bJl2rx5s5YtW6Z7771XKSkpOvPMM7Vt27Y+2Z/b7dZNN92kY489VmPHjt3nOrvdroyMjC6PZWRkyG7f+22X8+fPV0JCQuefQYMG9eq+fV11MXc5+aKimiKZMpWZkOnxXCBeeGBgYGBgYGBgYGBgYGBgYGBghLBhtD9nqn3wFGjDoL4yUuJSVN1YrfzKfI+1RMFYSN/plJOT0+Xjhx56SN9++61++OEHZWVldT4+cuRInXDCCbrssss0adIkvfXWW7rlllt8vr9rr71WGzdu1FdffdWr5503b16XO6McDkfQDZ6od2tsaVRBZYGyk7I9ngvYCw8MDAwMDAwMDAwMDAwMDAwMjJA1DP34alSBOAzqSyM+Kl65RbkanTlaEeERHscRBVOW7peETi+88IJ+/etfdxk4/bTs7Gz9+te/1nPPPefzvVx33XX64IMPtHTpUg0cOHC/azMzM1VaWtrlsdLSUmVmet6xIkmRkZGy2Wxd/gRTw44Y5u8thFzldeVqc7UpPjK+y+OBfOGBgYGBgYGBgYGBgYGBgYGBgRG6hsWwyJSpkpqSgB0G9ZWRFp+mktr2/z8QBXv9aui0Z88eRUV5fvP8aVFRUdqzx3df3KZp6rrrrtM777yjzz//XEOHDu32mKlTp2rJkiVdHlu8eLGmTp3qq236td0bdvt7CyGVy+1SYVWhIiMiZRhG5+OBfuGBgYGBgYGBgYGBgYGBgYGBgRG6hmEYanG1qKS2JGCHQX1lhFnCVFBZoE3FmzyOJQq2+tXQaeDAgXrnnXfU3Ny81+cbGxv1zjvvdHvnkTdde+21+uc//6lXX31V8fHxstvtstvtampq6lwzZ84czZs3r/PjG2+8UZ988okeeeQRbdmyRXfffbdWr16t6667zmf79Getza3+3kJIVdVQpaqGKiVEJ3Q+FgwXHhgYGBgYGBgYGBgYGBgYGBgYoWs0tjSqpa1FWQlZATsM6kvDGm7Vropdqmqo8jgHUTDVr4ZOV1xxhXbu3Kljjz1W7777riorKyVJlZWVWrRokY477jjl5+fryiuv9NkennrqKdXW1mr69OnKysrq/PP66693riksLFRJSUnnx8ccc4xeffVVPfvss5owYYLeeustLVq0SGPHjvXZPv1ZTEKMv7cQUhXVFMk0TUWEtb8ebLBceGBgYGBgYGBgYGBgYGBgYGBghK7R2NKoCEuEMhMyPZ4PpGFQXxmTBk9SU2uTdpXv8jgPUTAV7u8NdNTxpnG+7Oabb9bWrVv14osv6txzz5UkWSwWud3uzj1cdtlluvnmm322hwP5PJctW+bx2HnnnafzzjvPBzsKvFJzUmVvtvt7GyFRvbNe9lq7bNHt7+sVTBceGBgYGBgYGBgYGBgYGBgYGBiha8RFxqmuuc7j96WBNgzqSyMhJkGb7Zs1buA4WcOtHuckCoYCZuj0zjvvqKWlxaeGxWLRCy+8oDlz5uill17S+vXrVVtbq4SEBE2YMEEXX3yxpk+f7tM9UPcVri+UdQTfVHuj8rpyNbY0KikmKeguPDAwMDAwMDAwMDAwMDAwMDAwQteobKiUKQZOPzVS41KVX5mv3VW7dUj6IR7nJQqGAmboNHXq1D6zpk2bpmnTpvWZR+SP2lxt2l21W9ER0XK2OYPuwgMDAwMDAwMDAwMDAwMDAwMDI3QNiyySqc7BUyAPg/rKiAiLkEUWbS7ZrGFpw2QYhsf5iQI9r4dOJ510Uo/WR0VFKTExUWPGjNFpp52mKVOmeLsFCrHShqapVrX+3kbQV9lQqerGasVHxgflhQcGBgYGBgYGBgYGBgYGBgYGRugahmEwcNqL4TbdWp2/WkcPO1qp8akezxMFel4PnX76/kMdk9efvw7n3h43DEN33nmnZsyYoZdeeklZWVnebqVHuVwuVVRUyOl07vX5wYMH9+l+6MdcrS4pwt+7CP5KakvkbHOqwdkQlBceGBgYGBgYGBgYGBgYGBgYGBihaxiGIdM01eBs0K6KXQE/DOoro7qxWm3uNuVX5jN0oqDM66HTXXfdpdbWVj311FOqrq7W4MGDNW3aNGVnZ0uSioqKtHz5chUUFCg5OVlXX3216uvrtXr1aq1YsUJLlizRqaeequ+++05RUZ7flHq7NWvW6Pbbb9fy5cv3+R5ShmGora3N53uhvVe1p4r3dPKyeme9iqqLVN9cr4TohKC88MDAwMDAwMDAwMDAwMDAwMDACF3DkCGX6dL28u3KsGUE/DCoLw2LYdGWki0alz1OkRGRHmuJAjmvh05/+tOfNHPmTDU3N2vhwoWaM2fOXtf94x//0NVXX63vvvtOn3zyiSwWi7744gvNnj1bubm5eu6553T99dd7u539tm7dOh1//PEKDw/XKaecovfff18TJkxQZmam1q5dq/Lyck2fPl05OTk+3QeRryuvK1dBVYHS49KD9sIDAwMDAwMDAwMDAwMDAwMDAyN0jTZ3m5ytTkWHB88wqK+MVler8ivytad6jw5JP8RjPVEgZ+l+yf5bsGCBvvzyS/3tb3/b58BJki6++GL97W9/05IlS/TYY49JkqZNm6aHHnpIpmnq//7v/7zdSrfdd999kqRvv/1W7777riTpnHPO0ccff6z8/HxdffXV2rhxo+666y6f74X23dApQ/29haDO5XZpq32rosKj2v/LiCC98MDAwMDAwMDAwMDAwMDAwMDACF2jprFGFsOiYWnDgmYY1FdGRFiELIZFW0u3eqwnCvS8Hjq98sorCg8P18UXX9zt2t/+9rcKDw/Xyy+/3PnYBRdcIMMwlJub6+1Wuu2rr77SWWedpdGjR3c+1vE+U9HR0XriiSc0YMAA3X777T7fC+274s3F/t5CUFdZX6lSR6lGpI8I6gsPDAwMDAwMDAwMDAwMDAwMDIzQNcLDwjuHKz8tkIdBfWmkxadpW+k2VdZXehxHFMh5PXTasWOH4uLiZLV2/x48kZGRiouL0/bt2zsfS0hIUGJiohwOh7db6bba2loNGzas8+OIiAjV19d3fmyxWDR9+nQtWbLE53uhfedsdPp7C0Hdnuo9iouKkzWi69dksF14YGBgYGBgYGBgYGBgYGBgYGCErpEckyxTZpfng2EY1FeGxWJRbkmu8ivyPZ4jCuS8HjqFh4erpqZGJSUl3a4tKSlRTU2NwsO7vpVUY2OjEhISvN1Kt6Wnp6u6urrz48zMTG3btq3LmubmZjU2Nvp8L7TvouI9f2DRgdXgbFBZXZkSoxO7PB6MFx4YGBgYGBgYGBgYGBgYGBgYGKFrdAxqOgZPwTIM6ksjITpBm0s2q7Wt1WMNUaDm9dBp8uTJkqRbb72127W33XabTNPsPEaSSktL5XQ6lZGR4e1Wuu2www5TXl5e58fHHnusPvvsM61cuVKStHnzZr3xxhsaNWqUz/dC+y7jEN//uxCqldeVq7GlUbGRsZ2PBeuFBwYGBgYGBgYGBgYGBgYGBgZG6BqGYbS/9YkZfMOgvjKOGHKEyuvLtbt6t8c6okDN66HTDTfcINM09corr+j000/Xl19+qba2ts7n29ratHz5cv3iF7/QP//5TxmGoRtuuKHz+U8++USSdNRRR3m7lW6bNWuWli9f3nlX1q233irTNHXccccpLS1N48aNU01NDe/p5OcK1hX4ewtBmcvt0u7q3YqKiJJhGJKC+8IDAwMDAwMDAwMDAwMDAwMDAyN0DUPtv78KxmFQXxlREVGSKW0v2+6xlihQ83rodPbZZ2vu3LkyTVOfffaZpk+frri4OGVnZ2vgwIGKi4vTiSeeqE8//VSmaeqmm27S2Wef3Xn8qlWrNGHCBM2ePdvbrXTb1VdfraKiIqWkpEiSJkyYoCVLlui0005TamqqZs6cqffff1/nnHOOz/dC1NtVNVSpuqFaCdHtL1UZ7BceGBgYGBgYGBgYGBgYGBgYGBihaxiGIbfp1s6KnUE3DOpLI82Wpl0Vu1TdUO1xDFEgFt79ku77y1/+osMPP1x33nmntm/frpaWFo/3eBo+fLjuueceXXjhhV0ef+KJJ3pjCwdURESEx8v4HXPMMfrwww/7bA/UfamDU+WQw9/bCLpKHaVym25FhEWExIUHBgYGBgYGBgYGBgYGBgYGBkboGqZpqtXVqqbWJo0fND7ohkF9ZdiibCqqLlJhVaGSYpM8jiUKtHpl6CRJF1xwgS644AKtW7dOa9euVXl5uSQpLS1NkydP1sSJE3uLOujCwsJ0wQUX6JVXXvH3Voh6taaWJhXXFCs+Kj5kLjwwMDAwMDAwMDAwMDAwMDAwMELXqG6sltt065C0Q4JyGNRXhtt0q7y+XN/t+k5jBoxReFiv/UqfyCd5/W/ovffeK0m67LLLNGjQIE2cODEgBkx7y2azadCgQf7eBnVTRWGFrCOs/t5GUFVeX656Z72ybFkhc+GBgYGBgYGBgYGBgYGBgYGBgRG6Rpu7TeGWcEVboz3OEQzDoL40IsMjVd1YreKaYg1OGexxHqJAytL9kv13zz336P7771dmZmZv7MenHXnkkfrhhx/8vQ2iXs1turWnao8iLBEqri0OmQsPDAwMDAwMDAwMDAwMDAwMDIzQNVLjUmUYhmR2PUcwDYP6yhibPVbhlnDtKNvhcR6iQMvroVNqaqpsNpsiIiJ6Yz8+7e6779bnn3+ul19+2d9bof2UMzHH31sIqqobqlVRX6GGloaQuvDAwMDAwMDAwMDAwMDAwMDAwAhdwxru+UpHwTYM6ksjNS5V28u3y9Hk8DgfUSDl9cvrTZgwQZ9//rkqKyuVkpLSG3vyWYsXL9b06dN12WWX6fHHH9cRRxyhjIyM9on6TzIMQ3fccYefdkmlO0qlLH/vIngqdZSqpLZE8VHxIXXhgYGBgYGBgYGBgYGBgYGBgYERuoYhQ6ZpyvzPrU7BOgzqKyMhJkHbSrepsKpQY7PHepyXKFDyeuj0+9//Xv/+97/117/+VQ888EBv7Mln3X333Z1/v2bNGq1Zs2av6xg6+bfmumZZs3hPpwPJ2erU9rLtshiWkLvwwMDAwMDAwMDAwMDAwMDAwMAIXcMwDAZOPTAshkUx1hit371eo7NGexxLFCh5PXT65S9/qblz5+qhhx5Sa2urbrnlFqWmpvbG3nq9pUuX+nsLWr58uR5++GGtWbNGJSUleueddzR79ux9rl+2bJlOPPFEj8dLSkqC4n20DqbImMjOHzi0/8rry1VSW6KRGSND7sIDAwMDAwMDAwMDAwMDAwMDAyN0DUPt7+dkr7WrxdUStMOgvjTa3G36Yc8PmjZymrKTsj2eJwqEvB46nXTSSZKk2NhYPfLII1qwYIGGDx+u9PR0hYXtfdpqGIaWLFniLd3jpk2b1ufmz2toaNCECRN0+eWX69xzzz3g4/Ly8mSz2To/Tk9P98X2AqIBoweoqK7I39sI+EzTVH5FvlLiUhQTGePxfLBfeGBgYGBgYGBgYGBgYGBgYGBghLbR5m6T3WHX+IHjg3oY1FdGeV25woww7azYydCJAjavh07Lli3r8rHL5VJeXp7y8vL2eczP30OpP3X66afr9NNP7/Fx6enpSkxM7P0NBWC71uySdQQvr9ddtU21qmyoVHq85wAyVC48MDAwMDAwMDAwMDAwMDAwMDBC06h31stlupRhywj6YVBfGtZwq7bZt2ny4MmKjYz1WEvk77weOt111129sQ+fdNppp+m+++7TEUcc0eNjGxoa9Pjjjys+Pl7XXnutD3bXsyZOnCin06mxY8fq7rvv1rHHHrvPtU6nU06ns/Njh8PRF1ukPq7UUSq32+3xQztULjwwMDAwMDAwMDAwMDAwMDAwMELXqGuuk8WwKMOW4fF8MA6D+spwuV3aUbZDhVWFGp012mM9kb8L6aFTeXm5jj76aJ1wwgmaM2eOzj33XCUkJOz3mG+++Ub//Oc/9dprr6mpqUkvvfRSH+1272VlZenpp5/W4YcfLqfTqeeff17Tp0/Xt99+q8mTJ+/1mPnz5+uee+7p4532XskDk1Wven9vI6BrdbWqqKbI42X1QunCAwMDAwMDAwMDAwMDAwMDAwMjdA1btE0Ww+LxfLAOg/rKCLOEKTIiUnn2PI3MGOnxz4DI33k9dArk1qxZo5deekn33HOPfve73+nKK6/UyJEjNWXKFGVkZCgxMVHNzc2qqqpSXl6eVq9erbq6OoWFhemCCy7Q/fffr8GDB/v1cxg5cqRGjhzZ+fExxxyjHTt2aMGCBfrHP/6x12PmzZunuXPndn7scDg0aNAgn++1twqL2Pt7gdGPldeVy9Hk6PJfgoTahQcGBgYGBgYGBgYGBgYGBgYGRugaNU01Mk2zy/PBPAzqSyMtPk27yneprK5MmQmZHscS+bOQHjpJ0iWXXKI5c+boo48+0osvvqhly5bpn//8p8c6i8Wi8ePH65xzztEVV1yhrKwsP+z2wDryyCP11Vdf7fP5yMhIRUZG9uGOerfyXeW8p1M3FdcWy2JYOn9gheKFBwYGBgYGBgYGBgYGBgYGBgZG6Bq1TbUyZXYOnkJhGNRXhtt0a7N9s3aW72ToRAFXrw2dGhsb9fzzz+vTTz9VQUGBmpqatGPHjs7na2tr9eGHH8owDF144YW9xR5QhmFo1qxZmjVrliRp8+bN2rNnjyorKxUdHa20tDSNGTOm25feC5TWrVsX0EMx8m2OJofKHGWyRdskhe6FBwYGBgYGBgYGBgYGBgYGBgZG6BqGYXQ+HyrDoL40kmOTtbV0qyYNnqRoa7THOiJ/1StDp3Xr1unss8/Wnj17OifTP/2mIUk2m03333+/8vLylJGRoZNOOqk36INq9OjRGj3aP2+yVl9fr+3bt3d+vGvXLq1bt07JyckaPHiw5s2bp6KiIr388suSpEcffVRDhw7VmDFj1NzcrOeff16ff/65PvvsM7/svy8aPH6w7M12f28jYCurK1NTS5NS41JD+sIDAwMDAwMDAwMDAwMDAwMDAyN0DUOGTNNUQ0uD7A57yAyD+soYlz1Ouyp2aXfVbo3IHOGxlshfWbpfsv8qKys1a9Ys7d69W5MnT9Zf/vIX2Ww2j3WGYeh3v/udTNPUe++95y0btK1evVqTJk3SpEmTJElz587VpEmTdOedd0qSSkpKVFhY2Lm+paVFf/zjHzVu3DhNmzZNP/zwg/79739rxowZftl/X1RRUOHvLQRsba427aneo9jI2JC/8MDAwMDAwMDAwMDAwMDAwMDACF3DMNqHTrvKd4XUMKivDGu4VdZwq7aWbvV4bywif+b1nU4LFixQSUmJZsyYoU8//VQWi0UPP/yw6urqPNbOmjVLN998s1auXOktG7RNnz59v98EFi5c2OXjW265RbfccouPdxVYNdY2yprBezrtrYr6CtU21SrcEq7qxuqQvvDAwMDAwMDAwMDAwMDAwMDAwAhdo7WtVS7TpaiIqJAaBvWlkRaXpsKqQpXXlSvdlu5xHJE/snS/ZP+9//77MgxD//M//+PxzefnjRw5UhEREV3e64no50VERfh7CwFbcU2xHI0OBk4YGBgYGBgYGBgYGBgYGBgYGEFtlNeVS5KGpAwJuWFQXxlxUXGqb67XropdHscR+Suvh047d+6U1WrVxIkTu11rGIZsNpscDoe3LIVwg8YN8vcWArK65joVVhXK6XL6/aIAAwMDAwMDAwMDAwMDAwMDAwPDG8MabpXFsHgYoTAM6kujsrFS3+38Ts5Wp8fxRP7I66GT2+1WeHi4DMPodq1pmqqvr1dsbKy3LIVwO1ft9PcWArLyunLtqd6j7MRsv18UYGBgYGBgYGBgYGBgYGBgYGBgeGOkx6d7vA1JKA2D+sqwhllV52z/j9WJAiGvh07Z2dlqbGxUWVlZt2tXrVolp9OpoUOHessS9avaXG3Ks+cpNS5VKXEpHs+H4oUHBgYGBgYGBgYGBgYGBgYGBkboG6baB0+hNgzqK+OwAYcpJjJGW0u3egzxiPyR10On6dOnS5JefPHFbtfec889MgxDJ598sresz3I4HCosZCrsz5IGJPl7CwFXRUOFahprNDh5sMdzoX7hgYGBgYGBgYGBgYGBgYGBgYEReoZhGJ1DklAcBvWlkR6frsLKQlXUV3icj6iv83rodOONN8owDD344IP697//vdc1paWluuiii/Txxx/LarXq2muv9ZY9qN577z2dc845ys7OVnR0tAYMGKCZM2fqhRdekMvlkiQtWLCAO7H8nDXa6u8tBFzF1cWKscYoPCy8y+OhfOGBgYGBgYGBgYGBgYGBgYGBgRG6hiFDpky5XKE7DOorIz4qXvXOeu0q3+VxTqK+Lrz7JftvzJgxevDBB3Xbbbfp1FNP1aRJk1RbWytJ+s1vfqOCggKtWbNGra2tkqTHHntMgwd73q3hy+rr63XRRRfpgw8+6HKLod1ul91u19KlS/XEE0/otdde69N90d4r3VEq6wgGTx3VN9errK5Mtmhbl8dD/cIDAwMDAwMDAwMDAwMDAwMDAyN0DcMwJEn5lfmyRdtCchjUl0ZibKLW7V6nCYMmKDIi0uP8RH2Vpfsl3XfLLbfoueeek81m09q1a9Xc3CzTNPX6669r5cqVamlpUUJCghYuXKirrrqqN8gedeGFF+r999/XlClT9Oabb8put8vpdGrnzp166623NH36dP3www868cQTtWXLlj7fH9H+KnWUqrGlUTHWmM7H/H1RgIGBgYGBgYGBgYGBgYGBgYGB4Y1huk253C41tYb2MKivjNa2VuUW56qwqtDjOaK+zOs7nTr63e9+p/PPP1//93//p6+//lrFxcVyuVzKzMzUscceq/POO08JCQm9xR1wixYt0ocffqjf/OY3eumllxQW9uMX7JAhQzRkyBCde+65+uc//6mrrrpKb7zxRp/vkbo2cOxAlbWU+XsbAVGbq027q3crxhrT+V9/BMJFAQYGBgYGBgYGBgYGBgYGBgYGhjdGWX377/+Gpg4N6WFQXxkltSWKj4pXnj1Pw9OHd/4ukaiv67WhkyTFxcXpkksu0SWXXNKbp/WqF198UWlpaXr22We7DJx+3m9/+1slJCTo7LPP5gvSz1UXV0up/t5FYFRRX6HaplqlxaVJCpyLAgwMDAwMDAwMDAwMDAwMDAwMDG+MVlf727FER0R7nCOUhkF9adiibCqsKlR5XbnSbekea4n6ol4dOgViq1at0qxZsxQTE9Pt2jPPPFMPP/ywNm7c2Ac7o33VUNUgayrv6SRJRTVFMmQoPCw8oC4KMDAwMDAwMDAwMDAwMDAwMDAwvDEyEzI9jpdCcxjUl8ae6j3aWb6ToRP5rV4fOuXm5mr16tUqK2u/PTI9PV2HH364xowZ09vUAVVZWakBAzy/qPfVH//4Rx/uhg6kcGvIz0IPKEeTQ6WOUtmibQF3UYCBgYGBgYGBgYGBgYGBgYGBgeGN0eBskPmf/+solIdBfWUkxyYrz56nCYMmKNrqeRcZka/rtd/uf/DBB7r99tuVm5u71+fHjBmj+++/X2eddVZvkQdUQkKCysvLD3j9W2+9pU2bNunOO+/04a5ofw2ZPIQ3vJNkd9jV3NqsuMi4gLsowMDAwMDAwMDAwMDAwMDAwMDA8MZobGmUTKlj5hTqw6C+MlLiUpRbnKuCygKNyhrlcSyRr7N0v6T77r33Xp199tnauHGjTNNUWFiY0tPTlZ6errCwMJmmqY0bN+qcc87R3Xff3RvkATdx4kR9/PHHam1t7XbtypUrdeGFF+qee+7pg53Rvtr+zXZ/b8HvtbS1aE/1HoVbwgPyogADAwMDAwMDAwMDAwMDAwMDA8Nbo+MuJ38PakLJaGptUlF1kdbtXie32+3xPJGv83ro9Mknn+juu++WaZo64YQT9Nlnn6murk4lJSUqKSlRfX29PvvsM02fPl2maeq+++7Tp59+2ht7P6AuvPBC7dmzR7fddtt+1y1fvlyzZ8+Wy+Xqo50R7bvyunJV1lfK0eQI2IsCDAwMDAwMDAwMDAwMDAwMDAyMgzUMw5ApU2V1ZX4f1ISakZmQqVJHqUpqSzzWEPk6r4dOf/3rXyVJ5513npYuXaqZM2cqMjKy83mr1aqZM2dqyZIlOu+882SaZucxfdGcOXN0xBFH6NFHH9W5556rtWvXdj7ndru1bt06XXXVVTr55JPV2NioU045pc/2RnsvITPB31vwa27TrcLKQpXXlSvaGh2QFwUYGBgYGBgYGBgYGBgYGBgYGBjeGIYMmaapUkdpQAxqQskYP3C82txt2l7GK0pR3+f10Gn16tUyDEN//etfZRjGPtcZhqFHHnlEkrRq1Spv2QMuLCxM7777riZOnKhFixbpiCOOUHx8vAYOHKioqChNmTJFzz//vNLS0vTpp59q6tSpfbY32nvR8f37De6qG6q1rWybkmKSAvaiAAMDAwMDAwMDAwMDAwMDAwMDwxujtqlWkpRuSw+IQU2oGalxqdpWuk2OJofHeiJf5vXQqaWlRYmJicrOzu527cCBA5WUlHRA76/Um2VmZmrlypV69NFHNW7cODU2Nqq4uFhtbW3KycnR7bffrtzcXB1zzDGSJNM0+3R/1DX7Nru/t+DXimqK1NzWrKGpQwP2ogADAwMDAwMDAwMDAwMDAwMDA8Mbo7qxWpKUGpfq8XwoD4P6ykiMSVR1Y7V2VezyOIbIl4V7e4Jhw4YpLy9PLS0tslqt+13rdDpVX1+vUaNGecv2OKvVqhtuuEE33HCDnE6nqqqqlJiYqOjornfVXHrppZo+fXqf749IkhpbGrWjbIeGpjBwwsDAwMDAwMDAwMDAwMDAwMAIXaPj/D+/ASDUh0F9ZVgMi2IiY7Qqf5UOyzpMEeERHscT+SJL90v2329+8xu1trbq5Zdf7nbtP/7xD7W2tuo3v/mNt6xXRUZGKisry2PgJEk5OTmaNm2aH3ZFHWWP7v6uuVCt1FEql+mSLdrW5fFAuyjAwMDAwMDAwMDAwMDAwMDAwMDwxkiKSWp/8CczJ38PakLNqGms0abiTSqsKvQ4nshXeT10+uMf/6jjjjtON9xwg1566aV9rnv55Zd1ww036Pjjj9cf//hHb9mgbfny5TrzzDM1YMAAGYahRYsWdXvMsmXLNHnyZEVGRmr48OFauHChz/fpzxxlDn9vwS+1udpUWFmouMi4Lu+PFogXBRgYGBgYGBgYGBgYGBgYGBgYGN4YHb//Mv8zdQqUQU0oGa2uVqXFp2mLfQtvKUN9Vo9eXu/ee+/d6+PHH3+8NmzYoMsvv1x33XWXpk+f3vkeT0VFRfriiy9UWFiohIQEHX/88XrwwQd15513er/7IKyhoUETJkzQ5ZdfrnPPPbfb9bt27dKsWbN09dVX65VXXtGSJUt0xRVXKCsrS6eeemof7Ljvq6uskzVl/y/VGIqV1ZWpuqla6XHpnY8F6kUBBgYGBgYGBgYGBgYGBgYGBgaGN4ah9qGT23QH1KAm1AxJyq/IV6mjVJkJmR7nI+rtDLMHI06LxdLlDoyf13Gqn6/Z2+Mul6tHGw3FDMPQO++8o9mzZ+9zza233qoPP/xQGzdu7HzsggsuUE1NjT755JO9HuN0OuV0Ojs/djgcGjRokGpra2Wz2fZ6TCD1txf+pvBh4RqUPMjfW+mzTNPUqvxVstfaO7/5B/JFAQYGBgYGBgYGBgYGBgYGBgYGhjdGY0uj/rr4r7rrjLskQwE1qAk1I8+ep6OGHaXjDz3e45zUf3M4HEpISOj1uUGP7nQ64YQT9jt0ot5v5cqVmjlzZpfHTj31VN100037PGb+/Pm65557fLwz3zXs8GH97nVGqxqqVFZX1vlatoF+UYCBgYGBgYGBgYGBgYGBgYGBgeGN0XGnU0FlgUYNGBVwg5pQMlLjUpVXkqcJAyd4vJc8UW/Xo6HTsmXLfLQN2ld2u10ZGRldHsvIyJDD4VBTU5Oio6M9jpk3b57mzp3b+XHHnU7B0o7vdihieIS/t9GnFdcUq83VpsiIyKC4KMDAwMDAwMDAwMDAwMDAwMDAwPDGcLa1v1KTNdwakIOaUDKSY5P1zY5vtLN8pyYOnuhxfqLerEdDJwqOIiMjFRkZ6e9tHHSmu3+9qV19c72Kaopki7IFzUUBBgYGBgYGBgYGBgYGBgYGBgaGN0ZxTbEkaWDSwIAc1ISSUVJbotrmWq3JX6PRWaMVGRG8vzumwM/S/RLyZ5mZmSotLe3yWGlpqWw2217vcgqFbGn96xbPktoSNbY0KjoiOmguCjAwMDAwMDAwMDAwMDAwMDAwMLwxOo41LF3fziVQBjWhZowZMEbVTdXaVbHLYw1Rb8bQKcCbOnWqlixZ0uWxxYsXa+rUqX7ake+LS/H85hiqOVudKqwqVExEjIpri4PmogADAwMDAwMDAwMDAwMDAwMDA8MbY0DCgM7zdhRog5pQMgYlD1JURJQ2FW+Sy+3yWEvUW/Xo5fVOOumkXkENw/AYpPSX6uvrtX379s6Pd+3apXXr1ik5OVmDBw/WvHnzVFRUpJdfflmSdPXVV+uJJ57QLbfcossvv1yff/653njjDX344Yf++hR8XvGWYllHWP29jT7J7rCrprFGbtOtVldr0FwUYGBgYGBgYGBgYGBgYGBgYGBgeGO4TFfnuaXAHNSEmpFpy1RhdaF2V+3WkNQhHscQ9UY9GjotW7Zsv88bRvutkKZp7vXxjud++nF/a/Xq1TrxxBM7P547d64k6ZJLLtHChQtVUlKiwsLCzueHDh2qDz/8UH/4wx/02GOPaeDAgXr++ed16qmn9vneqXdrc7UpvyJftU21ioqICqqLAgwMDAwMDAwMDAwMDAwMDAwMDG8Mt6t92GTKDOhBTSgZkRGRkinlFuUqJyWnX/+ennxXj4ZOd911114fb2lp0VNPPaWamhplZ2dr+vTpGjhwoCSpqKhIy5Yt0549e5SUlKSrr75aVmv/uItlb02fPt1jKPfTFi5cuNdjvv/+ex/uKrDKGpmlSrPS39vweWV1ZdpZsVMRYRFBd1GAgYGBgYGBgYGBgYGBgYGBgYHhjdEx8GhztwX0oCbUjLioOK3cuVKTcibt9Xkib/N66NTW1qaZM2eqqalJzzzzjK644gqPCalpmnrhhRd0/fXXa8WKFfr3v//t3a4ppGuobpAS/b0L3+Zyu7StbJuaWpo0Nnts0F0UYGBgYGBgYGBgYGBgYGBgYGBgeGtI0p7qPRoaNjSgBzWhZOyp3iNHs0ObSzYzdCKf5PlV3sMWLFigL7/8UgsWLNCVV16511vyDMPQFVdcoQULFmj58uVasGCBtyyFcI4yh7+34PMq6iu0s2ynRmaODNqLAgwMDAwMDAwMDAwMDAwMDAwMjIM1Ol4NytnqDPhBTagZU3KmaGvpVpXXlXusI/I2r4dOr7zyisLDw3XZZZd1u/ayyy5TWFiY/vnPf3rLUggX6q8l6jbd2l66XfFR8YqPivd4PhguCjAwMDAwMDAwMDAwMDAwMDAwMLwximuKJUlZCVlBMagJJSM5Nln1znptsW/xWEvkbV4PnXbs2KG4uDhFRkZ2uzYyMlLx8fHasWOHtyyFcIccdYi/t+DTKusrVVZXpoyEDI/nguWiAAMDAwMDAwMDAwMDAwMDAwMDwxujxdUiSbKGWz3OEYiDmlAz0uPTtbl4s6obqj2OIfImr4dO4eHhqqmpUVFRUbdri4qKVF1drfDwHr2VFPWzdq3e5e8t+CzTNFVQWSCLxaLI8K6D2mC6KMDAwMDAwMDAwMDAwMDAwMDAwPDWkCRTZpc1gTyoCSUjKSZJNU012lS8yeM4Im/yeuh0+OGHS5L+67/+q9u1HWs6jiHaW642l7+34LOqGqpkd9iVFJPk8XiwXRRgYGBgYGBgYGBgYGBgYGBgYGB4Yxgy9NOZU6APakLJcJtu1TTV6KttX6m2sdbjeKKDzeuh09y5c2Wapt544w3NmDFDS5cuVWtra+fzbW1tWrp0qWbOnKk33nhDhmFo7ty53rIUwsWleH4TDYU67nJqc7V1+cEdrBcFGBgYGBgYGBgYGBgYGBgYGBgY3hiGYcgtt6TgGNSEmhFhiZDT5eS9nahX8/p17k4//XTdeeeduvfee7Vs2TItW7ZM4eHhSk1NlSRVVFSora1Nptk+sv7zn/+s008/3VuWQriEjASVt5X7exu9XlVDlUpqS5QUm9TlsWC9KMDAwMDAwMDAwMDAwMDAwMDAwPDWcLvdQTWoCSVjVNYoNbc2a0PRBo3KHKWEmASP8xH1NEv3S7rv7rvv1qJFizRq1CiZpqnW1laVlJSopKREra2tMk1To0eP1ttvv6177723N0gK4Yo2df/+YMFWx11Ora5WRUdESwqNiwIMDAwMDAwMDAwMDAwMDAwMDIyDNQzDUFVDVVANakLNSIlNUVVDlTaXbPY4H9HB5PWdTh2dddZZOuuss7RhwwatXr1aZWVlkqT09HQdfvjhGjduXG9RREFXZUOlimqKOn84h8JFAQYGBgYGBgYGBgYGBgYGBgYGhjdGhzP1kKlBNagJJcMwDKXHp2tj0UaNzBzZ5VWaiA6mXhs6dTRu3DgGTORVmYdmqkpV/t5Gr+U23covz5fbdCsqIipkLgowMDAwMDAwMDAwMDAwMDAwMDC8MWRKiTGJQTeoCTUjOTZZK3as0IaiDTphxAke5yfqSZbul+y/5cuX98Y+iDprrmv29xZ6tfK6chXXFis5NjmkLgowMDAwMDAwMDAwMDAwMDAwMDC8MQzDUEKU5/sIBcOgJpSMktoSOVudWp2/WhV1FR7PE/Ukr4dO06dP12GHHaZHH31UVVWhc3cK+a8ae42/t9Brudwu7azYKUlqcDaE1EUBBgYGBgYGBgYGBgYGBgYGBgaGN4bFsMgtd5fng2VQE2rGqKxRcptubdizQaZpeqwjOtC8HjpJ0pYtW/THP/5RAwcO1MUXX8zdT0T/yV5rV6mjVIZhhNxFAQYGBgYGBgYGBgYGBgYGBgYGhjeGYRhdBhzBNqgJNSMrIUub7JtUUlvisZboQPN66LRt2zbdcsstSk9PV3Nzs1599VWdeOKJ3P1EB90hRx3i7y30Sq2uVu2s2KlGZ6Nqm2pD7qIAAwMDAwMDAwMDAwMDAwMDAwPDW8Nttt/pFKyDmlAybNE2OVudWle4Tm632+MYogPJ66HTIYccooceeki7d+/WW2+9pZNPPlmGYXD3Ex10BesK/L2FXmlP9R7lV+SrzdUWshcFGBgYGBgYGBgYGBgYGBgYGBgYB2t03OkUzIOaUDOyk7K1sXij8ivzPY4jOpC8Hjp1FB4ernPPPVeffPKJdu7cqT/96U8aMGCAmpub9corr3D3Ex1wbc42f2/B6xpbGrW5ZLMaWhqUbksPyYsCDAwMDAwMDAwMDAwMDAwMDAwMbwxDhppbm4N+UBNKhtt0a1fFLn217Su1tLV4PE/UXb02dPppgwcP1n333aeCggK9++67OvPMM2WxWDzufvr66699wVOQF5sU6+8teN3O8p3aUbZDOck5IXtRgIGBgYGBgYGBgYGBgYGBgYGB4Y1hypTdYQ/6QU2oGQOTBsrusGuLfYvHGqLu8snQqfPkFovOPPNMXXPNNTrqqKM6b5fsuPvphBNO0LHHHqtVq1b5chsUZCUP9PwBFExVNVTphz0/KDspWylxKR7Ph8pFAQYGBgYGBgYGBgYGBgYGBgYGhjeGaZqKsESExKAmlIwxA8YoKSZJa/LXyNHk8FhLtL98NnQqKSnR/fffr2HDhmnWrFlasWKFTNPUcccdp8cee0yzZs2SYRhauXKljjvuOC1btsxXWwnInnzySQ0ZMkRRUVE66qij9N133+1z7cKFC2UYRpc/UVGe38hDpd0bdvt7Cwedy+3SpuJNcrldGpg00OP5ULoowMDAwMDAwMDAwMDAwMDAwMDA8MYwDENpcWkhMagJNSPdlq6K+gp9X/i9TNP0OIZoX/Xq0Mk0TX344YeaPXu2cnJydNdddyk/P1/x8fG69tprtWHDBi1fvlzXX3+93n//fW3dulUnn3yyWltbdccdd/TmVgK6119/XXPnztVdd92ltWvXasKECTr11FNVVla2z2NsNptKSko6/xQUFPThjulAK64pVkFlgQ5JO8TjuVC7KMDAwMDAwMDAwMDAwMDAwMDAwPDGCLOESUbX54N5UBNKhsWwaEDiAP2w+wftrgremwSo7+uVodOePXt0zz33aMiQITrrrLP03nvvqa2tTZMmTdKzzz6r4uJiPf744xozZkyX44YNG6Y333xTVqtV69ev742tBEV//etfdeWVV+qyyy7TYYcdpqeffloxMTH6+9//vs9jDMNQZmZm55+MjIw+3HHfln5Iur+3cFA1tjQqtyhXKXEpigiL6PJcKF4UYGBgYGBgYGBgYGBgYGBgYGBgeGMYMuSWu/P5YB/UhJoRGxmrnRU7tXzrcrW0tXgcT7S3wr09wRlnnKFPP/1UbrdbpmkqJiZG559/vq6++modccQR3R5vs9mUmZmp3bv7x7S0paVFa9as0bx58zofs1gsmjlzplauXLnP4+rr65WTkyO3263JkyfrwQcf9BjideR0OuV0Ojs/djgcvfcJ9EGtza1SrL930bNM09S20m1qbG1UdmJ2l+dC9aIAAwMDAwMDAwMDAwMDAwMDAwPDG8MwDLnN9qFTqAxqQs2Ii4xTfmW+NuzZoClDpnich+jneT10+uijjyRJo0eP1u9//3tdcsklSkhI6NE5fvWrX6mystLbrQRFFRUVcrlcHncqZWRkaMuWLXs9ZuTIkfr73/+u8ePHq7a2Vn/5y190zDHHKDc3VwMHer5v0Pz583XPPff4ZP99UXVRtawjrP7eRo8qdZSqsKpQKbEpMowf7wkO5YsCDAwMDAwMDAwMDAwMDAwMDAwMbwxDhkzTDLlBTSgZY7LHqKmlSWsK1mhg8kBl2EL3Fbiod/J66JSRkaHXX39d48aNU1JS0kGd4y9/+Yu32wjppk6dqqlTp3Z+fMwxx2j06NF65plndN9993msnzdvnubOndv5scPh0KBBg/pkr/2x5tZm5ZXmyTAMRVujuzweyhcFGBgYGBgYGBgYGBgYGBgYGBgYXhsud8gNakLNiIuM09bSrfpu53c6bexpigiP8DgvUUeW7pfsv9LSUs2YMaM39tIvSk1NVVhYmEpLS7s8XlpaqszMzAM6R0REhCZNmqTt27fv9fnIyEjZbLYuf4KpYYcP8/cWDjjTNLW9bHvnD9aOAuIHNgYGBgYGBgYGBgYGBgYGBgYGRgAbhmGovKE8JAc1oWbkpOQorzRPG4s3epyX6Kd5PXRKSEhQQkLCQd/l1N+yWq2aMmWKlixZ0vmY2+3WkiVLutzNtL9cLpc2bNigrKwsX23Tr+3J3ePvLRxwJbUl2lWxS6mxqbIY7V9OgfADGwMDAwMDAwMDAwMDAwMDAwMDI9ANl9ulFldLyA5qQsmIDI9Uq6tV7//wvkpqSjzOT9SR10On4cOHq66uTk6nszf20y+aO3eunnvuOb300kvavHmzrrnmGjU0NOiyyy6TJM2ZM0fz5s3rXH/vvffqs88+086dO7V27Vr99re/VUFBga644gp/fQo+raWpxd9bOKDqnfXaYt+iMCOs82X1AuUHNgYGBgYGBgYGBgYGBgYGBgYGRqAbkpQSkxKyg5pQMxpbGuVsc2rFjhVqbm32WEMk9cJ7Ol1wwQVas2aN3njjDV188cW9saeQ7/zzz1d5ebnuvPNO2e12TZw4UZ988okyMtrfhK2wsLDLN+Lq6mpdeeWVstvtSkpK0pQpU7RixQoddthh/voUfFq0LVouufy9jf3W5mrTFvsW1TTWKDsxW1Jg/cDGwMDAwMDAwMDAwMDAwMDAwMAIdCPcEq5wi+evqENpUBNqRnp8uraVbdN3u77T8YceL8MwPNZT/84wTdP05gRtbW2aNm2aNm7cqH/961/6xS9+0Vt7o17K4XAoISFBtbW1QfH+Tm9/87bsTXYNSh7k763ss62lW5VblKu0+DRZw60B9wMbAwMDAwMDAwMDAwMDAwMDAwMj0I2/f/13nTTyJM05Zk7nmlAc1ISaUddcp7K6Mp025jSNyhrlcQwFR76aG3h9p9ODDz6oE044QRs2bNCZZ56pMWPG6Nhjj1V6errCwsL2edydd97pLU0hWuEPhbKOsPp7G/vMXmvXttJtSohOYOCEgYGBgYGBgYGBgYGBgYGBgYFxkIYhQ6Z+vCcilAc1oWTER8WrrrlOS/OWKik2SRm2DI9jqf/m9dDp7rvvlmEY6rhhauPGjcrNze32OIZOFIw5mhzKLc6VIUNxUXEB+wMbAwMDAwMDAwMDAwMDAwMDAwMjGIyO3yuH+qAm1Iz4qHh9kfeFoq3R+tXkXykuyvMc1D/zeuh0wgkn8LqN1KulDUlTrWr9vQ2PmlubtaFog+qa65SVkBXwP7AxMDAwMDAwMDAwMDAwMDAwMDAC2TCM9judAmGIgtEzY2vpVuWk5qi0tlRfbvtSM0bPkDU8cF+9ivour4dOy5Yt64VtEP2Y2+X29xY8anW1Krc4V6WOUmUlZMnZ5gzoH9gYGBgYGBgYGBgYGBgYGBgYGBiBbhgy5GhyBMQQBePgjDZXmzYWbVRsZKyOG36cx78D1P/i3wAKuCp3V/p7C11yuV3aUrJFBZUFyrBlqNXVGvA/sDEwMDAwMDAwMDAwMDAwMDAwMALdcJtu1TXXBdQQBaNnRmREpLKTsrU6f7XWFq7tfLlE6r95facTUSjnNt3Ks+dpe/l2pcalyuV2BcUPbAwMDAwMDAwMDAwMDAwMDAwMjEA32txtirXGBtQQBaPnRnxUvJxtTi36fpGs4VaNHzje43jqP1m6X0LUtw2ZPMTfW5DUPnDaVrpNW0u3KikmSZKC5gc2BgYGBgYGBgYGBgYGBgYGBgZGoBsRYRGKjYr1eN7fQxSMnhtldWVqaGnQvzf9W7lFuR7noP4TQycKuErySvy9hc6B02b7ZiVEJ8hiWILqBzYGBgYGBgYGBgYGBgYGBgYGBkagG+GWcI+XYwuUIQpGz40jhx4pW7RNS/OWamPRRo9zUf+IoRMFXM4Gp1/9jvdw2lSySQlRCQqzhAXdD2wMDAwMDAwMDAwMDAwMDAwMDIxANwzDkNt0dz4faEMUjJ4bGbYMRUdE6/PNn2td4Tre46kfxtCJAq6oOM8fSH1VS1uLNhZtVF5pnpJikhg4YWBgYGBgYGBgYGBgYGBgYGBg+MgwZHQ+H6hDFIyeG+m2dMVHx+u9de9p5Y6VanO1eZyfQjeGThRwZR6a6Re3wdmgdbvXaUf5DqXGpvKSehgYGBgYGBgYGBgYGBgYGBgYGD40DMOQaZoBP0TB6LnR0taimqYafbzhYy3LW6amliaPNRSahft7A0Q/L//7fFlHWPvULK8rV25xrqoaqpRpy1Sbuy2of2BjYGBgYGBgYGBgYGBgYGBgYGAEg+FscwbFEAWj58bIzJFKjEnU97u/l6PJoeNHHK+0+DSP9RRaMXSifp3L7VJ+Rb7ySvPkcruUnZgtZ5szJH5gY2BgYGBgYGBgYGBgYGBgYGBgBLJhmqZqGmsUHREcQxSMgzMOSTtE+RX5qllXo6nDp2pkxkiPf58odOKfLAVcKYNT+sSpbarV2oK1Wl+0XtYwqzJsGQycMDAwMDAwMDAwMDAwMDAwMDAw+shocbUozBKmERkjgmqIgtEzIyIsQsPTh6u2uVYvfPmCFm9eLEeTw+N4Co2404kCLsMwul/kRa2uVhVWFmp72XY1tjQq3ZauiLCIkPqBjYGBgYGBgYGBgYGBgYGBgYGBEehGmCVMtiibLEZXI9CHKBg9NxpaGlTbVKv46HitK1wne61dRww5QiMyRig8jDFFKMU/TQq4KgoqfPKeTi63S6WOUu0o36HyunLFR8UrOylbUuj9wMbAwMDAwMDAwMDAwMDAwMDAwAh0IzI80uM/QA+WIQrGwRuGYchea9cnGz/R1tKtmjx4sgYlD/L5zQjUNzF0opDP5XapvK5cO8p2qKK+QuFh4cpKyOr8BhqKP7AxMDAwMDAwMDAwMDAwMDAwMDAC3TAMQ+Z//k8K3iEKRs+NAYkD5Gxzamf5Tq0pWKPDcw7X5JzJe/13j4Irhk4UcA2eMFj2JrvX53G2OlVaV6ptpdtU5ihTXGScUuNTFREW0bkmVH9gY2BgYGBgYGBgYGBgYGBgYGBgBLphyJBpMnDqr0a4JVyt7la1ulqVW5yrgsoCDUkdotFZozU4ebAiIyI9zkmBH0MnCrjKd5VLmQd3bJurTdWN1SqrK1N+Rb52V+2WLdqmnJScLsMmKbR/YGNgYGBgYGBgYGBgYGBgYGBgYAS60XGnk8vt0vby7UE/RME4OGPCoAmKi4xTY0uj8ivzta10m9Jt6To0/VDlpOQo3ZbucTwFbgydKOBqcjTJmnng7+nU1NKk2uZaVTdUy15rl6PZodqmWjW3NmtwymClxqV6HBPqP7AxMDAwMDAwMDAwMDAwMDAwMDAC3TBkyO12a2vpVjnbnCEzRME4OCPGGqOhqUPV5mrTppJN+mLrFxqaNlTDUoZpePpwZdgylBafpmhrtIdJgZOl+yXki5588kkNGTJEUVFROuqoo/Tdd9/td/2bb76pUaNGKSoqSuPGjdNHH33URzvt+6zR+x44udwu1TXXyV5r1/ay7fpm5zdavnW5vtn5jfLseWpxtSjMCJPFsGhwMgMnDAwMDAwMDAwMDAwMDAwMDAyMQDUkydHkUKOzMSSHKBgHZ5TVlam5tVlHDDlCI9JHyNHs0Bdbv9D/rf0//evbf+mDHz7Q94Xfq6CyQDWNNXK73R7nIP/FnU5+6PXXX9fcuXP19NNP66ijjtKjjz6qU089VXl5eUpPT/dYv2LFCl144YWaP3++zjjjDL366quaPXu21q5dq7Fjx/rhM/BtGaMyVFDT/g3D2eaUs9WpxpZG1TbVqqyuTHXNdQoPC1dEWIQiwyIVbY1WQnSCLBaLqhqqVNNUE9A/TDEwMDAwMDAwMDAwMDAwMDAwMDDccrY51epuDfkhCoZ3RsdfW12tsjvs+mjDRwoPC9fg5MGKi4xTbGSsUuNSlRKXovioeMVYYxRjjVF0RLSirdG8NF8fZ5gd79RGfdZRRx2lI444Qk888YSk9m+wgwYN0vXXX6/bbrvNY/3555+vhoYGffDBB52PHX300Zo4caKefvrpbj2Hw6GEhATV1tbKZrP13ifigyrqKvTos49qR/wOxUfFy+V2yZQp022qurFabtOtIalDZIuyyTCMLscGyw9TDAwMDAwMDAwMDAwMDAwMDAwMjCJ9tukz5aTk6OZTb/Z4P/ZQHqJg9I4htb/1SmNLo5pam9TU0qSS2hJJ0sCkgYqLjFNiTKLOGH+GYiJjPM7X3/PV3IChUx/X0tKimJgYvfXWW5o9e3bn45dccolqamr07rvvehwzePBgzZ07VzfddFPnY3fddZcWLVqkH374wWO90+mU0+ns/Li2tlaDBw/W7t27A37oVFxdrOdefk4brBsUFd7+A82UqQZng9rcbYqPjFd4mOcNeh3fVKKt0YqO8HxNzzZXm+qcdQq3hCs2MlYW42c/6Ew3BgYGBgYGBgYGBgYGBgYGBgYGRh8aefY81TTVeJzHbbrlNt2yGBaP46X23xe63W7JkMKMvd/F4jJdkilZLBYZMjyex+gfhsWw6I2r3tCRhxy512P6cw6HQ4MGDVJNTY0SEhJ67by8vF4fV1FRIZfLpYyMjC6PZ2RkaMuWLXs9xm6373W93W7f6/r58+frnnvu8Xh80KBBB7lrIiIiIiIiIiIiIt/UohZ/b4FCuJOfPtnfWwjo6urqGDrR/ps3b57mzp3b+bHb7VZVVZVSUlI8XpIu0OqYrgbDXVlE1PvxPYCo/8bXP1H/ju8BRP03vv6J+nd8DyDyX6Zpqq6uTgMGeL6UoTcxdOrjUlNTFRYWptLS0i6Pl5aWKjMzc6/HZGZm9mh9ZGSkIiMjuzyWmJh48Jv2QzabjR80RP04vgcQ9d/4+ifq3/E9gKj/xtc/Uf+O7wFE/qk373DqyPPFEcmnWa1WTZkyRUuWLOl8zO12a8mSJZo6depej5k6dWqX9ZK0ePHifa4nIiIiIiIiIiIiIiLq67jTyQ/NnTtXl1xyiQ4//HAdeeSRevTRR9XQ0KDLLrtMkjRnzhxlZ2dr/vz5kqQbb7xR06ZN0yOPPKJZs2bptdde0+rVq/Xss8/689MgIiIiIiIiIiIiIiLqjKGTHzr//PNVXl6uO++8U3a7XRMnTtQnn3yijIwMSVJhYaEslh9vQjvmmGP06quv6s9//rNuv/12HXrooVq0aJHGjh3rr0/BZ0VGRuquu+7yeHlAIuof8T2AqP/G1z9R/47vAUT9N77+ifp3fA8gCr0M0zRNf2+CiIiIiIiIiIiIiIiIgjve04mIiIiIiIiIiIiIiIi8jqETEREREREREREREREReR1DJyIiIiIiIiIiIiIiIvI6hk5ERERERERERERERETkdQydKKB68sknNWTIEEVFRemoo47Sd9995+8tEZGXzZ8/X0cccYTi4+OVnp6u2bNnKy8vr8ua5uZmXXvttUpJSVFcXJx++ctfqrS0tMuawsJCzZo1SzExMUpPT9fNN9+stra2vvxUiMjLHnroIRmGoZtuuqnzMb7+iUK7oqIi/fa3v1VKSoqio6M1btw4rV69uvN50zR15513KisrS9HR0Zo5c6a2bdvW5RxVVVW66KKLZLPZlJiYqN/97neqr6/v60+FiHqQy+XSHXfcoaFDhyo6OlqHHHKI7rvvPpmm2bmGr3+i0Gn58uU688wzNWDAABmGoUWLFnV5vre+3tevX6/jjz9eUVFRGjRokP7nf/7H158aER1EDJ0oYHr99dc1d+5c3XXXXVq7dq0mTJigU089VWVlZf7eGhF50RdffKFrr71W33zzjRYvXqzW1ladcsopamho6Fzzhz/8Qe+//77efPNNffHFFyouLta5557b+bzL5dKsWbPU0tKiFStW6KWXXtLChQt15513+uNTIqKDaNWqVXrmmWc0fvz4Lo/z9U8UulVXV+vYY49VRESEPv74Y23atEmPPPKIkpKSOtf8z//8j/72t7/p6aef1rfffqvY2Fideuqpam5u7lxz0UUXKTc3V4sXL9YHH3yg5cuX66qrrvLHp0REB9h///d/66mnntITTzyhzZs367//+7/1P//zP3r88cc71/D1TxQ6NTQ0aMKECXryySf3+nxvfL07HA6dcsopysnJ0Zo1a/Twww/r7rvv1rPPPuvzz4+IephJFCAdeeSR5rXXXtv5scvlMgcMGGDOnz/fj7siot6urKzMlGR+8cUXpmmaZk1NjRkREWG++eabnWs2b95sSjJXrlxpmqZpfvTRR6bFYjHtdnvnmqeeesq02Wym0+ns20+AiHpcXV2deeihh5qLFy82p02bZt54442mafL1TxTq3XrrreZxxx23z+fdbreZmZlpPvzww52P1dTUmJGRkea//vUv0zRNc9OmTaYkc9WqVZ1rPv74Y9MwDLOoqMh3mycir5o1a5Z5+eWXd3ns3HPPNS+66CLTNPn6JwrlJJnvvPNO58e99fX+v//7v2ZSUlKX/w1w6623miNHjvTxZ0REPY07nSggamlp0Zo1azRz5szOxywWi2bOnKmVK1f6cWdE1NvV1tZKkpKTkyVJa9asUWtra5ev/1GjRmnw4MGdX/8rV67UuHHjlJGR0bnm1FNPlcPhUG5ubh/unogOpmuvvVazZs3q8nUu8fVPFOq99957Ovzww3XeeecpPT1dkyZN0nPPPdf5/K5du2S327t8D0hISNBRRx3V5XtAYmKiDj/88M41M2fOlMVi0bffftt3nwwR9ahjjjlGS5Ys0datWyVJP/zwg7766iudfvrpkvj6J+pP9dbX+8qVK3XCCSfIarV2rjn11FOVl5en6urqPvpsiOhACvf3BogkqaKiQi6Xq8svlCQpIyNDW7Zs8dOuiKi3c7vduummm3Tsscdq7NixkiS73S6r1arExMQuazMyMmS32zvX7O37Q8dzRBS4vfbaa1q7dq1WrVrl8Rxf/0Sh3c6dO/XUU09p7ty5uv3227Vq1SrdcMMNslqtuuSSSzq/hvf2Nf7T7wHp6eldng8PD1dycjLfA4gCuNtuu00Oh0OjRo1SWFiYXC6XHnjgAV100UWSxNc/UT+qt77e7Xa7hg4d6nGOjud++vK9ROTfGDoREVGfde2112rjxo366quv/L0VIuqDdu/erRtvvFGLFy9WVFSUv7dDRH2c2+3W4YcfrgcffFCSNGnSJG3cuFFPP/20LrnkEj/vjoh82RtvvKFXXnlFr776qsaMGaN169bppptu0oABA/j6JyIiCvF4eT0KiFJTUxUWFqbS0tIuj5eWliozM9NPuyKi3uy6667TBx98oKVLl2rgwIGdj2dmZqqlpUU1NTVd1v/06z8zM3Ov3x86niOiwGzNmjUqKyvT5MmTFR4ervDwcH3xxRf629/+pvDwcGVkZPD1TxTCZWVl6bDDDuvy2OjRo1VYWCjpx6/h/f1vgMzMTJWVlXV5vq2tTVVVVXwPIArgbr75Zt1222264IILNG7cOF188cX6wx/+oPnz50vi65+oP9VbX+/87wKi4ImhEwVEVqtVU6ZM0ZIlSzofc7vdWrJkiaZOnerHnRGRt5mmqeuuu07vvPOOPv/8c4/b4adMmaKIiIguX/95eXkqLCzs/PqfOnWqNmzY0OUidPHixbLZbB6/zCKiwGnGjBnasGGD1q1b1/nn8MMP10UXXdT593z9E4Vuxx57rPLy8ro8tnXrVuXk5EiShg4dqszMzC7fAxwOh7799tsu3wNqamq0Zs2azjWff/653G63jjrqqD74LIjoYGpsbJTF0vVXTmFhYXK73ZL4+ifqT/XW1/vUqVO1fPlytba2dq5ZvHixRo4cyUvrEQVaJlGA9Nprr5mRkZHmwoULzU2bNplXXXWVmZiYaNrtdn9vjYi86JprrjETEhLMZcuWmSUlJZ1/GhsbO9dcffXV5uDBg83PP//cXL16tTl16lRz6tSpnc+3tbWZY8eONU855RRz3bp15ieffGKmpaWZ8+bN88enREReNG3aNPPGG2/s/Jivf6LQ7bvvvjPDw8PNBx54wNy2bZv5yiuvmDExMeY///nPzjUPPfSQmZiYaL777rvm+vXrzbPPPtscOnSo2dTU1LnmtNNOMydNmmR+++235ldffWUeeuih5oUXXuiPT4mIDrBLLrnEzM7ONj/44ANz165d5ttvv22mpqaat9xyS+cavv6JQqe6ujrz+++/N7///ntTkvnXv/7V/P77782CggLTNHvn672mpsbMyMgwL774YnPjxo3ma6+9ZsbExJjPPPNMn3++RLT/GDpRQPX444+bgwcPNq1Wq3nkkUea33zzjb+3REReJmmvf1588cXONU1NTeb/+3//z0xKSjJjYmLMc845xywpKelynvz8fPP00083o6OjzdTUVPOPf/yj2dra2sefDRF528+HTnz9E4V277//vjl27FgzMjLSHDVqlPnss892ed7tdpt33HGHmZGRYUZGRpozZsww8/LyuqyprKw0L7zwQjMuLs602WzmZZddZtbV1fXlp0FEPczhcJg33nijOXjwYDMqKsocNmyY+ac//cl0Op2da/j6Jwqdli5dutf/3X/JJZeYptl7X+8//PCDedxxx5mRkZFmdna2+dBDD/XVp0hEPcgwTdP0zz1WREREREREREREREREFCrxnk5ERERERERERERERETkdQydiIiIiIiIiIiIiIiIyOsYOhEREREREREREREREZHXMXQiIiIiIiIiIiIiIiIir2PoRERERERERERERERERF7H0ImIiIiIiIiIiIiIiIi8jqETEREREREREREREREReR1DJyIiIiIiIiIiIiIiIvI6hk5EREREREQh1i9+8QtdeeWVPjl3ZWWlYmNj9dFHH/nk/EREREREFLwZpmma/t4EERERERERebZixQp99tlnuummm5SYmHhAx3z99deaNm2atmzZouHDh/tkXzfeeKO++uorrVmzxifnJyIiIiKi4Iw7nYiIiIiIiAK0FStW6J577lFNTc0BH/Pwww9rxowZPhs4SdLVV1+ttWvX6vPPP/eZQUREREREwRdDJyIiIiIiohCprKxMH374oX7961/71Bk9erTGjh2rhQsX+tQhIiIiIqLgiqETERERERFRAHb33Xfr5ptvliQNHTpUhmHIMAzl5+fv85gPP/xQbW1tmjlzZpfHFy5cKMMw9PXXX2vu3LlKS0tTbGyszjnnHJWXl3dZu3r1ap166qlKTU1VdHS0hg4dqssvv9zDOvnkk/X++++LV2wnIiIiIqKOwv29ASIiIiIiIvLs3HPP1datW/Wvf/1LCxYsUGpqqiQpLS1tn8esWLFCKSkpysnJ2evz119/vZKSknTXXXcpPz9fjz76qK677jq9/vrrktrvlDrllFOUlpam2267TYmJicrPz9fbb7/tca4pU6ZowYIFys3N1dixY3vhMyYiIiIiomCPoRMREREREVEANn78eE2ePFn/+te/NHv2bA0ZMqTbY7Zs2bLfdSkpKfrss89kGIYkye12629/+5tqa2uVkJCgFStWqLq6Wp999pkOP/zwzuPuv/9+j3MNGzZMkrRp0yaGTkREREREJImX1yMiIiIiIgqZKisrlZSUtM/nr7rqqs6BkyQdf/zxcrlcKigokCQlJiZKkj744AO1trbu1+pwKir+f3t3zNJWFAZg+GvVSTCDgosoIoLiIgRFp7gaCSIuzg5ODu5OIu5OImQQh0AI/gBBJx0EMZtCJjM5CJUr6OJiOxlqtaX1ttSmz7MlnOQ7d36553xKuWsAAKBZiE4AAABN5Ed3LPX29j77/BSOkiSJiIhcLhfz8/OxtrYWXV1dMTs7Gzs7O/Hw8PDdOV9HLAAA4P8mOgEAADSJzs7ORkB6TUtLy6vffx2Q9vb24uTkJJaXl+Pq6ioWFxcjm83G/f39s988zXm6awoAAEB0AgAAeKd+9S2ioaGhqNfrqedOTEzExsZGnJ2dRalUiouLiyiXy8/WPM0ZHh5OPQ8AAGgOohMAAMA71d7eHhERt7e3P7V+cnIykiSJy8vLN81LkuTF8Xyjo6MRES+O2KtWq5HJZGJkZORNswAAgObT+rc3AAAAwOuy2WxERKyursbCwkK0tbVFoVBoxKhvzczMRGtraxweHsbS0tIvz9vd3Y2tra2Ym5uLgYGBuLu7i2KxGB0dHZHP55+tPTg4iEKh4E4nAACgQXQCAAB4p8bGxmJ9fT22t7djf38/Hh8fo16vfzc6dXd3Rz6fj0ql8qbolMvl4vT0NMrlclxfX0cmk4nx8fEolUrR39/fWFer1eL8/Dw2Nzff+mgAAEAT+vD527MTAAAA+GcdHx/H1NRU1Gq1GBwc/CMzVlZW4ujoKKrVqjedAACABtEJAACgyUxPT0dPT08Ui8Xf/t83NzfR19cXlUrlxZF7AADA/010AgAAAAAAILWPf3sDAAAAAAAA/PtEJwAAAAAAAFITnQAAAAAAAEhNdAIAAAAAACA10QkAAAAAAIDURCcAAAAAAABSE50AAAAAAABITXQCAAAAAAAgNdEJAAAAAACA1EQnAAAAAAAAUvsCYKNlHs0R9fsAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import pulser\n", + "\n", + "sequence = pulser.Sequence(\n", + " pulser.Register({\"q0\": (0, 0)}), pulser.AnalogDevice\n", + ")\n", + "sequence.declare_channel(\"rydberg_global\", \"rydberg_global\")\n", + "pulse = pulser.Pulse.ConstantPulse(1000, 3.14, 0, 0)\n", + "sequence.add(pulse, \"rydberg_global\")\n", + "\n", + "print(sequence)\n", + "sequence.draw()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- With `print(sequence)`, we have access to its contents in text form. This is particularly useful to access the exact timings of each instruction. \n", + "\n", + "
\n", + "\n", + "Tip\n", + "\n", + "To get the text representation of a sequence as a string, e.g. for saving it to a file, call `str(sequence)` instead.\n", + "\n", + "
\n", + "\n", + "\n", + "- With `sequence.draw()`, we see a plot of the channel's contents over time. This method is [highly configurable](apidoc/_autosummary/pulser.sequence.Sequence.rst#pulser.sequence.Sequence.draw), though most of its options are related to features we have not yet covered.\n", + "\n", + "
\n", + "\n", + "**Understanding** `Sequence.draw()`:\n", + "\n", + "You might have noticed that **two overlapping curves appear when you draw the** `Sequence`. These represent the pulse you programmed (shaded in solid color) and the *modulated pulse* we expect to actually reach the atoms (hashed with diagonal lines). You can find an explation for this effect and its implications in [this tutorial](tutorials/output_mod_eom.nblink).\n", + "\n", + "
\n", + "\n", + " " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Runtime Validation\n", + "\n", + "Alongside containing all the information necessary for execution on a backend, the `Sequence`'s main function is to ensure that all its contents respect the [Device specifications](hardware.ipynb).\n", + "\n", + "Here is an example: \n", + "\n", + "- `pulser.AnalogDevice` has a defined `min_atom_distance`, a minimum distance that must be respected between any two atoms in a register;\n", + "- if we create a `Register` where two atoms are closer than this distance, it will not respect the device's constraints;\n", + "- therefore, once they are both given to the `Sequence` it will complain right away, giving us a chance to modify our register or choose a new device before we proceed with the `Sequence` creation." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Failed with error: The minimal distance between atoms in this device (5 µm) is not respected (up to a precision of 1e-6 µm) for the pairs: [('q0', 'q1')]\n" + ] + } + ], + "source": [ + "import pulser\n", + "\n", + "spacing = 2 # The spacing we will use between atoms\n", + "reg = pulser.Register({\"q0\": (0, 0), \"q1\": (spacing, 0)})\n", + "\n", + "# spacing is below the AnalogDevice's specs, so we expect an error\n", + "assert spacing < pulser.AnalogDevice.min_atom_distance\n", + "\n", + "try:\n", + " pulser.Sequence(reg, pulser.AnalogDevice)\n", + "except ValueError as e:\n", + " print(\"Failed with error: \", e)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + "Important\n", + " \n", + "This automatic validation occurs upon every addition to the `Sequence` so that, if something is invalid, it is caught and corrected right away. By the same token, **if a Sequence is constructed without any errors, it is automatically ensured to be valid against the chosen device's constraints**.\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Switching the device" + ] + }, + { + "cell_type": "raw", + "metadata": { + "editable": true, + "raw_mimetype": "text/restructuredtext", + "slideshow": { + "slide_type": "" + }, + "tags": [], + "vscode": { + "languageId": "raw" + } + }, + "source": [ + "Sometimes, one wants to change the device of an already constructed ``Sequence``. Before manually reconstructing the ``Sequence`` with the new device, one can try the :py:class:`~pulser.sequence.Sequence.switch_device()` method: \n", + "\n", + ".. autofunction:: pulser.Sequence.switch_device" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This method attemps to automatically reconstruct the sequence with the new device, which will only succeed if the new sequence is valid on the new device. \n", + "\n", + "
\n", + "\n", + "Attention\n", + " \n", + "This switch may modify the contents of the `Sequence` — to ensure the contents stay unchanged, set `strict=True`.\n", + "\n", + "
" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Backend-specific validation\n", + "\n", + "There are a handful of backend-specific constraints that are not enforced during sequence construction. This is because execution on a [QPU enforces additional restrictions](tutorials/backends.nblink#1.2.-Preparation-for-execution-on-QPUBackend) that don't directly affect the programmed Hamiltonian, so they are not enforced by default. Nonetheless, **when using an emulator to fully mimic the QPU execution process, all the QPU constraints can be enabled via the** `mimic_qpu` **argument**." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/source/tutorials/1D_crystals.nblink b/docs/source/tutorials/1D_crystals.nblink deleted file mode 100644 index 40da06e50..000000000 --- a/docs/source/tutorials/1D_crystals.nblink +++ /dev/null @@ -1,3 +0,0 @@ -{ - "path": "../../../tutorials/quantum_simulation/Building 1D Rydberg Crystals.ipynb" -} diff --git a/docs/source/tutorials/afm_prep.nblink b/docs/source/tutorials/afm_prep.nblink deleted file mode 100644 index 22103200b..000000000 --- a/docs/source/tutorials/afm_prep.nblink +++ /dev/null @@ -1,3 +0,0 @@ -{ - "path": "../../../tutorials/quantum_simulation/Preparing state with antiferromagnetic order in the Ising model.ipynb" -} diff --git a/docs/source/tutorials/cz_gate.nblink b/docs/source/tutorials/cz_gate.nblink deleted file mode 100644 index 12057d48b..000000000 --- a/docs/source/tutorials/cz_gate.nblink +++ /dev/null @@ -1,3 +0,0 @@ -{ - "path": "../../../tutorials/applications/Control-Z Gate Sequence.ipynb" -} diff --git a/docs/source/tutorials/mw_engineering.nblink b/docs/source/tutorials/mw_engineering.nblink deleted file mode 100644 index 9b5caebdd..000000000 --- a/docs/source/tutorials/mw_engineering.nblink +++ /dev/null @@ -1,3 +0,0 @@ -{ - "path": "../../../tutorials/quantum_simulation/Microwave-engineering of programmable XXZ Hamiltonians in arrays of Rydberg atoms.ipynb" -} diff --git a/docs/source/tutorials/shadow_est.nblink b/docs/source/tutorials/shadow_est.nblink deleted file mode 100644 index 401a03802..000000000 --- a/docs/source/tutorials/shadow_est.nblink +++ /dev/null @@ -1,3 +0,0 @@ -{ - "path": "../../../tutorials/quantum_simulation/Shadow estimation for VQS.ipynb" -} diff --git a/docs/source/tutorials/simulating.nblink b/docs/source/tutorials/simulating.nblink deleted file mode 100644 index 726f4183e..000000000 --- a/docs/source/tutorials/simulating.nblink +++ /dev/null @@ -1,3 +0,0 @@ -{ - "path": "../../../tutorials/simulating_sequences.ipynb" -} diff --git a/pulser-core/pulser/__init__.py b/pulser-core/pulser/__init__.py index 2c5931c8a..9c467ce92 100644 --- a/pulser-core/pulser/__init__.py +++ b/pulser-core/pulser/__init__.py @@ -45,6 +45,8 @@ backends as backends, ) +# NOTE: If any of these change, remember to MANUALLY replicate them in the +# API reference doc (ie they are not updated automatically). __all__ = [ # pulser.waveforms "CompositeWaveform", diff --git a/pulser-core/pulser/abstract_repr.py b/pulser-core/pulser/abstract_repr.py new file mode 100644 index 000000000..ca13e4062 --- /dev/null +++ b/pulser-core/pulser/abstract_repr.py @@ -0,0 +1,38 @@ +# Copyright 2024 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. +"""Convenience functions for deserialization from the abstract sequence.""" + +from pulser.json.abstract_repr.deserializer import ( + deserialize_abstract_layout as deserialize_layout, +) +from pulser.json.abstract_repr.deserializer import ( + deserialize_abstract_noise_model as deserialize_noise_model, +) +from pulser.json.abstract_repr.deserializer import ( + deserialize_abstract_register as deserialize_register, +) +from pulser.json.abstract_repr.deserializer import ( + deserialize_abstract_sequence as deserialize_sequence, +) +from pulser.json.abstract_repr.deserializer import ( + deserialize_device as deserialize_device, +) + +__all__ = [ + "deserialize_layout", + "deserialize_noise_model", + "deserialize_register", + "deserialize_sequence", + "deserialize_device", +] diff --git a/pulser-core/pulser/backend/__init__.py b/pulser-core/pulser/backend/__init__.py index f4f9361b3..5d3645e9f 100644 --- a/pulser-core/pulser/backend/__init__.py +++ b/pulser-core/pulser/backend/__init__.py @@ -15,7 +15,7 @@ import pulser.noise_model as noise_model # For backwards compat from pulser.backend.config import EmulatorConfig -from pulser.noise_model import NoiseModel # For backwards compat +from pulser.noise_model import NoiseModel as NoiseModel # For backwards compat from pulser.backend.qpu import QPUBackend -__all__ = ["EmulatorConfig", "NoiseModel", "QPUBackend"] +__all__ = ["EmulatorConfig", "QPUBackend"] diff --git a/pulser-core/pulser/backend/config.py b/pulser-core/pulser/backend/config.py index 482fd29ff..afa983c44 100644 --- a/pulser-core/pulser/backend/config.py +++ b/pulser-core/pulser/backend/config.py @@ -28,7 +28,7 @@ class BackendConfig: """The base backend configuration. - Attributes: + Args: backend_options: A dictionary of backend specific options. """ @@ -39,7 +39,7 @@ class BackendConfig: class EmulatorConfig(BackendConfig): """The configuration for emulator backends. - Attributes: + Args: backend_options: A dictionary of backend-specific options. sampling_rate: The fraction of samples to extract from the pulse sequence for emulation. diff --git a/pulser-core/pulser/backends.py b/pulser-core/pulser/backends.py index c32a915aa..f3b93660d 100644 --- a/pulser-core/pulser/backends.py +++ b/pulser-core/pulser/backends.py @@ -11,16 +11,32 @@ # 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. -"""A module gathering all available backends.""" +"""A module gathering all available backends. + +This module is a single-point access to backends spread across different +packages. As long as the appropriate package is installed, the ``Backend`` +instances defined within it should be importable via this module, like so:: + + import pulser.backends as backends + + backends.QPUBackend # Same as pulser.QPUBackend + backends.QutipBackend # Same as pulser_simulation.QutipBackend + +Attributes: + QPUBackend: See :py:class:`pulser.backend.QPUBackend`. + QutipBackend: See :py:class:`pulser_simulation.QutipBackend`. + EmuFreeBackend: See :py:class:`pulser_pasqal.EmuFreeBackend`. + EmuTNBackend: See :py:class:`pulser_pasqal.EmuTNBackend`. + +""" from __future__ import annotations import importlib from typing import TYPE_CHECKING, Type -from pulser.backend.abc import Backend - if TYPE_CHECKING: from pulser.backend import QPUBackend as QPUBackend + from pulser.backend.abc import Backend from pulser_pasqal import EmuFreeBackend as EmuFreeBackend from pulser_pasqal import EmuTNBackend as EmuTNBackend from pulser_simulation import QutipBackend as QutipBackend diff --git a/pulser-core/pulser/channels/__init__.py b/pulser-core/pulser/channels/__init__.py index 84be8e9f8..469be51af 100644 --- a/pulser-core/pulser/channels/__init__.py +++ b/pulser-core/pulser/channels/__init__.py @@ -15,5 +15,14 @@ from pulser.channels.channels import Microwave, Raman, Rydberg from pulser.channels.dmm import DMM +from pulser.channels.eom import BaseEOM, RydbergEOM, RydbergBeam -__all__ = ["Microwave", "Raman", "Rydberg", "DMM"] +__all__ = [ + "Microwave", + "Raman", + "Rydberg", + "DMM", + "BaseEOM", + "RydbergEOM", + "RydbergBeam", +] diff --git a/pulser-core/pulser/channels/base_channel.py b/pulser-core/pulser/channels/base_channel.py index f3fb11433..a90219d6e 100644 --- a/pulser-core/pulser/channels/base_channel.py +++ b/pulser-core/pulser/channels/base_channel.py @@ -663,6 +663,7 @@ def __str__(self) -> str: config += f", Maximum pulse duration: {self.max_duration} ns" if self.mod_bandwidth: config += f", Modulation Bandwidth: {self.mod_bandwidth} MHz" + config += f", Supports EOM: {self.supports_eom()}" config += f", Basis: '{self.basis}')" return self.name + config diff --git a/pulser-core/pulser/channels/eom.py b/pulser-core/pulser/channels/eom.py index 0db609ffd..8b004a127 100644 --- a/pulser-core/pulser/channels/eom.py +++ b/pulser-core/pulser/channels/eom.py @@ -70,7 +70,7 @@ class _BaseEOMDefaults: class BaseEOM(_BaseEOMDefaults, _BaseEOM): """A base class for the EOM configuration. - Attributes: + Args: mod_bandwidth: The EOM modulation bandwidth at -3dB (50% reduction), in MHz. custom_buffer_time: A custom wait time to enforce during EOM buffers. @@ -146,7 +146,7 @@ class _RydbergEOMDefaults: class RydbergEOM(_RydbergEOMDefaults, BaseEOM, _RydbergEOM): """The EOM configuration for a Rydberg channel. - Attributes: + Args: limiting_beam: The beam with the smallest amplitude range. max_limiting_amp: The maximum amplitude the limiting beam can reach, in rad/µs. diff --git a/pulser-core/pulser/devices/__init__.py b/pulser-core/pulser/devices/__init__.py index 68ef7ce1c..64acdcecf 100644 --- a/pulser-core/pulser/devices/__init__.py +++ b/pulser-core/pulser/devices/__init__.py @@ -11,7 +11,16 @@ # 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. -"""Valid devices for Pulser Sequence execution.""" +"""Classes for specification of neutral-atom devices. + +The :class:`Device` class sets the structure of a physical device, +while :class:`VirtualDevice` is a more permissive device type which can +only be used in emulators, as it does not necessarily represent the +constraints of a physical device. +Illustrative instances of :class:`Device` (e.g. :class:`AnalogDevice`) and +:class:`VirtualDevice` (the :class:`MockDevice`) come included in the +module. +""" from __future__ import annotations diff --git a/pulser-core/pulser/devices/_device_datacls.py b/pulser-core/pulser/devices/_device_datacls.py index 01b0181d0..cc6581544 100644 --- a/pulser-core/pulser/devices/_device_datacls.py +++ b/pulser-core/pulser/devices/_device_datacls.py @@ -64,37 +64,39 @@ class BaseDevice(ABC): r"""Base class of a neutral-atom device. - Attributes: + Args: name: The name of the device. dimensions: Whether it supports 2D or 3D arrays. - channel_objects: The Channel subclass instances specifying each - channel in the device. - channel_ids: Custom IDs for each channel object. When defined, - an ID must be given for each channel. If not defined, the IDs are - generated internally based on the channels' names and addressing. - dmm_objects: The DMM subclass instances specifying each channel in the - device. They are referenced by their order in the list, with the ID - "dmm_[index in dmm_objects]". - rydberg_level: The value of the principal quantum number :math:`n` - when the Rydberg level used is of the form - :math:`|nS_{1/2}, m_j = +1/2\rangle`. max_atom_num: Maximum number of atoms supported in an array. max_radial_distance: The furthest away an atom can be from the center of the array (in μm). min_atom_distance: The closest together two atoms can be (in μm). + requires_layout: Whether the register used in the sequence must be + created from a register layout. Only enforced in QPU execution. + min_layout_traps: The minimum number of traps a layout can have. + max_layout_traps: An optional value for the maximum number of traps a + layout can have. + max_layout_filling: The largest fraction of a layout that can be filled + with atoms. + optimal_layout_filling: An optional value for the fraction of a layout + that should be filled with atoms. + rydberg_level: The value of the principal quantum number :math:`n` + when the Rydberg level used is of the form + :math:`|nS_{1/2}, m_j = +1/2\rangle`. interaction_coeff_xy: :math:`C_3/\hbar` (in :math:`rad \cdot \mu s^{-1} \cdot \mu m^3`), which sets the van der Waals interaction strength between atoms in different Rydberg states. Needed only if there is a Microwave channel in the device. If unsure, 3700.0 is a good default value. + channel_objects: The Channel subclass instances specifying each + channel in the device. + channel_ids: Custom IDs for each channel object. When defined, + an ID must be given for each channel. If not defined, the IDs are + generated internally based on the channels' names and addressing. + dmm_objects: The DMM subclass instances specifying each channel in the + device. They are referenced by their order in the list, with the ID + "dmm_[index in dmm_objects]". supports_slm_mask: Whether the device supports the SLM mask feature. - max_layout_filling: The largest fraction of a layout that can be filled - with atoms. - optimal_layout_filling: An optional value for the fraction of a layout - that should be filled with atoms. - min_layout_traps: The minimum number of traps a layout can have. - max_layout_traps: An optional value for the maximum number of traps a - layout can have. max_sequence_duration: The maximum allowed duration for a sequence (in ns). max_runs: The maximum number of runs allowed on the device. Only used @@ -102,8 +104,6 @@ class BaseDevice(ABC): default_noise_model: An optional noise model characterizing the default noise of the device. Can be used by emulator backends that support noise. - requires_layout: Whether the register used in the sequence must be - created from a register layout. Only enforced in QPU execution. """ name: str @@ -126,6 +126,7 @@ class BaseDevice(ABC): channel_objects: tuple[Channel, ...] = field(default_factory=tuple) dmm_objects: tuple[DMM, ...] = field(default_factory=tuple) default_noise_model: NoiseModel | None = None + short_description: str = field(default="", repr=False, compare=False) def __post_init__(self) -> None: def type_check( @@ -292,6 +293,8 @@ def type_check( if self.default_noise_model is not None: type_check("default_noise_model", NoiseModel) + type_check("short_description", str) + def to_tuple(obj: tuple | list) -> tuple: if isinstance(obj, (tuple, list)): obj = tuple(to_tuple(el) for el in obj) @@ -302,6 +305,9 @@ def to_tuple(obj: tuple | list) -> tuple: if "channel" in param or param == "dmm_objects": object.__setattr__(self, param, to_tuple(getattr(self, param))) + # Hack to override the docstring of an instance + object.__setattr__(self, "__doc__", self._specs(for_docs=True)) + @property @abstractmethod def _optional_parameters(self) -> tuple[str, ...]: @@ -524,7 +530,7 @@ def _params(self, init_only: bool = False) -> dict[str, Any]: return { f.name: getattr(self, f.name) for f in fields(self) - if not init_only or f.init + if (not init_only or f.init) and f.name != "short_description" } def _validate_coords( @@ -615,7 +621,6 @@ def _register_lines(self) -> list[str]: register_lines = [ "\nRegister parameters:", f" - Dimensions: {self.dimensions}D", - f" - Rydberg level: {self.rydberg_level}", self._param_check_none(self.max_atom_num)( " - Maximum number of atoms: {}" ), @@ -624,7 +629,6 @@ def _register_lines(self) -> list[str]: ), " - Minimum distance between neighbouring atoms: " + f"{self.min_atom_distance} μm", - f" - SLM Mask: {self._param_yes_no(self.supports_slm_mask)}", ] return [line for line in register_lines if line != ""] @@ -647,21 +651,23 @@ def _device_lines(self) -> list[str]: device_lines = [ "\nDevice parameters:", - self._param_check_none(self.max_runs)( - " - Maximum number of runs: {}" + f" - Rydberg level: {self.rydberg_level}", + self._param_check_none(self.interaction_coeff)( + " - Ising interaction coefficient: {}", ), - self._param_check_none(self.max_sequence_duration)( - " - Maximum sequence duration: {} ns", + self._param_check_none(self.interaction_coeff_xy)( + " - XY interaction coefficient: {}", ), " - Channels can be reused: " + self._param_yes_no(self.reusable_channels), f" - Supported bases: {', '.join(self.supported_bases)}", f" - Supported states: {', '.join(self.supported_states)}", - self._param_check_none(self.interaction_coeff)( - " - Ising interaction coefficient: {}", + f" - SLM Mask: {self._param_yes_no(self.supports_slm_mask)}", + self._param_check_none(self.max_sequence_duration)( + " - Maximum sequence duration: {} ns", ), - self._param_check_none(self.interaction_coeff_xy)( - " - XY interaction coefficient: {}", + self._param_check_none(self.max_runs)( + " - Maximum number of runs: {}" ), self._param_check_none(self.default_noise_model)( " - Default noise model: {}", @@ -728,7 +734,8 @@ def _channel_lines(self, for_docs: bool = False) -> list[str]: def _specs(self, for_docs: bool = False) -> str: return "\n".join( - self._register_lines() + ([self.short_description] if self.short_description else []) + + self._register_lines() + self._layout_lines() + self._device_lines() + self._channel_lines(for_docs=for_docs) @@ -739,34 +746,27 @@ def _specs(self, for_docs: bool = False) -> str: class Device(BaseDevice): r"""Specifications of a neutral-atom device. - A Device instance is immutable and must have all of its parameters defined. - For usage in emulations, it can be converted to a VirtualDevice through the - `Device.to_virtual()` method. + Each ``Device`` instance holds the characteristics of a physical device, + which when associated with a :class:`pulser.Sequence` condition its + development. + + Note: + A Device instance is immutable and must have all of its parameters + defined. For more unconstrained usage in emulations, it can be + converted to a VirtualDevice through the `Device.to_virtual()` method.` - Attributes: + Args: name: The name of the device. dimensions: Whether it supports 2D or 3D arrays. - channel_objects: The Channel subclass instances specifying each - channel in the device. - channel_ids: Custom IDs for each channel object. When defined, - an ID must be given for each channel. If not defined, the IDs are - generated internally based on the channels' names and addressing. - dmm_objects: The DMM subclass instances specifying each channel in the - device. They are referenced by their order in the list, with the ID - "dmm_[index in dmm_objects]". - rydberg_level: The value of the principal quantum number :math:`n` - when the Rydberg level used is of the form - :math:`|nS_{1/2}, m_j = +1/2\rangle`. max_atom_num: Maximum number of atoms supported in an array. max_radial_distance: The furthest away an atom can be from the center of the array (in μm). min_atom_distance: The closest together two atoms can be (in μm). - interaction_coeff_xy: :math:`C_3/\hbar` - (in :math:`rad \cdot \mu s^{-1} \cdot \mu m^3`), - which sets the van der Waals interaction strength between atoms in - different Rydberg states. Needed only if there is a Microwave - channel in the device. If unsure, 3700.0 is a good default value. - supports_slm_mask: Whether the device supports the SLM mask feature. + requires_layout: Whether the register used in the sequence must be + created from a register layout. Only enforced in QPU execution. + accepts_new_layouts: Whether registers built from register layouts + that are not already calibrated are accepted. Only enforced in + QPU execution. max_layout_filling: The largest fraction of a layout that can be filled with atoms. optimal_layout_filling: An optional value for the fraction of a layout @@ -774,6 +774,25 @@ class Device(BaseDevice): min_layout_traps: The minimum number of traps a layout can have. max_layout_traps: An optional value for the maximum number of traps a layout can have. + pre_calibrated_layouts: RegisterLayout instances that are already + available on the Device. + rydberg_level: The value of the principal quantum number :math:`n` + when the Rydberg level used is of the form + :math:`|nS_{1/2}, m_j = +1/2\rangle`. + interaction_coeff_xy: :math:`C_3/\hbar` + (in :math:`rad \cdot \mu s^{-1} \cdot \mu m^3`), + which sets the van der Waals interaction strength between atoms in + different Rydberg states. Needed only if there is a Microwave + channel in the device. If unsure, 3700.0 is a good default value. + channel_objects: The Channel subclass instances specifying each + channel in the device. + channel_ids: Custom IDs for each channel object. When defined, + an ID must be given for each channel. If not defined, the IDs are + generated internally based on the channels' names and addressing. + dmm_objects: The DMM subclass instances specifying each channel in the + device. They are referenced by their order in the list, with the ID + "dmm_[index in dmm_objects]". + supports_slm_mask: Whether the device supports the SLM mask feature. max_sequence_duration: The maximum allowed duration for a sequence (in ns). max_runs: The maximum number of runs allowed on the device. Only used @@ -781,13 +800,6 @@ class Device(BaseDevice): default_noise_model: An optional noise model characterizing the default noise of the device. Can be used by emulator backends that support noise. - requires_layout: Whether the register used in the sequence must be - created from a register layout. Only enforced in QPU execution. - pre_calibrated_layouts: RegisterLayout instances that are already - available on the Device. - accepts_new_layouts: Whether registers built from register layouts - that are not already calibrated are accepted. Only enforced in - QPU execution. """ max_atom_num: int @@ -810,8 +822,6 @@ def __post_init__(self) -> None: ) for layout in self.pre_calibrated_layouts: self.validate_layout(layout) - # Hack to override the docstring of an instance - object.__setattr__(self, "__doc__", self._specs(for_docs=True)) @property def _optional_parameters(self) -> tuple[str, ...]: @@ -930,30 +940,15 @@ class VirtualDevice(BaseDevice): to be declared multiple times in the same Sequence (when `reusable_channels=True`) and allows the Rydberg level to be changed. - Attributes: + Args: name: The name of the device. dimensions: Whether it supports 2D or 3D arrays. - channel_objects: The Channel subclass instances specifying each - channel in the device. - channel_ids: Custom IDs for each channel object. When defined, - an ID must be given for each channel. If not defined, the IDs are - generated internally based on the channels' names and addressing. - dmm_objects: The DMM subclass instances specifying each channel in the - device. They are referenced by their order in the list, with the ID - "dmm_[index in dmm_objects]". - rydberg_level: The value of the principal quantum number :math:`n` - when the Rydberg level used is of the form - :math:`|nS_{1/2}, m_j = +1/2\rangle`. max_atom_num: Maximum number of atoms supported in an array. max_radial_distance: The furthest away an atom can be from the center of the array (in μm). min_atom_distance: The closest together two atoms can be (in μm). - interaction_coeff_xy: :math:`C_3/\hbar` - (in :math:`rad \cdot \mu s^{-1} \cdot \mu m^3`), - which sets the van der Waals interaction strength between atoms in - different Rydberg states. Needed only if there is a Microwave - channel in the device. If unsure, 3700.0 is a good default value. - supports_slm_mask: Whether the device supports the SLM mask feature. + requires_layout: Whether the register used in the sequence must be + created from a register layout. Only enforced in QPU execution. max_layout_filling: The largest fraction of a layout that can be filled with atoms. optimal_layout_filling: An optional value for the fraction of a layout @@ -961,6 +956,25 @@ class VirtualDevice(BaseDevice): min_layout_traps: The minimum number of traps a layout can have. max_layout_traps: An optional value for the maximum number of traps a layout can have. + rydberg_level: The value of the principal quantum number :math:`n` + when the Rydberg level used is of the form + :math:`|nS_{1/2}, m_j = +1/2\rangle`. + interaction_coeff_xy: :math:`C_3/\hbar` + (in :math:`rad \cdot \mu s^{-1} \cdot \mu m^3`), + which sets the van der Waals interaction strength between atoms in + different Rydberg states. Needed only if there is a Microwave + channel in the device. If unsure, 3700.0 is a good default value. + reusable_channels: Whether each channel can be declared multiple times + on the same pulse sequence. + channel_objects: The Channel subclass instances specifying each + channel in the device. + channel_ids: Custom IDs for each channel object. When defined, + an ID must be given for each channel. If not defined, the IDs are + generated internally based on the channels' names and addressing. + dmm_objects: The DMM subclass instances specifying each channel in the + device. They are referenced by their order in the list, with the ID + "dmm_[index in dmm_objects]". + supports_slm_mask: Whether the device supports the SLM mask feature. max_sequence_duration: The maximum allowed duration for a sequence (in ns). max_runs: The maximum number of runs allowed on the device. Only used @@ -968,10 +982,6 @@ class VirtualDevice(BaseDevice): default_noise_model: An optional noise model characterizing the default noise of the device. Can be used by emulator backends that support noise. - requires_layout: Whether the register used in the sequence must be - created from a register layout. Only enforced in QPU execution. - reusable_channels: Whether each channel can be declared multiple times - on the same pulse sequence. """ min_atom_distance: float = 0 @@ -982,6 +992,9 @@ class VirtualDevice(BaseDevice): dmm_objects: tuple[DMM, ...] = (DMM(),) reusable_channels: bool = True + def __post_init__(self) -> None: + super().__post_init__() + @property def _optional_parameters(self) -> tuple[str, ...]: return ("max_atom_num", "max_radial_distance") diff --git a/pulser-core/pulser/devices/_devices.py b/pulser-core/pulser/devices/_devices.py index 5f7929daa..8cd5bc658 100644 --- a/pulser-core/pulser/devices/_devices.py +++ b/pulser-core/pulser/devices/_devices.py @@ -65,6 +65,7 @@ total_bottom_detuning=-2 * np.pi * 2000, ), ), + short_description="A device with digital and analog capabilites.", ) AnalogDevice = Device( @@ -97,4 +98,5 @@ ), ), pre_calibrated_layouts=(TriangularLatticeLayout(61, 5),), + short_description="A realistic device for analog sequence execution.", ) diff --git a/pulser-core/pulser/devices/_mock_device.py b/pulser-core/pulser/devices/_mock_device.py index f9092d7c6..0158e8b2e 100644 --- a/pulser-core/pulser/devices/_mock_device.py +++ b/pulser-core/pulser/devices/_mock_device.py @@ -32,4 +32,5 @@ Microwave.Global(None, None, max_duration=None), ), dmm_objects=(DMM(),), + short_description="A virtual device for unconstrained prototyping.", ) diff --git a/pulser-core/pulser/json/abstract_repr/deserializer.py b/pulser-core/pulser/json/abstract_repr/deserializer.py index d4bcfd2a1..e410e0b94 100644 --- a/pulser-core/pulser/json/abstract_repr/deserializer.py +++ b/pulser-core/pulser/json/abstract_repr/deserializer.py @@ -597,7 +597,7 @@ def deserialize_device(obj_str: str) -> Device | VirtualDevice: Raises: DeserializeDeviceError: Whenever the device deserialization - fails due to an invalid 'obj_str'. + fails due to an invalid 'obj_str'. """ if not isinstance(obj_str, str): type_error = TypeError( diff --git a/pulser-core/pulser/register/__init__.py b/pulser-core/pulser/register/__init__.py index 5eb20397b..b915255cc 100644 --- a/pulser-core/pulser/register/__init__.py +++ b/pulser-core/pulser/register/__init__.py @@ -13,7 +13,7 @@ # limitations under the License. """Classes for qubit register definition.""" -from pulser.register.base_register import QubitId +from pulser.register.base_register import QubitId as QubitId from pulser.register.register import Register from pulser.register.register3d import Register3D from pulser.register.register_layout import RegisterLayout @@ -22,13 +22,14 @@ TriangularLatticeLayout, RectangularLatticeLayout, ) +from pulser.register.weight_maps import DetuningMap __all__ = [ - "QubitId", "Register", "Register3D", "RegisterLayout", "SquareLatticeLayout", "TriangularLatticeLayout", "RectangularLatticeLayout", + "DetuningMap", ] diff --git a/pulser-core/pulser/register/base_register.py b/pulser-core/pulser/register/base_register.py index ef73e43ac..956455acf 100644 --- a/pulser-core/pulser/register/base_register.py +++ b/pulser-core/pulser/register/base_register.py @@ -243,7 +243,7 @@ def define_detuning_map( Returns: A DetuningMap associating detuning weights to the trap coordinates - of the targeted qubits. + of the targeted qubits. """ if not set(detuning_weights.keys()) <= set(self.qubit_ids): raise ValueError( diff --git a/pulser-core/pulser/register/mappable_reg.py b/pulser-core/pulser/register/mappable_reg.py index 47c6ace10..130187d3c 100644 --- a/pulser-core/pulser/register/mappable_reg.py +++ b/pulser-core/pulser/register/mappable_reg.py @@ -137,7 +137,7 @@ def define_detuning_map( Returns: A DetuningMap associating detuning weights to the trap coordinates - of the targeted traps. + of the targeted traps. """ return self._layout.define_detuning_map(detuning_weights, slug) diff --git a/pulser-core/pulser/register/register.py b/pulser-core/pulser/register/register.py index 0f0be2fd8..27fa36102 100644 --- a/pulser-core/pulser/register/register.py +++ b/pulser-core/pulser/register/register.py @@ -37,6 +37,7 @@ if TYPE_CHECKING: from pulser.devices import Device + from pulser.devices._device_datacls import BaseDevice class Register(BaseRegister, RegDrawer): @@ -70,7 +71,7 @@ def square( spacing: float | pm.TensorLike = 4.0, prefix: Optional[str] = None, ) -> Register: - """Initializes the register with the qubits in a square array. + """Creates the register with the qubits in a square array. Args: side: Side of the square in number of qubits. @@ -173,7 +174,7 @@ def triangular_lattice( spacing: float | pm.TensorLike = 4.0, prefix: Optional[str] = None, ) -> Register: - """Initializes the register with the qubits in a triangular lattice. + """Creates the register with the qubits in a triangular lattice. Initializes the qubits in a triangular lattice pattern, more specifically a triangular lattice with horizontal rows, meaning the @@ -226,7 +227,7 @@ def hexagon( spacing: float | pm.TensorLike = 4.0, prefix: Optional[str] = None, ) -> Register: - """Initializes the register with the qubits in a hexagonal layout. + """Creates the register with the qubits in a hexagonal layout. Args: layers: Number of layers around a central atom. @@ -262,7 +263,7 @@ def hexagon( def max_connectivity( cls, n_qubits: int, - device: pulser.devices._device_datacls.BaseDevice, + device: BaseDevice, spacing: float | pm.TensorLike | None = None, prefix: str | None = None, ) -> Register: diff --git a/pulser-core/pulser/register/register3d.py b/pulser-core/pulser/register/register3d.py index 1cf246212..75eeaee53 100644 --- a/pulser-core/pulser/register/register3d.py +++ b/pulser-core/pulser/register/register3d.py @@ -155,8 +155,8 @@ def to_2D(self, tol_width: float = 0.0) -> Register: """Converts a Register3D into a Register (if possible). Args: - tol_width: The allowed transverse width of - the register to be projected. + tol_width: The allowed transverse width of the register to be + projected. Returns: Returns a 2D register with the coordinates of the atoms diff --git a/pulser-core/pulser/register/register_layout.py b/pulser-core/pulser/register/register_layout.py index 7ea41e5ca..acb817169 100644 --- a/pulser-core/pulser/register/register_layout.py +++ b/pulser-core/pulser/register/register_layout.py @@ -42,9 +42,17 @@ class RegisterLayout(Traps, RegDrawer): """A layout of traps out of which registers can be defined. - The traps are always sorted under the same convention: ascending order - along x, then along y, then along z (if applicable). Respecting this order, - the traps are then numbered starting from 0. + A ``RegisterLayout`` is used to define a register from a set of traps. It + is intended to be given to the user by the hardware provider as a way of + showing which layouts are already available on a given device. In turn, + the user can create a ``Register`` by selecting the traps on which to place + atoms, or even a ``MappableRegister``, which allows for the creation of + sequences whose register can be defined at build time. + + Note: + The traps are always sorted under the same convention: ascending order + along x, then along y, then along z (if applicable). Respecting this + order, the traps are then numbered starting from 0. Args: trap_coordinates: The trap coordinates defining the layout. @@ -118,7 +126,7 @@ def define_detuning_map( Returns: A DetuningMap associating detuning weights to the trap coordinates - of the targeted traps. + of the targeted traps. """ if not set(detuning_weights.keys()) <= set(self.traps_dict): raise ValueError( diff --git a/pulser-core/pulser/register/weight_maps.py b/pulser-core/pulser/register/weight_maps.py index d0c67f241..f1f2f30a9 100644 --- a/pulser-core/pulser/register/weight_maps.py +++ b/pulser-core/pulser/register/weight_maps.py @@ -180,8 +180,10 @@ def _to_abstract_repr(self) -> dict[str, Any]: class DetuningMap(WeightMap): """Defines a DetuningMap. - A DetuningMap associates a detuning weight (a value between 0 and 1) - to the coordinates of a trap. + A ``DetuningMap`` is associated to a ``DMM`` in a ``Sequence``. It links a + set of weights to a set of trap coordinates. It is intended to be defined + by the user from a ``RegisterLayout``, a ``Register`` or a + ``MappableRegister`` using ``define_detuning_map``. Args: trap_coordinates: An array containing the coordinates of the traps. diff --git a/pulser-core/pulser/sampler/__init__.py b/pulser-core/pulser/sampler/__init__.py index 1c71f03ac..e28f3a8b1 100644 --- a/pulser-core/pulser/sampler/__init__.py +++ b/pulser-core/pulser/sampler/__init__.py @@ -17,4 +17,12 @@ The samples of a sequence are organized in channels and are used for plotting and simulation. """ -from pulser.sampler.sampler import sample as sample +from pulser.sampler.sampler import sample +from pulser.sampler.samples import ChannelSamples, DMMSamples, SequenceSamples + +__all__ = [ + "sample", + "ChannelSamples", + "DMMSamples", + "SequenceSamples", +] diff --git a/pulser-core/pulser/sequence/__init__.py b/pulser-core/pulser/sequence/__init__.py index 955653a88..93da144e1 100644 --- a/pulser-core/pulser/sequence/__init__.py +++ b/pulser-core/pulser/sequence/__init__.py @@ -13,4 +13,8 @@ # limitations under the License. """Module containing the sequence class definition.""" -from pulser.sequence.sequence import Sequence as Sequence +from pulser.sequence.sequence import Sequence + +__all__ = [ + "Sequence", +] diff --git a/pulser-core/pulser/sequence/sequence.py b/pulser-core/pulser/sequence/sequence.py index 0f1a213dd..3a87763a8 100644 --- a/pulser-core/pulser/sequence/sequence.py +++ b/pulser-core/pulser/sequence/sequence.py @@ -82,7 +82,7 @@ class Sequence(Generic[DeviceType]): A sequence is composed by - - The device in which we want to implement it + - The device constraints it must respect - The register of qubits on which to act - The device's channels that are used - The schedule of operations on each channel @@ -104,8 +104,7 @@ class Sequence(Generic[DeviceType]): register: The atom register on which to apply the pulses. If given as a MappableRegister instance, the traps corrresponding to each qubit ID must be given when building the sequence. - device: A valid device in which to execute the Sequence (import - it from ``pulser.devices``). + device: A valid device in which to execute the Sequence. Note: The register and device do not support variable parameters. As such, @@ -685,9 +684,9 @@ def switch_register( Warns: UserWarning: If the sequence is configuring a detuning map, a - warning is raised to remind the user that the detuning map is - unchanged and might no longer be aligned with the qubits in - the new register. + warning is raised to remind the user that the detuning map is + unchanged and might no longer be aligned with the qubits in + the new register. Args: new_register: The new register to give the sequence. @@ -726,8 +725,7 @@ def switch_device( guarantee the pulse sequence is left unchanged. Returns: - The sequence on the new device, using the match channels of - the former device declared in the sequence. + The sequence on the new device. """ return switch_device(self, new_device, strict) @@ -738,7 +736,7 @@ def declare_channel( channel_id: str, initial_target: Optional[Union[QubitId, Collection[QubitId]]] = None, ) -> None: - """Declares a new channel to the Sequence. + """Declares a new channel in the Sequence. The first declared channel implicitly defines the sequence's mode of operation (i.e. the underlying Hamiltonian). In particular, if the @@ -749,7 +747,8 @@ def declare_channel( Note: Regular devices only allow a channel to be declared once, but - ``MockDevice`` channels can be repeatedly declared if needed. + channels in ``VirtualDevice`` with ``reusable_channels=True`` + can be repeatedly declared if needed. Args: name: Unique name for the channel in the sequence. diff --git a/pulser-core/pulser/waveforms.py b/pulser-core/pulser/waveforms.py index 6b029011e..08b1684b6 100644 --- a/pulser-core/pulser/waveforms.py +++ b/pulser-core/pulser/waveforms.py @@ -789,7 +789,7 @@ def __mul__(self, other: float | ArrayLike) -> BlackmanWaveform: class InterpolatedWaveform(Waveform): - """Creates a waveform from interpolation of a set of data points. + """A waveform created from interpolation of a set of data points. Args: duration: The waveform duration (in ns). diff --git a/pulser-pasqal/pulser_pasqal/pasqal_cloud.py b/pulser-pasqal/pulser_pasqal/pasqal_cloud.py index 7a4797b39..f66e1bd09 100644 --- a/pulser-pasqal/pulser_pasqal/pasqal_cloud.py +++ b/pulser-pasqal/pulser_pasqal/pasqal_cloud.py @@ -27,6 +27,7 @@ ) from pulser import Sequence +from pulser.abstract_repr import deserialize_device from pulser.backend.config import EmulatorConfig from pulser.backend.qpu import QPUBackend from pulser.backend.remote import ( @@ -38,7 +39,6 @@ RemoteResultsError, ) from pulser.devices import Device -from pulser.json.abstract_repr.deserializer import deserialize_device from pulser.json.utils import make_json_compatible from pulser.result import Result, SampledResult diff --git a/pulser-simulation/pulser_simulation/__init__.py b/pulser-simulation/pulser_simulation/__init__.py index cddf5e04f..442df6903 100644 --- a/pulser-simulation/pulser_simulation/__init__.py +++ b/pulser-simulation/pulser_simulation/__init__.py @@ -11,7 +11,7 @@ # 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.""" +"""Classes for classical emulation of a Pulser Sequence.""" from pulser import EmulatorConfig, NoiseModel @@ -20,6 +20,8 @@ from pulser_simulation.simconfig import SimConfig from pulser_simulation.simulation import QutipEmulator +# NOTE: If any of these change, remember to MANUALLY replicate them in the +# API reference doc (ie they are not updated automatically). __all__ = [ "EmulatorConfig", "NoiseModel", diff --git a/tests/test_channels.py b/tests/test_channels.py index 582deb23b..21908a006 100644 --- a/tests/test_channels.py +++ b/tests/test_channels.py @@ -190,7 +190,7 @@ def test_repr(): "Raman.Local(Max Absolute Detuning: None, Max Amplitude: " "2 rad/µs, Minimum retarget time: 1000 ns, " "Fixed retarget time: 200 ns, Max targets: 4, Clock period: 4 ns, " - "Minimum pulse duration: 16 ns, Basis: 'digital')" + "Minimum pulse duration: 16 ns, Supports EOM: False, Basis: 'digital')" ) assert raman.__str__() == r1 @@ -200,7 +200,8 @@ def test_repr(): "Max Amplitude: None, Clock period: 1 ns, " "Minimum pulse duration: 1 ns, " "Maximum pulse duration: 100000000 ns, " - "Modulation Bandwidth: 4 MHz, Basis: 'ground-rydberg')" + "Modulation Bandwidth: 4 MHz, Supports EOM: False, " + "Basis: 'ground-rydberg')" ) assert ryd.__str__() == r2 diff --git a/tests/test_devices.py b/tests/test_devices.py index df473a895..574d6626e 100644 --- a/tests/test_devices.py +++ b/tests/test_devices.py @@ -281,7 +281,6 @@ def specs(dev): register_str = ( "\nRegister parameters:\n" + f" - Dimensions: {dev.dimensions}D\n" - + f" - Rydberg level: {dev.rydberg_level}\n" + check_none_fn(dev, "max_atom_num", "Maximum number of atoms: {}") + check_none_fn( dev, @@ -290,7 +289,6 @@ def specs(dev): ) + " - Minimum distance between neighbouring atoms: " + f"{dev.min_atom_distance} μm\n" - + yes_no_fn(dev, "supports_slm_mask", "SLM Mask") ) layout_str = ( @@ -312,19 +310,21 @@ def specs(dev): device_str = ( "\nDevice parameters:\n" - + check_none_fn(dev, "max_runs", "Maximum number of runs: {}") + + f" - Rydberg level: {dev.rydberg_level}\n" + + f" - Ising interaction coefficient: {dev.interaction_coeff}\n" + check_none_fn( - dev, - "max_sequence_duration", - "Maximum sequence duration: {} ns", + dev, "interaction_coeff_xy", "XY interaction coefficient: {}" ) + yes_no_fn(dev, "reusable_channels", "Channels can be reused") + f" - Supported bases: {', '.join(dev.supported_bases)}\n" + f" - Supported states: {', '.join(dev.supported_states)}\n" - + f" - Ising interaction coefficient: {dev.interaction_coeff}\n" + + yes_no_fn(dev, "supports_slm_mask", "SLM Mask") + check_none_fn( - dev, "interaction_coeff_xy", "XY interaction coefficient: {}" + dev, + "max_sequence_duration", + "Maximum sequence duration: {} ns", ) + + check_none_fn(dev, "max_runs", "Maximum number of runs: {}") ) channel_str = "\nChannels:\n" + "\n".join( @@ -332,7 +332,14 @@ def specs(dev): for name, ch in {**dev.channels, **dev.dmm_channels}.items() ) - return register_str + layout_str + device_str + channel_str + first_line = ( + (device.short_description + "\n") + if device.short_description + else "" + ) + return ( + first_line + register_str + layout_str + device_str + channel_str + ) assert device.specs == specs(device) diff --git a/tutorials/advanced_features/Backends for Sequence Execution.ipynb b/tutorials/advanced_features/Backends for Sequence Execution.ipynb index 956e1ec47..4d98a4aa4 100644 --- a/tutorials/advanced_features/Backends for Sequence Execution.ipynb +++ b/tutorials/advanced_features/Backends for Sequence Execution.ipynb @@ -5,7 +5,14 @@ "id": "6f230abe", "metadata": {}, "source": [ - "# Backend Execution of Pulser Sequences" + "# Backend Execution\n", + "\n", + "*What you will learn:*\n", + "\n", + "- what a `Backend` is for;\n", + "- what types of `Backend` exist;\n", + "- how to choose the best `Backend` for your needs;\n", + "- how to execute a `Sequence` on a `Backend` and retrieve the results." ] }, { @@ -13,6 +20,8 @@ "id": "ae508ab2", "metadata": {}, "source": [ + "## Introduction\n", + "\n", "When the time comes to execute a Pulser sequence, there are many options: one can choose to execute it on a QPU or on an emulator, which might happen locally or remotely. All these options are accessible through an unified interface we call a `Backend`. \n", "\n", "This tutorial is a step-by-step guide on how to use the different backends for Pulser sequence execution." @@ -84,7 +93,15 @@ "id": "122a3c37", "metadata": {}, "source": [ - "The next step is to create the sequence that we want to execute. Here, we make a sequence with a variable duration combining a Blackman waveform in amplitude and a ramp in detuning. Since it will be executed on an emulator, we can create the register we want and choose a `VirtualDevice` that does not impose hardware restrictions (like the `MockDevice`)." + "The next step is to create the sequence that we want to execute. Here, we make a sequence with a variable duration combining a Blackman waveform in amplitude and a ramp in detuning. Since it will be executed on an emulator, we can create the register we want and choose a `VirtualDevice` that does not impose hardware restrictions (like the `MockDevice`).\n", + "\n", + "
\n", + "\n", + "Attention\n", + " \n", + "This examples uses a [parametrized sequence](paramseqs.nblink), which is only introduced in the *Extend Usage* section.\n", + "\n", + "
" ] }, { @@ -166,9 +183,9 @@ "id": "365ed331", "metadata": {}, "source": [ - "Upon creation, all backends require the sequence they will execute. Emulator backends also accept, optionally, a configuration given as an instance of the `EmulatorConfig` class. This class allows for setting all the parameters available in `QutipEmulator` and is forward looking, meaning that it envisions that these options will at some point be availabe on other emulator backends. This also means that trying to change parameters in the configuration of a backend that does not support them yet will raise an error.\n", + "Upon creation, all backends require the sequence they will execute. Emulator backends also accept, optionally, a configuration given as an instance of the `EmulationConfig` class. This class allows for setting all the parameters available in `QutipEmulator` and is forward looking, meaning that it envisions that these options will at some point be available on other emulator backends. This also means that trying to change parameters in the configuration of a backend that does not support them yet will raise an error.\n", "\n", - "Even so, `EmulatorConfig` also has a dedicated `backend_options` for options specific to each backend, which are detailed in the [backends' docstrings](../apidoc/backend.rst)." + "Even so, `EmulationConfig` also has a dedicated `backend_options` for options specific to each backend, which are detailed in the [backends' docstrings](../apidoc/_autosummary/pulser.backends.rst#module-pulser.backends)." ] }, { diff --git a/tutorials/creating_sequences.ipynb b/tutorials/creating_sequences.ipynb index b5fc3aaf1..309381a58 100644 --- a/tutorials/creating_sequences.ipynb +++ b/tutorials/creating_sequences.ipynb @@ -4,54 +4,19 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Pulse Sequence Creation" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pulser\n", - "from pprint import pprint" + "# Tutorial: Programming with Pulser" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 1. Creating the `Register`" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `Register` defines the positions of the atoms and their names of each one. There are multiple ways of defining a `Register`, the most customizable one being to create a dictionary that associates a name (the key) to a cooordinate (the value)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "L = 4\n", - "square = np.array([[i, j] for i in range(L) for j in range(L)], dtype=float)\n", - "square -= np.mean(square, axis=0)\n", - "square *= 5\n", + "This tutorial demonstrates how to use Pulser to program the evolution of a quantum system. Two programs are presented:\n", "\n", - "qubits = {f\"q{i}\": point for (i, point) in enumerate(square)}\n", - "reg = pulser.Register(qubits)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The `Register` class provides some useful features, like the ability to visualise the array and to make a rotated copy." + "- [In a first part](#preparing-an-atom-in-the-rydberg-state), we excite one atom from its ground state to its excited state using a constant pulse. \n", + "- [In a second part](#adiabatic-preparation-of-an-anti-ferromagnetic-state), we show how to prepare a quantum system of 9 atoms in an anti-ferromagnetic state using time-dependent pulses.\n", + "\n", + "This tutorial follows the step-py-step guide on how to create a quantum program using Pulser that is provided in the [programming page](../programming.md). For more information regarding the steps followed and the mathematical objects at stake, please refer to this page." ] }, { @@ -60,69 +25,88 @@ "metadata": {}, "outputs": [], "source": [ - "print(\"The original array:\")\n", - "reg.draw()\n", - "reg1 = reg.rotated(45) # Rotate by 45 degrees\n", - "print(\"The rotated array:\")\n", - "reg1.draw()" + "import numpy as np\n", + "import pulser\n", + "from matplotlib import pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "If one doesn't particularly care about the name given to the qubits, one can also create a `Register` just from a list of coordinates (using the `Register.from_coordinates` class method). In this case, the qubit ID's are just numbered, starting from 0, in the order they are provided in, with the option of adding a common prefix before each number. Also, it automatically centers the entire array around the origin, an option that can be disabled if desired." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "reg2 = pulser.Register.from_coordinates(\n", - " square, prefix=\"q\"\n", - ") # All qubit IDs will start with 'q'\n", - "reg2.draw()" + "## Preparing an atom in the Rydberg state" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Furthermore, there are also built-in class methods from creation of common array patterns, namely:\n", - "- Square lattices in rectangular or square shapes\n", - "- Triangular lattices\n", + "As presented in [\"Programming a neutral-atom QPU\"](../programming.md), Pulser enables you to program [an Hamiltonian](../programming.md#hamiltonian-evolves-the-state) composed of an [interaction Hamiltonian](../programming.md#interaction-hamiltonian) and a [drive Hamiltonian](../programming.md#driving-hamiltonian).\n", "\n", - "We could, thus, create the same square array as before by doing:" + "Let's program this Hamiltonian $H$ such that an atom initially in the ground state $\\left|g\\right>$ is measured in the Rydberg state $\\left|r\\right>$ after a time $\\Delta t$.\n", + "\n", + "Since we are working with a single atom, there is no [interaction Hamiltonian](../programming.md#interaction-hamiltonian). In this specific example, $H=H^D$. For a simple pulse having a duration $\\Delta t$, a constant amplitude along time $\\Omega$, detuning $\\delta=0$ and phase $\\phi=0$ [the Hamiltonian between 0 and Δt is](../programming.md#hamiltonian-evolves-the-state):\n", + "\n", + "$$ H(t) = \\hbar\\frac{\\Omega}{2} (|g\\rangle\\langle r| + |r\\rangle\\langle g|)$$" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "reg3 = pulser.Register.square(\n", - " 4, spacing=5, prefix=\"q\"\n", - ") # 4x4 array with atoms 5 um apart\n", - "reg3.draw()" + "To find the atom in the Rydberg state at the end of the program, we want $\\Omega \\Delta t = \\pi$ so we choose $\\Delta t=1000\\ ns$ and $\\Omega=\\pi\\ rad/\\mu s$.\n", + "
\n", + "\n", + "\n", + "- We can use the Bloch sphere representation\n", + "\n", + " The pulse being of duration $\\Delta t$, of detuning $\\delta=0$, of phase $\\phi=0$ and constant amplitude $\\Omega$, the pulse will make the vector representing the state rotate by an angle $\\Omega \\Delta t$ around the axis $(1, 0, 0)$. To go from the ground state $\\left|g\\right>$ to the excited state $\\left|r\\right>$ by rotating around the $(1, 0, 0)$ axis, we need to make a rotation of angle $\\pi$.\n", + "\n", + " Therefore we get that the final state will be the Rydberg state if $\\Omega \\Delta t = \\pi$. From this condition, we choose $\\Delta t = 1000\\ ns$ and $\\Omega=\\pi\\ rad/\\mu s$. \n", + "\n", + "
\n", + "
\n", + " \"Bloch\n", + "
The Bloch vector rotates around the x axis by an angle of π, going from the ground state to the Rydberg state.
\n", + "
\n", + "
\n", + "\n", + "\n", + "- We can compute the final state knowing the initial state\n", + "\n", + " The initial state being the ground state and the Hamiltonian $H$ being constant along time, [the final state is](../programming.md#hamiltonian-evolves-the-state):\n", + "\n", + " $$\n", + " \\begin{align}\n", + " \\left|\\Psi_f\\right> &= e^{-\\frac{i}{\\hbar} H \\Delta t} \\left|g\\right> \\\\\n", + " &= \\left(\\cos\\left(\\frac{\\Omega}{2} \\Delta t\\right)(|g\\rangle\\langle g| + |r\\rangle\\langle r|) - i \\sin\\left(\\frac{\\Omega}{2} \\Delta t\\right)(|g\\rangle\\langle r| + |r\\rangle\\langle g|)\\right)\\left|g\\right>\\\\\n", + " &= \\cos\\left(\\frac{\\Omega}{2} \\Delta t\\right)\\left|g\\right> - i \\sin\\left(\\frac{\\Omega}{2} \\Delta t\\right)\\left|r\\right>\n", + " \\end{align}\n", + " $$\n", + "\n", + " The final state will be the Rydberg state $\\left|r\\right>$ if $\\frac{\\Omega}{2} \\Delta t = \\frac{\\pi}{2}$. From this condition, we choose $\\Delta t = 1000\\ ns$ and $\\Omega=\\pi\\ rad/\\mu s$.\n", + "\n", + "
\n", + "\n", + "

\n", + "
\n", + "

" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 2. Initializing the Sequence" + "### 1. Picking a `Device`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "To create a `Sequence`, one has to provide it with the `Register` instance and the device in which the sequence will be executed. The chosen device will dictate whether the register is valid or not.\n", + "We need a `Device` that will enable us to target the transition between the ground and the Rydberg state. `pulser.AnalogDevice` contains the `Rydberg.Global` channel, which targets the transition between these two states. Let's select this `Device`!\n", "\n", - "We import the device (in this case, `DigitalAnalogDevice`) from `pulser.devices` and initialize our sequence with the freshly created register:" + "We can check in the device specifications (accessed via `Device.specs`) that the `AnalogDevice` supports the ground-rydberg transition." ] }, { @@ -131,23 +115,24 @@ "metadata": {}, "outputs": [], "source": [ - "from pulser.devices import DigitalAnalogDevice\n", - "\n", - "seq = pulser.Sequence(reg, DigitalAnalogDevice)" + "device = pulser.AnalogDevice\n", + "print(device.specs)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 3. Declaring the channels that will be used" + "### 2. Creating the `Register`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Inspecting what channels are available on this device:" + "We want to excite one atom. There will therefore be only one atom in the `Register`, whose position does not matter because it will not interact with another atom.\n", + "\n", + "Let's then create a `Register` containing one atom at the coordinate (0, 0)." ] }, { @@ -156,63 +141,38 @@ "metadata": {}, "outputs": [], "source": [ - "seq.available_channels" + "register = pulser.Register.from_coordinates([(0, 0)], prefix=\"q\")\n", + "register.draw()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We're going to choose the `'rydberg_local'` and `'raman_local'` channels. Note how a declared channel is no longer reported as available." + "At this stage, we can initialize the `Sequence`, our quantum program. This will check that the created `Register` matches the parameters set by the `Device` we picked. " ] }, { "cell_type": "code", "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "seq.declare_channel(\"ch0\", \"raman_local\")\n", - "print(\"Available channels after declaring 'ch0':\")\n", - "pprint(seq.available_channels)\n", - "\n", - "seq.declare_channel(\"ch1\", \"rydberg_local\", initial_target=\"q4\")\n", - "print(\"\\nAvailable channels after declaring 'ch1':\")\n", - "pprint(seq.available_channels)" - ] - }, - { - "cell_type": "markdown", "metadata": {}, - "source": [ - "At any time, we can also consult which channels were declared, their specifications and the name they were given by calling:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, "outputs": [], "source": [ - "seq.declared_channels" + "sequence = pulser.Sequence(register, device)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 4. Composing the Sequence" + "### 3. Picking the Channels" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Every channel needs to start with a target. For `Global` channels this is predefined to be all qubits in the device, but for `Local` channels this has to be defined. This initial target can be set through at channel declaration (see how `'ch1'` was set to target qubit `4`), or it can be done through the standard `target` instruction." + "The only channel we need to pick is a `Rydberg` channel to target the transition between $\\left|g\\right>$ and $\\left|r\\right>$. Since we only have one atom, the addressing does not matter, the `Rydberg.Global` channel will address the atom in the register. " ] }, { @@ -221,30 +181,31 @@ "metadata": {}, "outputs": [], "source": [ - "seq.target(\"q1\", \"ch0\")" + "sequence.declare_channel(\"rydberg_global\", \"rydberg_global\")\n", + "print(\n", + " \"The states used in the computation are\", sequence.get_addressed_states()\n", + ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now both channels have an initial target, so we can start building the sequence. Let's start by creating a simple pulse with a constant Rabi frequency of 2 rad/µs and a constant detuning of -10 rad/µs that lasts 200 ns." + "At this stage, the atom is initialized in the ground state $\\left|g\\right>$ and only two energy levels are used in the computation - the state of the system is described by a qubit." ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "simple_pulse = pulser.Pulse.ConstantPulse(200, 2, -10, 0)" + "### 4. Adding the pulses" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Let's add this pulse to `'ch0'`:" + "Let's now add the pulse of duration $\\Delta t = 1000\\ ns$, amplitude $\\Omega=\\pi\\ rad/\\mu s$, detuning $\\delta=0$ and phase $\\phi=0$ to the `Rydberg.Global` channel to modify the state of the atom and make it reach the state $\\left|r\\right>$." ] }, { @@ -253,30 +214,23 @@ "metadata": {}, "outputs": [], "source": [ - "seq.add(simple_pulse, \"ch0\")" + "pi_pulse = pulser.Pulse.ConstantPulse(1000, np.pi, 0, 0)\n", + "sequence.add(pi_pulse, \"rydberg_global\")\n", + "sequence.draw(mode=\"input\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Now, say we want to idle `'ch1'` for 100 ns while `'ch0'` is doing its pulse. We do that by calling: " - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "seq.delay(100, \"ch1\")" + "### Executing the Pulse Sequence" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Next, we want to create a more complex pulse to add to `'ch1'`, where the amplitude and the detuning are not constant. To do that, we use `Waveform`s:" + "We are now done with our first Pulser program! We can now submit it to a backend for execution. Pulser provides multiple backends, notably the QPUs, but also a backend to simulate small quantum systems on your laptop based on **QuTip**. Let's use this `QutipBackend` to simulate the final state of the system: " ] }, { @@ -285,20 +239,15 @@ "metadata": {}, "outputs": [], "source": [ - "duration = 1000\n", - "amp_wf = pulser.BlackmanWaveform(\n", - " duration, np.pi / 2\n", - ") # Duration: 1000 ns, Area: pi/2\n", - "detuning_wf = pulser.RampWaveform(\n", - " duration, -20, 20\n", - ") # Duration: 1000ns, linear sweep from -20 to 20 rad/µs" + "backend = pulser.backends.QutipBackend(sequence)\n", + "result = backend.run()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We can visualize a waveform by calling:" + "When running an experiment on a neutral-atom QPU, the output of the quantum program is the sampling of the final state. It is a dictionnary associating to each measured state the number of times it was measured." ] }, { @@ -307,32 +256,54 @@ "metadata": {}, "outputs": [], "source": [ - "amp_wf.draw()" + "result.sample_final_state(1000)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "Also, it is often convenient to find the integral of a waveform, which can be obtain by calling:" + "When measuring in the ground-rydberg basis, [the ground state is labelled \"0\" and the rydberg state \"1\"](../conventions.md#state-preparation-and-measurement). For each of the 1000 measurements we did, the atom was measured in the Rydberg state, which means we designed our quantum program correctly!" ] }, { - "cell_type": "code", - "execution_count": null, - "metadata": { - "scrolled": true - }, - "outputs": [], + "cell_type": "markdown", + "metadata": {}, "source": [ - "amp_wf.integral # dimensionless" + "## Adiabatic preparation of an Anti-Ferromagnetic State" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We then create the pulse with the waveforms instead of fixed values and we can also visualize it:" + "Let's now program the [Ising Hamiltonian](../programming.md#ising-hamiltonian) such that a set of 9 atoms initially in the ground state $\\left|ggggggggg\\right>$ are prepared in an antiferromagnetic state $\\left|rgrgrgrgr\\right>$.\n", + "\n", + "To reach the desired antiferromagentic state, we can take advantage of the [adiabatic theorem](https://en.wikipedia.org/wiki/Adiabatic_theorem). The idea is to use a time-dependent Hamiltonian that changes slowly so that the system stays in its ground state. Therefore, we must choose a final Hamiltonian that has the antiferromagnetic state as its ground state.\n", + "\n", + "This final Hamiltonian should simultaneously favor having the largest number of atoms in the $\\left|r\\right>$ state (by having $\\delta > 0$) and discourage nearest neighbors from being both in $\\left|r\\right>$ (via the [interaction Hamiltonian](../programming.md#ising-hamiltonian)). When these contributions are appropriately balanced, we get an Hamiltonian with $\\left|rgrgrgrgr\\right>$ as its ground state.\n", + "\n", + "Let's follow the protocol from [this paper](https://journals.aps.org/prx/abstract/10.1103/PhysRevX.8.021070), where we define the parameters with respect to the interaction strength between nearest neighbours, $U$ (see Table 1 of the paper):\n", + "\n", + "$$\n", + "U = 2\\pi\\ rad/\\mu s\\\\\n", + "\\Omega_{max} = 2 U\\\\\n", + "\\delta_0 = -6 U\\\\\n", + "\\delta_f = 2U\\\\\n", + "t_{rise} = 252\\ ns,\\ t_{fall} = 500\\ ns\\\\\n", + "t_{sweep} = \\frac{\\delta_f - \\delta_0 [rad\\cdot\\mu s^{-1}]}{2 \\pi \\cdot 10\\ [rad\\cdot\\mu s^{-2}]}\n", + "$$\n", + "\n", + "and define $\\Omega(t)$ and $\\delta(t)$ over time as (see Figure 1 (b)): \n", + "
\n", + "\"AF\n", + "
\n", + "\n", + "The [Hamiltonian](../programming.md#hamiltonian-evolves-the-state) we are implementing is (the phase is constant and equal to $0$ over time):\n", + "\n", + "$$H = \\hbar \\sum_i \\left (\\frac{\\Omega(t)}{2} \\left(|g\\rangle\\langle r| + |r\\rangle\\langle g|\\right) - \\delta(t) |r\\rangle\\langle r| + \\sum_{j$ and $\\left|r\\right>$. Since we want to apply the same amplitude, detuning and phase on each atom, we can use the `Rydberg.Global` channel: " ] }, { @@ -467,33 +413,48 @@ "metadata": {}, "outputs": [], "source": [ - "seq.target(\"q0\", \"ch0\")\n", - "seq.add(complex_pulse, \"ch0\", protocol=\"no-delay\")\n", - "\n", - "print(\"Current Schedule:\")\n", - "print(seq)\n", - "seq.draw()" + "sequence.declare_channel(\"rydberg_global\", \"rydberg_global\")\n", + "print(\n", + " \"The states used in the computation are\", sequence.get_addressed_states()\n", + ")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "With this protocol, it is possible (though not advised), to create an overlap where multiple channels can be acting on the same qubit at the same time. Here, we can see that both act on qubit `0` from `ti=2100` to `tf=2300`." + "At this stage, all the atoms are initialized in the state $\\left|g\\right>$ and only two energy levels are used in the computation, i.e. each atom is a qubit and the initial state of the quantum system is $\\left|ggggggggg\\right>$.\n", + "\n", + "The interaction Hamiltonian is now completely determined, and will not change over time." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## 5. Measurement" + "### 4. Adding the pulses" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "To finish a sequence, we measure it. A measurement signals the end of a sequence, so after it no more changes are possible. We can measure a sequence by calling:" + "Let's now define the driving Hamiltonian at each nanosecond between $0$ and $t_{tot}=t_{rise}+t_{sweep}+t_{fall}$. We follow the program that we described above. The `Sequence` will be composed of three pulses:\n", + "- A first \"rise\" pulse with:\n", + " - Duration: $t_{rise}$\n", + " - Amplitude: $0 \\rightarrow \\Omega_{max}$\n", + " - Detuning: $\\delta_0$\n", + " - Phase: $0$\n", + "- A second \"sweep\" pulse with:\n", + " - Duration: $t_{sweep}$\n", + " - Amplitude: $\\Omega_{max}$\n", + " - Detuning: $\\delta_0 \\rightarrow\\delta_{final}$\n", + " - Phase: $0$\n", + "- A third \"fall\" pulse with:\n", + " - Duration: $t_{fall}$\n", + " - Amplitude: $\\Omega_{max}\\rightarrow 0$\n", + " - Detuning: $\\delta_{final}$\n", + " - Phase: $0$ " ] }, { @@ -502,45 +463,63 @@ "metadata": {}, "outputs": [], "source": [ - "seq.measure(basis=\"ground-rydberg\")" + "rise = pulser.Pulse.ConstantDetuning(\n", + " pulser.RampWaveform(t_rise, 0.0, Omega_max), delta_0, 0.0\n", + ")\n", + "sweep = pulser.Pulse.ConstantAmplitude(\n", + " Omega_max, pulser.RampWaveform(t_sweep, delta_0, delta_f), 0.0\n", + ")\n", + "fall = pulser.Pulse.ConstantDetuning(\n", + " pulser.RampWaveform(t_fall, Omega_max, 0.0), delta_f, 0.0\n", + ")\n", + "sequence.add(rise, \"rydberg_global\")\n", + "sequence.add(sweep, \"rydberg_global\")\n", + "sequence.add(fall, \"rydberg_global\")\n", + "sequence.draw(mode=\"input\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "When measuring, one has to select the desired measurement basis. The availabe options depend on the device and can be consulted by calling:" + "### Executing the Pulse Sequence" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "DigitalAnalogDevice.supported_bases" + "We are now done with this program! Let's use the `QutipBackend` to simulate the final state of the system: " ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "And so, we've obtained the final sequence!" + "backend = pulser.backends.QutipBackend(sequence)\n", + "result = backend.run()\n", + "counts = result.sample_final_state(1000)\n", + "# Let's plot the histogram associated to the measurements\n", + "# Let's select only the states that are measured more than 10 times\n", + "most_freq = {k: v for k, v in counts.items() if v > 10}\n", + "plt.bar(list(most_freq.keys()), list(most_freq.values()))\n", + "plt.xticks(rotation=\"vertical\")\n", + "plt.show()" ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "seq.draw()" + "The state that is measured the most frequently is the $\\left|101010101\\right>\\rightarrow\\left|rgrgrgrgr\\right>$: our quantum program correctly excites the ground sate $\\left|ggggggggg\\right>$ into the state $\\left|rgrgrgrgr\\right>$." ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "pulserenv", "language": "python", "name": "python3" }, @@ -554,7 +533,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.12.3" } }, "nbformat": 4, diff --git a/tutorials/quantum_simulation/Spin chain of 3 atoms in XY mode.ipynb b/tutorials/quantum_simulation/Spin chain of 3 atoms in XY mode.ipynb index 515b412bc..a3d6b6441 100644 --- a/tutorials/quantum_simulation/Spin chain of 3 atoms in XY mode.ipynb +++ b/tutorials/quantum_simulation/Spin chain of 3 atoms in XY mode.ipynb @@ -4,31 +4,24 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# XY Spin Chain" + "# XY Hamiltonian" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "We introduce here a new mode of interaction between the atoms driven by the dipole-dipole interaction. This mode is called the XY mode. The interaction hamiltonian of a system of $N$ qubits is now given by \n", + "The [XY Hamiltonian](../programming.md#xy-hamiltonian) arises when the [basis states](../conventions.md#bases) are the $|0\\rangle$ and $|1\\rangle$ state of the `XY` basis, which is addressed by the [Microwave channel](../apidoc/_autosummary/pulser.channels.Microwave.rst#pulser.channels.Microwave). When a `Microwave` channel is declared, the `Sequence` is immediately assumed to be in the so-called *XY mode*.\n", "\n", - "$$\n", - "H_{XY} = \\sum_{i=1}^N\\sum_{j Date: Fri, 14 Feb 2025 17:23:28 +0100 Subject: [PATCH 10/12] Defining the new Backend API classes (#764) * Introducing the Results class * Defining State and Operator ABCs * Defining the Observable ABC * Updates to State and Operator ABCs * Introducing EmulationConfig * Define the default observables * Define the EmulatorBackend ABC * Implement more flexible evaluation time matching * Finish up changes in the config base classes * Include total sequence duration in Results * Import sorting * Fix typing issues * Applying review suggestions * Rename QubitDensity -> Occupation * Change State to require `probabilities()` instead of `sample()` * Docstring updates * Removing 'custom_operators' option * Definition of QutipState * Definition of QutipOp * Remove Results.meas_basis * Revert to having `State.sample()` as the abstractmethod * Incorporating UUIDs in observables * Add Results.get_tagged_results() * Misc adjustments * First UTs for pulser.backend classes * UTs for QutipState and QutipOperator * Unit tests for observables * Unit tests for Results * Make UT compatible with Python 3.9 * Fix docs build * Expose relevant new classes * Check interaction matrix is symmetric * Separate times and result values in Results * Check that evaluation times are unique * Finishing touches * Enforce evaluation times to be in ascending order * Explicitly enforce that results are stored with asceding evaluation times * Implementing review suggestion * Rename SecondMomentOfEnergy -> EnergySecondMoment * Add review suggestion * Add warnings * Change cutoffs * Fix docs --- pulser-core/pulser/backend/__init__.py | 40 +- pulser-core/pulser/backend/abc.py | 46 +- pulser-core/pulser/backend/config.py | 243 +++++++- .../pulser/backend/default_observables.py | 416 ++++++++++++++ pulser-core/pulser/backend/observable.py | 199 +++++++ pulser-core/pulser/backend/operator.py | 142 +++++ pulser-core/pulser/backend/qpu.py | 4 +- pulser-core/pulser/backend/remote.py | 39 +- pulser-core/pulser/backend/results.py | 165 ++++++ pulser-core/pulser/backend/state.py | 174 ++++++ pulser-core/pulser/register/register.py | 5 +- pulser-core/pulser/result.py | 79 +-- pulser-core/pulser/waveforms.py | 2 +- pulser-pasqal/pulser_pasqal/backends.py | 2 +- .../pulser_simulation/__init__.py | 4 + .../pulser_simulation/qutip_op.py | 270 +++++++++ .../pulser_simulation/qutip_result.py | 4 +- .../pulser_simulation/qutip_state.py | 286 ++++++++++ .../pulser_simulation/simresults.py | 17 +- .../pulser_simulation/simulation.py | 11 +- tests/test_backend.py | 477 +++++++++++++++- tests/test_pasqal.py | 4 +- tests/test_qutip_state_op.py | 536 ++++++++++++++++++ tests/test_result.py | 15 + .../Backends for Sequence Execution.ipynb | 4 +- 25 files changed, 3078 insertions(+), 106 deletions(-) create mode 100644 pulser-core/pulser/backend/default_observables.py create mode 100644 pulser-core/pulser/backend/observable.py create mode 100644 pulser-core/pulser/backend/operator.py create mode 100644 pulser-core/pulser/backend/results.py create mode 100644 pulser-core/pulser/backend/state.py create mode 100644 pulser-simulation/pulser_simulation/qutip_op.py create mode 100644 pulser-simulation/pulser_simulation/qutip_state.py create mode 100644 tests/test_qutip_state_op.py diff --git a/pulser-core/pulser/backend/__init__.py b/pulser-core/pulser/backend/__init__.py index 5d3645e9f..e40ec1902 100644 --- a/pulser-core/pulser/backend/__init__.py +++ b/pulser-core/pulser/backend/__init__.py @@ -14,8 +14,44 @@ """Classes for backend execution.""" import pulser.noise_model as noise_model # For backwards compat -from pulser.backend.config import EmulatorConfig +from pulser.backend.abc import Backend, EmulatorBackend +from pulser.backend.config import EmulatorConfig, EmulationConfig from pulser.noise_model import NoiseModel as NoiseModel # For backwards compat from pulser.backend.qpu import QPUBackend +from pulser.backend.results import Results +from pulser.backend.state import State +from pulser.backend.operator import Operator +from pulser.backend.observable import Callback, Observable +from pulser.backend.default_observables import ( + BitStrings, + CorrelationMatrix, + Energy, + EnergySecondMoment, + EnergyVariance, + Expectation, + Fidelity, + Occupation, + StateResult, +) -__all__ = ["EmulatorConfig", "QPUBackend"] +__all__ = [ + "Backend", + "QPUBackend", + "EmulatorBackend", + "EmulatorConfig", + "EmulationConfig", + "Results", + "Operator", + "State", + "Callback", + "Observable", + "BitStrings", + "CorrelationMatrix", + "Energy", + "EnergySecondMoment", + "EnergyVariance", + "Expectation", + "Fidelity", + "Occupation", + "StateResult", +] diff --git a/pulser-core/pulser/backend/abc.py b/pulser-core/pulser/backend/abc.py index 704e91d55..39f84e7dd 100644 --- a/pulser-core/pulser/backend/abc.py +++ b/pulser-core/pulser/backend/abc.py @@ -14,34 +14,38 @@ """Base class for the backend interface.""" from __future__ import annotations -import typing from abc import ABC, abstractmethod +from collections.abc import Sequence +from typing import ClassVar +import pulser +from pulser.backend.config import EmulationConfig +from pulser.backend.results import Results from pulser.devices import Device -from pulser.result import Result -from pulser.sequence import Sequence - -Results = typing.Sequence[Result] class Backend(ABC): """The backend abstract base class.""" - def __init__(self, sequence: Sequence, mimic_qpu: bool = False) -> None: + def __init__( + self, sequence: pulser.Sequence, mimic_qpu: bool = False + ) -> None: """Starts a new backend instance.""" self.validate_sequence(sequence, mimic_qpu=mimic_qpu) self._sequence = sequence self._mimic_qpu = bool(mimic_qpu) @abstractmethod - def run(self) -> Results | typing.Sequence[Results]: + def run(self) -> Results | Sequence[Results]: """Executes the sequence on the backend.""" pass @staticmethod - def validate_sequence(sequence: Sequence, mimic_qpu: bool = False) -> None: + def validate_sequence( + sequence: pulser.Sequence, mimic_qpu: bool = False + ) -> None: """Validates a sequence prior to submission.""" - if not isinstance(sequence, Sequence): + if not isinstance(sequence, pulser.Sequence): raise TypeError( "'sequence' should be a `Sequence` instance" f", not {type(sequence)}." @@ -70,3 +74,27 @@ def validate_sequence(sequence: Sequence, mimic_qpu: bool = False) -> None: "the register's layout must be one of the layouts available " f"in '{device.name}.calibrated_register_layouts'." ) + + +class EmulatorBackend(Backend): + """The emulator backend parent class.""" + + default_config: ClassVar[EmulationConfig] + + def __init__( + self, + sequence: pulser.Sequence, + *, + config: EmulationConfig | None = None, + mimic_qpu: bool = False, + ) -> None: + """Initializes the backend.""" + super().__init__(sequence, mimic_qpu=mimic_qpu) + config = config or self.default_config + if not isinstance(config, EmulationConfig): + raise TypeError( + "'config' must be an instance of 'EmulationConfig', " + f"not {type(config)}." + ) + # See the BackendConfig definition to see why this works + self._config = type(self.default_config)(**config._backend_options) diff --git a/pulser-core/pulser/backend/config.py b/pulser-core/pulser/backend/config.py index afa983c44..504f715e2 100644 --- a/pulser-core/pulser/backend/config.py +++ b/pulser-core/pulser/backend/config.py @@ -14,31 +14,260 @@ """Defines the backend configuration classes.""" from __future__ import annotations +import copy +import warnings +from collections import Counter from dataclasses import dataclass, field -from typing import Any, Literal, Sequence, get_args +from typing import ( + Any, + Generic, + Literal, + Sequence, + SupportsFloat, + TypeVar, + cast, + get_args, +) import numpy as np +from numpy.typing import ArrayLike +import pulser.math as pm +from pulser.backend.observable import Observable +from pulser.backend.state import State from pulser.noise_model import NoiseModel EVAL_TIMES_LITERAL = Literal["Full", "Minimal", "Final"] +StateType = TypeVar("StateType", bound=State) + -@dataclass(frozen=True) class BackendConfig: - """The base backend configuration. + """The base backend configuration.""" + + _backend_options: dict[str, Any] + + def __init__(self, **backend_options: Any) -> None: + """Initializes the backend config.""" + cls_name = self.__class__.__name__ + if invalid_kwargs := ( + set(backend_options) + - (self._expected_kwargs() | {"backend_options"}) + ): + warnings.warn( + f"{cls_name!r} received unexpected keyword arguments: " + f"{invalid_kwargs}; only the following keyword " + f"arguments are expected: {self._expected_kwargs()}.", + stacklevel=2, + ) + # Prevents potential issues with mutable arguments + self._backend_options = copy.deepcopy(backend_options) + if "backend_options" in backend_options: + with warnings.catch_warnings(): + warnings.filterwarnings("always") + warnings.warn( + f"The 'backend_options' argument of {cls_name!r} " + "has been deprecated. Please provide the options " + f"as keyword arguments directly to '{cls_name}()'.", + DeprecationWarning, + stacklevel=2, + ) + self._backend_options.update(backend_options["backend_options"]) + + def _expected_kwargs(self) -> set[str]: + return set() + + def __getattr__(self, name: str) -> Any: + if ( + # Needed to avoid recursion error + "_backend_options" in self.__dict__ + and name in self._backend_options + ): + return self._backend_options[name] + raise AttributeError(f"{name!r} has not been passed to {self!r}.") + + +class EmulationConfig(BackendConfig, Generic[StateType]): + """Configures an emulation on a backend. Args: - backend_options: A dictionary of backend specific options. + observables: A sequence of observables to compute at specific + evaluation times. The observables without specified evaluation + times will use this configuration's 'default_evaluation_times'. + default_evaluation_times: The default times at which observables + are computed. Can be a sequence of unique relative times between 0 + (the start of the sequence) and 1 (the end of the sequence), in + ascending order. Can also be specified as "Full", in which case + every step in the emulation will also be an evaluation time. + initial_state: The initial state from which emulation starts. If + specified, the state type needs to be compatible with the emulator + backend. If left undefined, defaults to starting with all qudits + in the ground state. + with_modulation: Whether to emulate the sequence with the programmed + input or the expected output. + interaction_matrix: An optional interaction matrix to replace the + interaction terms in the Hamiltonian. For an N-qudit system, + must be an NxN symmetric matrix where entry (i, j) dictates + the interaction coefficient between qudits i and j, ie it replaces + the C_n/r_{ij}^n term. + prefer_device_noise_model: If True, uses the noise model of the + sequence's device (if the sequence's device has one), regardless + of the noise model given with this configuration. + noise_model: An optional noise model to emulate the sequence with. + Ignored if the sequence's device has default noise model and + `prefer_device_noise_model=True`. """ - backend_options: dict[str, Any] = field(default_factory=dict) + observables: Sequence[Observable] + default_evaluation_times: np.ndarray | Literal["Full"] + initial_state: StateType | None + with_modulation: bool + interaction_matrix: pm.AbstractArray | None + prefer_device_noise_model: bool + noise_model: NoiseModel + + def __init__( + self, + *, + observables: Sequence[Observable] = (), + # Default evaluation times for observables that don't specify one + default_evaluation_times: Sequence[SupportsFloat] | Literal["Full"] = ( + 1.0, + ), + initial_state: StateType | None = None, # Default is ggg... + with_modulation: bool = False, + interaction_matrix: ArrayLike | None = None, + prefer_device_noise_model: bool = False, + noise_model: NoiseModel = NoiseModel(), + **backend_options: Any, + ) -> None: + """Initializes the EmulationConfig.""" + obs_tags = [] + if not observables: + warnings.warn( + f"{self.__class__.__name__!r} was initialized without any " + "observables. The corresponding emulation results will be" + " empty.", + stacklevel=2, + ) + + for obs in observables: + if not isinstance(obs, Observable): + raise TypeError( + "All entries in 'observables' must be instances of " + f"Observable. Instead, got instance of type {type(obs)}." + ) + obs_tags.append(obs.tag) + repeated_tags = [k for k, v in Counter(obs_tags).items() if v > 1] + if repeated_tags: + raise ValueError( + "Some of the provided 'observables' share identical tags. Use " + "'tag_suffix' when instantiating multiple instances of the " + "same observable so they can be distinguished. " + f"Repeated tags found: {repeated_tags}" + ) + if default_evaluation_times != "Full": + eval_times_arr = Observable._validate_eval_times( + list(map(float, default_evaluation_times)) + ) + default_evaluation_times = cast(Sequence[float], eval_times_arr) -@dataclass(frozen=True) + if initial_state is not None and not isinstance(initial_state, State): + raise TypeError( + "When defined, 'initial_state' must be an instance of State;" + f" got object of type {type(initial_state)} instead." + ) + + if interaction_matrix is not None: + interaction_matrix = pm.AbstractArray(interaction_matrix) + _shape = interaction_matrix.shape + if len(_shape) != 2 or _shape[0] != _shape[1]: + raise ValueError( + "'interaction_matrix' must be a square matrix. Instead, " + f"an array of shape {_shape} was given." + ) + if ( + initial_state is not None + and _shape[0] != initial_state.n_qudits + ): + raise ValueError( + f"The received interaction matrix of shape {_shape} is " + "incompatible with the received initial state of " + f"{initial_state.n_qudits} qudits." + ) + matrix_arr = interaction_matrix.as_array(detach=True) + if not np.allclose(matrix_arr, matrix_arr.transpose()): + raise ValueError( + "The received interaction matrix is not symmetric." + ) + if np.any(np.diag(matrix_arr) != 0): + warnings.warn( + "The received interaction matrix has non-zero values in " + "its diagonal; keep in mind that these values are " + "ignored.", + stacklevel=2, + ) + + if not isinstance(noise_model, NoiseModel): + raise TypeError( + "'noise_model' must be a NoiseModel instance," + f" not {type(noise_model)}." + ) + + super().__init__( + observables=tuple(observables), + default_evaluation_times=default_evaluation_times, + initial_state=initial_state, + with_modulation=bool(with_modulation), + interaction_matrix=interaction_matrix, + prefer_device_noise_model=bool(prefer_device_noise_model), + noise_model=noise_model, + **backend_options, + ) + + def _expected_kwargs(self) -> set[str]: + return super()._expected_kwargs() | { + "observables", + "default_evaluation_times", + "initial_state", + "with_modulation", + "interaction_matrix", + "prefer_device_noise_model", + "noise_model", + } + + def is_evaluation_time(self, t: float, tol: float = 1e-6) -> bool: + """Assesses whether a relative time is an evaluation time.""" + return ( + self.default_evaluation_times == "Full" and 0.0 <= t <= 1.0 + ) or ( + self.is_time_in_evaluation_times( + t, self.default_evaluation_times, tol=tol + ) + ) + + @staticmethod + def is_time_in_evaluation_times( + t: float, evaluation_times: ArrayLike, tol: float = 1e-6 + ) -> bool: + """Checks if a time is within a collection of evaluation times.""" + return 0.0 <= t <= 1.0 and bool( + np.any(np.abs(np.array(evaluation_times, dtype=float) - t) <= tol) + ) + + +# Legacy class + + +@dataclass class EmulatorConfig(BackendConfig): """The configuration for emulator backends. + Warning: + This class will be deprecated in favor of EmulationConfig once all + backends migrate to it. + Args: backend_options: A dictionary of backend-specific options. sampling_rate: The fraction of samples to extract from the pulse @@ -74,6 +303,7 @@ class EmulatorConfig(BackendConfig): `prefer_device_noise_model=True`. """ + backend_options: dict[str, Any] = field(default_factory=dict) sampling_rate: float = 1.0 evaluation_times: float | Sequence[float] | EVAL_TIMES_LITERAL = "Full" initial_state: Literal["all-ground"] | Sequence[complex] | np.ndarray = ( @@ -84,6 +314,7 @@ class EmulatorConfig(BackendConfig): noise_model: NoiseModel = field(default_factory=NoiseModel) def __post_init__(self) -> None: + # TODO: Deprecate once QutipBackendV2 is feature complete if not (0 < self.sampling_rate <= 1.0): raise ValueError( "The sampling rate (`sampling_rate` = " diff --git a/pulser-core/pulser/backend/default_observables.py b/pulser-core/pulser/backend/default_observables.py new file mode 100644 index 000000000..3e50a8ff6 --- /dev/null +++ b/pulser-core/pulser/backend/default_observables.py @@ -0,0 +1,416 @@ +# Copyright 2024 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. +"""Defines the default observables.""" +from __future__ import annotations + +import copy +import functools +from collections import Counter +from collections.abc import Sequence +from typing import Any, Type + +import pulser.math as pm +from pulser.backend.config import EmulationConfig +from pulser.backend.observable import Observable +from pulser.backend.operator import Operator, OperatorType +from pulser.backend.state import Eigenstate, State, StateType + + +class StateResult(Observable): + """Stores the quantum state at the evaluation times. + + Args: + evaluation_times: The relative times at which to store the state. + If left as `None`, uses the ``default_evaluation_times`` of the + backend's ``EmulationConfig``. + tag_suffix: An optional suffix to append to the tag. Needed if + multiple instances of the same observable are given to the + same EmulationConfig. + """ + + @property + def _base_tag(self) -> str: + return "state" + + def apply(self, *, state: StateType, **kwargs: Any) -> StateType: + """Calculates the observable to store in the Results.""" + return copy.deepcopy(state) + + +class BitStrings(Observable): + """Stores bitstrings sampled from the state at the evaluation times. + + Error rates are taken from the NoiseModel passed to the backend via + the EmulationConfig. + The bitstrings are stored as a Counter[str]. + + Args: + evaluation_times: The relative times at which to sample bitstrings. + If left as `None`, uses the ``default_evaluation_times`` of the + backend's ``EmulationConfig``. + num_shots: How many bitstrings to sample each time this observable + is computed. + one_state: The eigenstate that measures to 1. Can be left undefined + if the state's eigenstates form a known eigenbasis with a + defined "one state". + tag_suffix: An optional suffix to append to the tag. Needed if + multiple instances of the same observable are given to the + same EmulationConfig. + """ + + def __init__( + self, + *, + evaluation_times: Sequence[float] | None = None, + num_shots: int = 1000, + one_state: Eigenstate | None = None, + tag_suffix: str | None = None, + ): + """Initializes the observable.""" + super().__init__( + evaluation_times=evaluation_times, tag_suffix=tag_suffix + ) + if num_shots < 1: + raise ValueError( + "'num_shots' must be greater than or equal to 1, " + f"not {num_shots}." + ) + self.num_shots = int(num_shots) + self.one_state = one_state + + @property + def _base_tag(self) -> str: + return "bitstrings" + + def apply( + self, + *, + config: EmulationConfig, + state: State, + **kwargs: Any, + ) -> Counter[str]: + """Calculates the observable to store in the Results.""" + return state.sample( + num_shots=self.num_shots, + one_state=self.one_state, + p_false_pos=config.noise_model.p_false_pos, + p_false_neg=config.noise_model.p_false_neg, + ) + + +class Fidelity(Observable): + """Stores the fidelity with a pure state at the evaluation times. + + The fidelity uses the overlap between the given state and the state of + the system at each evaluation time. For pure states, this corresponds + to ``|<ψ|φ(t)>|^2`` for the given state ``|ψ>`` and the state ``|φ(t)>`` + obtained by time evolution. + + Args: + state: The state ``|ψ>``. Note that this must be of an appropriate type + for the backend. + evaluation_times: The relative times at which to compute the fidelity. + If left as `None`, uses the ``default_evaluation_times`` of the + backend's ``EmulationConfig``. + tag_suffix: An optional suffix to append to the tag. Needed if + multiple instances of the same observable are given to the + same EmulationConfig. + """ + + def __init__( + self, + state: State, + *, + evaluation_times: Sequence[float] | None = None, + tag_suffix: str | None = None, + ): + """Initializes the observable.""" + super().__init__( + evaluation_times=evaluation_times, tag_suffix=tag_suffix + ) + if not isinstance(state, State): + raise TypeError( + f"'state' must be a State instance; got {type(state)} instead." + ) + self.state = state + + @property + def _base_tag(self) -> str: + return "fidelity" + + def apply(self, *, state: State, **kwargs: Any) -> Any: + """Calculates the observable to store in the Results.""" + return self.state.overlap(state) + + +class Expectation(Observable): + """Stores the expectation of the given operator on the current state. + + Args: + evaluation_times: The relative times at which to compute the + expectation value. If left as `None`, uses the + ``default_evaluation_times`` of the backend's ``EmulationConfig``. + operator: The operator to measure. Must be of the appropriate type + for the backend. + tag_suffix: An optional suffix to append to the tag. Needed if + multiple instances of the same observable are given to the + same EmulationConfig. + """ + + def __init__( + self, + operator: Operator, + *, + evaluation_times: Sequence[float] | None = None, + tag_suffix: str | None = None, + ): + """Initializes the observable.""" + super().__init__( + evaluation_times=evaluation_times, tag_suffix=tag_suffix + ) + if not isinstance(operator, Operator): + raise TypeError( + "'operator' must be an Operator instance;" + f" got {type(operator)} instead." + ) + self.operator = operator + + @property + def _base_tag(self) -> str: + return "expectation" + + def apply(self, *, state: State, **kwargs: Any) -> Any: + """Calculates the observable to store in the Results.""" + return self.operator.expect(state) + + +class CorrelationMatrix(Observable): + """Stores the correlation matrix for the current state. + + The correlation matrix is calculated as + ``[[<φ(t)|n_i n_j|φ(t)> for j in qubits] for i in qubits]`` + where ``n_k = |one_state> str: + return "correlation_matrix" + + @staticmethod + @functools.cache + def _get_number_operator( + qudit_ids: frozenset[int], + n_qudits: int, + eigenstates: Sequence[Eigenstate], + one_state: Eigenstate, + op_type: Type[OperatorType], + ) -> OperatorType: + n_op = {one_state * 2: 1.0} + return op_type.from_operator_repr( + eigenstates=eigenstates, + n_qudits=n_qudits, + operations=[(1.0, [(n_op, qudit_ids)])], + ) + + def apply( + self, *, state: State, hamiltonian: Operator, **kwargs: Any + ) -> list[list]: + """Calculates the observable to store in the Results.""" + + @functools.cache + def calc_expectation(qudit_ids: frozenset[int]) -> Any: + return self._get_number_operator( + qudit_ids, + state.n_qudits, + state.eigenstates, + self.one_state or state.infer_one_state(), + type(hamiltonian), + ).expect(state) + + return [ + [ + calc_expectation(frozenset((i, j))) + for j in range(state.n_qudits) + ] + for i in range(state.n_qudits) + ] + + +class Occupation(Observable): + """Stores the occupation number of an eigenstate on each qudit. + + For every qudit i, calculates ``<φ(t)|n_i|φ(t)>``, where + ``n_i = |one_state> str: + return "occupation" + + def apply( + self, *, state: State, hamiltonian: Operator, **kwargs: Any + ) -> list: + """Calculates the observable to store in the Results.""" + return [ + CorrelationMatrix._get_number_operator( + frozenset((i,)), + state.n_qudits, + state.eigenstates, + self.one_state or state.infer_one_state(), + type(hamiltonian), + ).expect(state) + for i in range(state.n_qudits) + ] + + +class Energy(Observable): + """Stores the energy of the system at the evaluation times. + + The energy is calculated as the expectation value of the Hamiltonian, + i.e. ``<φ(t)|H(t)|φ(t)>``. + + Args: + evaluation_times: The relative times at which to compute the energy. + If left as `None`, uses the ``default_evaluation_times`` of the + backend's ``EmulationConfig``. + tag_suffix: An optional suffix to append to the tag. Needed if + multiple instances of the same observable are given to the + same EmulationConfig. + """ + + @property + def _base_tag(self) -> str: + return "energy" + + def apply( + self, *, state: State, hamiltonian: Operator, **kwargs: Any + ) -> Any: + """Calculates the observable to store in the Results.""" + return hamiltonian.expect(state) + + +class EnergyVariance(Observable): + r"""Stores the varaiance of the Hamiltonian at the evaluation times. + + The variance of the Hamiltonian at time ``t`` is calculated by + ``<φ(t)|H(t)^2|φ(t)> - <φ(t)|H(t)|φ(t)>^2`` + + + Args: + evaluation_times: The relative times at which to compute the variance. + If left as `None`, uses the ``default_evaluation_times`` of the + backend's ``EmulationConfig``. + tag_suffix: An optional suffix to append to the tag. Needed if + multiple instances of the same observable are given to the + same EmulationConfig. + """ + + @property + def _base_tag(self) -> str: + return "energy_variance" + + def apply( + self, *, state: State, hamiltonian: Operator, **kwargs: Any + ) -> Any: + """Calculates the observable to store in the Results.""" + # This works for state vectors and density matrices and avoids + # squaring the hamiltonian + h_state = hamiltonian.apply_to(state) + result: pm.AbstractArray = pm.sqrt( + h_state.overlap(h_state).real + ) - state.overlap(h_state) + if not result.requires_grad: + return float(result) + # If the result requires_grad, return the AbstractArray + return result # pragma: no cover + + +class EnergySecondMoment(Observable): + """Stores the expectation value of ``H(t)^2`` at the evaluation times. + + Useful for computing the variance when averaging over many executions of + the program. + + Args: + evaluation_times: The relative times at which to compute the variance. + If left as `None`, uses the ``default_evaluation_times`` of the + backend's ``EmulationConfig``. + tag_suffix: An optional suffix to append to the tag. Needed if + multiple instances of the same observable are given to the + same EmulationConfig. + """ + + @property + def _base_tag(self) -> str: + return "energy_second_moment" + + def apply( + self, *, state: State, hamiltonian: Operator, **kwargs: Any + ) -> Any: + """Calculates the observable to store in the Results.""" + # This works for state vectors and density matrices and avoids + # squaring the hamiltonian + h_state = hamiltonian.apply_to(state) + result = pm.sqrt(h_state.overlap(h_state).real) + if not result.requires_grad: + return float(result) + return result # pragma: no cover diff --git a/pulser-core/pulser/backend/observable.py b/pulser-core/pulser/backend/observable.py new file mode 100644 index 000000000..23d5b12e7 --- /dev/null +++ b/pulser-core/pulser/backend/observable.py @@ -0,0 +1,199 @@ +# Copyright 2024 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. +"""Defines the abstract base class for a callback and an observable.""" +from __future__ import annotations + +import uuid +from abc import ABC, abstractmethod +from collections.abc import Sequence +from typing import TYPE_CHECKING, Any + +import numpy as np +from numpy.typing import ArrayLike + +from pulser.backend.operator import Operator +from pulser.backend.state import State + +if TYPE_CHECKING: + from pulser.backend.config import EmulationConfig + from pulser.backend.results import Results + + +class Callback(ABC): + """A general Callback that is called during the emulation.""" + + def __init__(self) -> None: + """Initializes a Callback.""" + self._uuid: uuid.UUID = uuid.uuid4() + + @property + def uuid(self) -> uuid.UUID: + """A universal unique identifier for this instance.""" + return self._uuid + + @abstractmethod + def __call__( + self, + config: EmulationConfig, + t: float, + state: State, + hamiltonian: Operator, + result: Results, + ) -> None: + """Specifies a call to the callback at a specific time. + + This is called after each time step performed by the emulator. + By default it calls `apply()` to compute a result and put it in Results + if t in self.evaluation_times. + It can be overloaded to define general callbacks that don't put results + in the Results object. + + Args: + config: The config object passed to the backend. + t: The relative time as a float between 0 and 1. + state: The current state. + hamiltonian: The Hamiltonian at this time. + result: The Results object to store the result in. + """ + pass + + +class Observable(Callback): + """The Observable abstract base class. + + Args: + evaluation_times: The times at which to add a result to Results. + If left as `None`, uses the ``default_evaluation_times`` of the + backend's ``EmulationConfig``. + tag_suffix: An optional suffix to append to the tag. Needed if + multiple instances of the same observable are given to the + same EmulationConfig. + """ + + def __init__( + self, + *, + evaluation_times: Sequence[float] | None = None, + tag_suffix: str | None = None, + ): + """Initializes the observable.""" + super().__init__() + if evaluation_times is not None: + self._validate_eval_times(evaluation_times) + self.evaluation_times = evaluation_times + self._tag_suffix = tag_suffix + + @property + @abstractmethod + def _base_tag(self) -> str: + pass + + @property + def tag(self) -> str: + """Label for the observable, used to index the Results object. + + Within a Results instance, all computed observables must have different + tags. + + Returns: + The tag of the observable. + """ + if self._tag_suffix is None: + return self._base_tag + return f"{self._base_tag}_{self._tag_suffix}" + + def __call__( + self, + config: EmulationConfig, + t: float, + state: State, + hamiltonian: Operator, + result: Results, + ) -> None: + """Specifies a call to the observable at a specific time. + + This is called after each time step performed by the emulator. + By default it calls `apply()` to compute a result and put it in Results + if t in self.evaluation_times. + It can be overloaded to define general callbacks that don't put results + in the Results object. + + Args: + config: The config object passed to the backend. + t: The relative time as a float between 0 and 1. + state: The current state. + hamiltonian: The Hamiltonian at this time. + result: The Results object to store the result in. + time_tol: Tolerance below which two time values are considered + equal. + """ + time_tol = ( + (0.5 / result.total_duration) if result.total_duration else 1e-6 + ) + if ( + self.evaluation_times is not None + and config.is_time_in_evaluation_times( + t, self.evaluation_times, tol=time_tol + ) + ) or config.is_evaluation_time(t, tol=time_tol): + value_to_store = self.apply( + config=config, state=state, hamiltonian=hamiltonian + ) + result._store(observable=self, time=t, value=value_to_store) + + @abstractmethod + def apply( + self, + *, + config: EmulationConfig, + state: State, + hamiltonian: Operator, + ) -> Any: + """Calculates the observable to store in the Results. + + Args: + config: The config object passed to the backend. + state: The current state. + hamiltonian: The Hamiltonian at this time. + + Returns: + The result to put in Results. + """ + pass + + def __repr__(self) -> str: + return f"{self.tag}:{self.uuid}" + + @staticmethod + def _validate_eval_times( + evaluation_times: ArrayLike | Sequence[float], + ) -> np.ndarray: + eval_times_arr = np.array(evaluation_times, dtype=float) + if np.any((eval_times_arr < 0.0) | (eval_times_arr > 1.0)): + raise ValueError( + "All evaluation times must be between 0. and 1. " + f"Instead, got {evaluation_times!r}." + ) + unique_eval_times = np.unique(eval_times_arr) + if unique_eval_times.size < eval_times_arr.size: + raise ValueError( + "Evaluation times must be unique but " + f"{evaluation_times!r} has repeated values." + ) + if not np.all(eval_times_arr[:-1] < eval_times_arr[1:]): + raise ValueError( + "Evaluation times must be in ascending order." + f"Instead, got {evaluation_times!r}." + ) + return eval_times_arr diff --git a/pulser-core/pulser/backend/operator.py b/pulser-core/pulser/backend/operator.py new file mode 100644 index 000000000..e7687da12 --- /dev/null +++ b/pulser-core/pulser/backend/operator.py @@ -0,0 +1,142 @@ +# Copyright 2024 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. +"""Defines the abstract base class for a quantum operator.""" +from __future__ import annotations + +from abc import ABC, abstractmethod +from collections.abc import Collection, Mapping, Sequence +from typing import Generic, Type, TypeVar + +from pulser.backend.state import Eigenstate, State + +ArgScalarType = TypeVar("ArgScalarType") +ReturnScalarType = TypeVar("ReturnScalarType") +StateType = TypeVar("StateType", bound=State) +OperatorType = TypeVar("OperatorType", bound="Operator") + +QuditOp = Mapping[str, ArgScalarType] # single qudit operator +TensorOp = Sequence[ + tuple[QuditOp, Collection[int]] +] # QuditOp applied to set of qudits +FullOp = Sequence[tuple[ArgScalarType, TensorOp]] # weighted sum of TensorOp + + +class Operator(ABC, Generic[ArgScalarType, ReturnScalarType, StateType]): + """Base class for a quantum operator.""" + + @abstractmethod + def apply_to(self, state: StateType, /) -> StateType: + """Apply the operator to a state. + + Args: + state: The state to apply this operator to. + + Returns: + The resulting state. + """ + pass + + @abstractmethod + def expect(self, state: StateType, /) -> ReturnScalarType: + """Compute the expectation value of self on the given state. + + Args: + state: The state with which to compute. + + Returns: + The expectation value. + """ + pass + + @abstractmethod + def __add__(self: OperatorType, other: OperatorType, /) -> OperatorType: + """Computes the sum of two operators. + + Args: + other: The other operator. + + Returns: + The summed operator. + """ + pass + + @abstractmethod + def __rmul__(self: OperatorType, scalar: ArgScalarType) -> OperatorType: + """Scale the operator by a scalar factor. + + Args: + scalar: The scalar factor. + + Returns: + The scaled operator. + """ + pass + + @abstractmethod + def __matmul__(self: OperatorType, other: OperatorType) -> OperatorType: + """Compose two operators where 'self' is applied after 'other'. + + Args: + other: The operator to compose with self. + + Returns: + The composed operator. + """ + pass + + @classmethod + @abstractmethod + def from_operator_repr( + cls: Type[OperatorType], + *, + eigenstates: Sequence[Eigenstate], + n_qudits: int, + operations: FullOp, + ) -> OperatorType: + """Create an operator from the operator representation. + + The full operator representation (``FullOp``) is a weigthed sum of + tensor operators (``TensorOp``), written as a sequence of coefficient + and tensor operator pairs, ie + + ``FullOp = Sequence[tuple[ScalarType, TensorOp]]`` + + Each ``TensorOp`` is itself a sequence of qudit operators (``QuditOp``) + applied to mutually exclusive sets of qudits (represented by their + indices), ie + + ``TensorOp = Sequence[tuple[QuditOp, Collection[int]]]`` + + Qudits without an associated ``QuditOp`` are applied the identity + operator. + + Finally, each ``QuditOp`` is represented as weighted sum of pre-defined + single-qudit operators. It is given as a mapping between a string + representation of the single-qudit operator and its respective + coefficient, ie + + ``QuditOp = Mapping[str, ScalarType]`` + + By default it identifies strings ``"ij"`` as single-qudit operators, + where ``i`` and ``j`` are eigenstates that denote ``|i> tuple[Result, ...]: + def results(self) -> tuple[Results, ...]: """The actual results, obtained after execution is done.""" - return self._results + return self._results_seq @property def batch_id(self) -> str: @@ -115,14 +115,14 @@ def get_batch_status(self) -> BatchStatus: """Gets the status of the batch linked to these results.""" return self._connection._get_batch_status(self._batch_id) - def get_available_results(self) -> dict[str, Result]: + def get_available_results(self) -> dict[str, Results]: """Returns the available results. Unlike the `results` property, this method does not raise an error if some of the jobs do not have results. Returns: - dict[str, Result]: A dictionary mapping the job ID to its results. + dict[str, Results]: A dictionary mapping the job ID to its results. Jobs with no result are omitted. """ results = { @@ -138,14 +138,14 @@ def get_available_results(self) -> dict[str, Result]: return results def __getattr__(self, name: str) -> Any: - if name == "_results": + if name == "_results_seq": try: - self._results = tuple( + self._results_seq = tuple( self._connection._fetch_result( self.batch_id, self._job_ids ) ) - return self._results + return self._results_seq except RemoteResultsError as e: raise RemoteResultsError( "Results are not available for all jobs. Use the " @@ -168,21 +168,21 @@ def submit( open: bool = True, batch_id: str | None = None, **kwargs: Any, - ) -> RemoteResults | tuple[RemoteResults, ...]: + ) -> RemoteResults: """Submit a job for execution.""" pass @abstractmethod def _fetch_result( self, batch_id: str, job_ids: list[str] | None - ) -> typing.Sequence[Result]: + ) -> typing.Sequence[Results]: """Fetches the results of a completed batch.""" pass @abstractmethod def _query_job_progress( self, batch_id: str - ) -> Mapping[str, tuple[JobStatus, Result | None]]: + ) -> Mapping[str, tuple[JobStatus, Results | None]]: """Fetches the status and results of all the jobs in a batch. Unlike `_fetch_result`, this method does not raise an error if some @@ -320,7 +320,7 @@ def __init__( def run( self, job_params: list[JobParams] | None = None, wait: bool = False - ) -> RemoteResults | tuple[RemoteResults, ...]: + ) -> RemoteResults: """Runs the sequence on the remote backend and returns the result. Args: @@ -377,13 +377,10 @@ def __init__(self, backend: RemoteBackend) -> None: self.backend = backend def __enter__(self) -> _OpenBatchContextManager: - batch = cast( - RemoteResults, - self.backend._connection.submit( - self.backend._sequence, - open=True, - **self.backend._submit_kwargs(), - ), + batch = self.backend._connection.submit( + self.backend._sequence, + open=True, + **self.backend._submit_kwargs(), ) self.backend._batch_id = batch.batch_id return self diff --git a/pulser-core/pulser/backend/results.py b/pulser-core/pulser/backend/results.py new file mode 100644 index 000000000..21c97611d --- /dev/null +++ b/pulser-core/pulser/backend/results.py @@ -0,0 +1,165 @@ +# Copyright 2024 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. +"""Defines the base class for storing backend results.""" +from __future__ import annotations + +import collections.abc +import typing +import uuid +from dataclasses import dataclass, field +from typing import Any, TypeVar, overload + +from pulser.backend.observable import Observable + + +@dataclass +class Results: + """A collection of results. + + Args: + atom_order: The order of the atoms/qudits in the results. + total_duration: The total duration of the sequence, in ns. + """ + + atom_order: tuple[str, ...] + total_duration: int + _results: dict[uuid.UUID, list[Any]] = field(init=False) + _times: dict[uuid.UUID, list[float]] = field(init=False) + _tagmap: dict[str, uuid.UUID] = field(init=False) + + def __post_init__(self) -> None: + self._results = {} + self._times = {} + self._tagmap = {} + + def _store( + self, *, observable: Observable, time: float, value: Any + ) -> None: + """Store the result of an observable at a specific time. + + Args: + observable: The observable computing the result. + time: The relative time at which the observable was taken. + value: The value of the observable. + """ + _times = self._times.setdefault(observable.uuid, []) + if time in _times: + raise RuntimeError( + f"A value is already stored for observable '{observable.tag}'" + f" at time {time}." + ) + self._tagmap[observable.tag] = observable.uuid + assert ( + _times == [] or _times[-1] < time + ), "Evaluation times are not sorted." + _times.append(time) + self._results.setdefault(observable.uuid, []).append(value) + assert len(_times) == len(self._results[observable.uuid]) + + def __getattr__(self, name: str) -> list[Any]: + if name in self._tagmap: + return list(self._results[self._tagmap[name]]) + raise AttributeError(f"{name!r} is not in the results.") + + def get_result_tags(self) -> list[str]: + """Get a list of results tags present in this object.""" + return list(self._tagmap.keys()) + + def get_result_times(self, observable: Observable | str) -> list[float]: + """Get a list of times for which the given result has been stored. + + Args: + observable: The observable instance used to calculate the result + or its tag. + + Returns: + List of relative times. + """ + return list(self._times[self._find_uuid(observable)]) + + def get_result(self, observable: Observable | str, time: float) -> Any: + """Get the a specific result at a given time. + + Args: + observable: The observable instance used to calculate the result + or its tag. + time: Relative time at which to get the result. + + Returns: + The result. + """ + obs_uuid = self._find_uuid(observable) + try: + ind = self._times[obs_uuid].index(time) + return self._results[obs_uuid][ind] + except (KeyError, ValueError): + raise ValueError( + f"{observable!r} is not available at time {time}." + ) + + def get_tagged_results(self) -> dict[str, list[Any]]: + """Gets the results for every tag. + + Returns: + A mapping between a tag and the results associated to it, + at every evaluation time. + """ + return { + tag: list(self._results[uuid_]) + for tag, uuid_ in self._tagmap.items() + } + + def _find_uuid(self, observable: Observable | str) -> uuid.UUID: + if isinstance(observable, Observable): + if observable.uuid not in self._results: + raise ValueError( + f"'{observable!r}' has not been stored in the results" + ) + return observable.uuid + try: + return self._tagmap[observable] + except KeyError: + raise ValueError( + f"{observable!r} is not an Observable instance " + "nor a known observable tag in the results." + ) + + +ResultsType = TypeVar("ResultsType", bound=Results) + + +class ResultsSequence(typing.Sequence[ResultsType]): + """An immutable sequence of results.""" + + _results_seq: tuple[ResultsType, ...] + + @overload + def __getitem__(self, key: int) -> ResultsType: + pass + + @overload + def __getitem__(self, key: slice) -> tuple[ResultsType, ...]: + pass + + def __getitem__( + self, key: int | slice + ) -> ResultsType | tuple[ResultsType, ...]: + return self._results_seq[key] + + def __len__(self) -> int: + return len(self._results_seq) + + def __iter__(self) -> collections.abc.Iterator[ResultsType]: + for res in self._results_seq: + yield res diff --git a/pulser-core/pulser/backend/state.py b/pulser-core/pulser/backend/state.py new file mode 100644 index 000000000..393045476 --- /dev/null +++ b/pulser-core/pulser/backend/state.py @@ -0,0 +1,174 @@ +# Copyright 2024 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. +"""Defines the abstract base class for a quantum state.""" +from __future__ import annotations + +from abc import ABC, abstractmethod +from collections import Counter +from collections.abc import Sequence +from typing import Generic, Literal, Type, TypeVar, Union + +import numpy as np + +from pulser.channels.base_channel import States + +Eigenstate = Union[States, Literal["0", "1"]] + +ArgScalarType = TypeVar("ArgScalarType") +ReturnScalarType = TypeVar("ReturnScalarType") +StateType = TypeVar("StateType", bound="State") + + +class State(ABC, Generic[ArgScalarType, ReturnScalarType]): + """Base class enforcing an API for quantum states. + + Each backend will implement its own type of state and the methods below. + """ + + _eigenstates: Sequence[Eigenstate] + + @property + @abstractmethod + def n_qudits(self) -> int: + """The number of qudits in the state.""" + pass + + @property + def eigenstates(self) -> tuple[Eigenstate, ...]: + """The eigenstates that form a qudit's eigenbasis. + + The order of the states should match the order in a numerical (ie + state vector or density matrix) representation. + For example, with eigenstates ("a", "b", ...), "a" will be associated + to eigenvector (1, 0, ...), "b" to (0, 1, ...) and so on. + """ + return tuple(self._eigenstates) + + @property + def qudit_dim(self) -> int: + """The dimensions (ie number of eigenstates) of a qudit.""" + return len(self.eigenstates) + + def get_basis_state_from_index(self, index: int) -> str: + """Generates a basis state combination from its index in the state. + + Assumes a state vector representation regardless of the actual support + of the state. + + Args: + index: The position of the state in a state vector. + + Returns: + The basis state combination for the desired index. + """ + if index < 0: + raise ValueError( + f"'index' must be a non-negative integer;" + f" got {index} instead." + ) + return "".join( + self.eigenstates[int(dig)] + for dig in np.base_repr(index, base=self.qudit_dim).zfill( + self.n_qudits + ) + ) + + @abstractmethod + def overlap(self: StateType, other: StateType, /) -> ReturnScalarType: + """Compute the overlap between this state and another of the same type. + + Generally computes ``Tr[AB]`` for mixed states ``A`` and ``B``, which + corresponds to ``||^2`` for pure states ``A=|a> Counter[str]: + """Sample bitstrings from the state, taking into account error rates. + + Args: + num_shots: How many bitstrings to sample. + one_state: The eigenstate that measures to 1. Can be left undefined + if the state's eigenstates form a known eigenbasis with a + defined "one state". + p_false_pos: The rate at which a 0 is read as a 1. + p_false_neg: The rate at which a 1 is read as a 0. + + Returns: + The measured bitstrings, by count. + """ + pass + + @classmethod + @abstractmethod + def from_state_amplitudes( + cls: Type[StateType], + *, + eigenstates: Sequence[Eigenstate], + amplitudes: dict[str, ArgScalarType], + ) -> StateType: + """Construct the state from its basis states' amplitudes. + + Args: + eigenstates: The basis states (e.g., ('r', 'g')). + amplitudes: A mapping between basis state combinations and + complex amplitudes. + + Returns: + The state constructed from the amplitudes. + """ + pass + + def infer_one_state(self) -> Eigenstate: + """Infers the state measured as 1 from the eigenstates. + + Only works when the eigenstates form a known eigenbasis with + a well-defined "one state". + """ + eigenstates = set(self.eigenstates) + if eigenstates == {"0", "1"}: + return "1" + if eigenstates == {"r", "g"}: + return "r" + if eigenstates == {"g", "h"}: + return "h" + if eigenstates == {"u", "d"}: + return "d" + raise RuntimeError( + "Failed to infer the 'one state' from the " + f"eigenstates: {self.eigenstates}" + ) + + @staticmethod + def _validate_eigenstates(eigenstates: Sequence[Eigenstate]) -> None: + if any(not isinstance(s, str) or len(s) != 1 for s in eigenstates): + raise ValueError( + "All eigenstates must be represented by single characters." + ) + if len(eigenstates) != len(set(eigenstates)): + raise ValueError("'eigenstates' can't contain repeated entries.") diff --git a/pulser-core/pulser/register/register.py b/pulser-core/pulser/register/register.py index 27fa36102..4f211f28f 100644 --- a/pulser-core/pulser/register/register.py +++ b/pulser-core/pulser/register/register.py @@ -356,10 +356,7 @@ def with_automatic_layout( raise TypeError( f"'device' must be of type Device, not {type(device)}." ) - if ( - self._coords_arr.is_tensor - and self._coords_arr.as_tensor().requires_grad - ): + if self._coords_arr.requires_grad: raise NotImplementedError( "'Register.with_automatic_layout()' does not support " "registers with differentiable coordinates." diff --git a/pulser-core/pulser/result.py b/pulser-core/pulser/result.py index 717fd7ef7..d8b434c6d 100644 --- a/pulser-core/pulser/result.py +++ b/pulser-core/pulser/result.py @@ -14,27 +14,43 @@ """Classes to store measurement results.""" from __future__ import annotations -import collections.abc -import typing +import uuid +import warnings from abc import ABC, abstractmethod from collections import Counter -from dataclasses import dataclass -from typing import Any, TypeVar, overload +from dataclasses import dataclass, field +from typing import Any import matplotlib.pyplot as plt import numpy as np -from pulser.register import QubitId +import pulser.backend.results as backend_results +from pulser.backend.default_observables import BitStrings -__all__ = ["Result", "SampledResult", "Results", "ResultType"] + +def __getattr__(name: str) -> Any: + name_map = {"Results": "ResultsSequence", "ResultType": "ResultsType"} + if name not in name_map: + raise AttributeError(f"Module {__name__!r} has no attribute {name!r}.") + warnings.warn( + f"The 'pulser.result.{name}' class has been renamed to " + f"'{name_map[name]}' and moved to 'pulser.backend.results'. " + "Importing it as '{name}' from 'pulser.results' is deprecated.", + DeprecationWarning, + stacklevel=3, + ) + return getattr(backend_results, name_map[name]) + + +__all__ = ["Result", "SampledResult"] @dataclass -class Result(ABC): - """Base class for storing the result of a sequence run.""" +class Result(ABC, backend_results.Results): + """Base class for storing the result of an observable at specific time.""" - atom_order: tuple[QubitId, ...] meas_basis: str + total_duration: int = field(default=0, init=False) @property def sampling_dist(self) -> dict[str, float]: @@ -136,12 +152,28 @@ class SampledResult(Result): meas_basis: The measurement basis. bitstring_counts: The number of times each bitstring was measured. + evaluation_time: Relative time at which the samples were + taken. """ bitstring_counts: dict[str, int] + evaluation_time: float = 1.0 def __post_init__(self) -> None: + super().__post_init__() self.n_samples = sum(self.bitstring_counts.values()) + # TODO: Make sure this is not too hacky + bitstrings_obs = BitStrings() + # Override UUID so that two SampledResult instances with + # the same counts are identical + bitstrings_obs._uuid = uuid.UUID( + "00000000-0000-0000-0000-000000000000" + ) + self._store( + observable=bitstrings_obs, + time=self.evaluation_time, + value=self.bitstring_counts, + ) @property def sampling_errors(self) -> dict[str, float]: @@ -159,32 +191,3 @@ def _weights(self) -> np.ndarray: for bitstr, counts in self.bitstring_counts.items(): weights[int(bitstr, base=2)] = counts / self.n_samples return weights / sum(weights) - - -ResultType = TypeVar("ResultType", bound=Result) - - -class Results(typing.Sequence[ResultType]): - """An immutable sequence of results.""" - - _results: tuple[ResultType, ...] - - @overload - def __getitem__(self, key: int) -> ResultType: - pass - - @overload - def __getitem__(self, key: slice) -> tuple[ResultType, ...]: - pass - - def __getitem__( - self, key: int | slice - ) -> ResultType | tuple[ResultType, ...]: - return self._results[key] - - def __len__(self) -> int: - return len(self._results) - - def __iter__(self) -> collections.abc.Iterator[ResultType]: - for res in self._results: - yield res diff --git a/pulser-core/pulser/waveforms.py b/pulser-core/pulser/waveforms.py index 08b1684b6..45df107b5 100644 --- a/pulser-core/pulser/waveforms.py +++ b/pulser-core/pulser/waveforms.py @@ -190,7 +190,7 @@ def modulated_samples( The array of samples after modulation. """ detach = True # We detach unless... - if self.samples.is_tensor and self.samples.as_tensor().requires_grad: + if self.samples.requires_grad: # ... the samples require grad. In this case, we clear the cache # so that the modulation is recalculated with the current samples self._modulated_samples.cache_clear() diff --git a/pulser-pasqal/pulser_pasqal/backends.py b/pulser-pasqal/pulser_pasqal/backends.py index 1051178ec..d04160adb 100644 --- a/pulser-pasqal/pulser_pasqal/backends.py +++ b/pulser-pasqal/pulser_pasqal/backends.py @@ -59,7 +59,7 @@ def __init__( def run( self, job_params: list[JobParams] | None = None, wait: bool = False - ) -> RemoteResults | tuple[RemoteResults, ...]: + ) -> RemoteResults: """Executes on the emulator backend through the Pasqal Cloud. Args: diff --git a/pulser-simulation/pulser_simulation/__init__.py b/pulser-simulation/pulser_simulation/__init__.py index 442df6903..170b22969 100644 --- a/pulser-simulation/pulser_simulation/__init__.py +++ b/pulser-simulation/pulser_simulation/__init__.py @@ -16,6 +16,8 @@ from pulser import EmulatorConfig, NoiseModel from pulser_simulation._version import __version__ as __version__ +from pulser_simulation.qutip_state import QutipState +from pulser_simulation.qutip_op import QutipOperator from pulser_simulation.qutip_backend import QutipBackend from pulser_simulation.simconfig import SimConfig from pulser_simulation.simulation import QutipEmulator @@ -25,6 +27,8 @@ __all__ = [ "EmulatorConfig", "NoiseModel", + "QutipState", + "QutipOperator", "QutipBackend", "QutipEmulator", "SimConfig", diff --git a/pulser-simulation/pulser_simulation/qutip_op.py b/pulser-simulation/pulser_simulation/qutip_op.py new file mode 100644 index 000000000..876ab5271 --- /dev/null +++ b/pulser-simulation/pulser_simulation/qutip_op.py @@ -0,0 +1,270 @@ +# Copyright 2024 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. +"""Definition of QutipState and QutipOperator.""" +from __future__ import annotations + +from collections.abc import Collection, Mapping, Sequence +from typing import Any, SupportsComplex, Type, TypeVar, cast + +import qutip + +from pulser.backend.operator import Operator +from pulser.backend.state import Eigenstate +from pulser_simulation.qutip_state import QutipState + +QutipStateType = TypeVar("QutipStateType", bound=QutipState) +QutipOperatorType = TypeVar("QutipOperatorType", bound="QutipOperator") + +QuditOp = Mapping[str, SupportsComplex] +TensorOp = Sequence[tuple[QuditOp, Collection[int]]] +FullOp = Sequence[tuple[SupportsComplex, TensorOp]] + + +class QutipOperator(Operator[SupportsComplex, complex, QutipStateType]): + """A quantum operator stored as a qutip.Qobj. + + Args: + state: The operator as a qutip.Qobj. + eigenstates: The eigenstates that form a qudit's eigenbasis, each + given as an individual character. The order of the eigenstates + matters, as for eigenstates ("a", "b", ...), "a" will be + associated to eigenvector (1, 0, ...), "b" to (0, 1, ...) and + so on. + + """ + + def __init__( + self, operator: qutip.Qobj, eigenstates: Sequence[Eigenstate] + ): + """Initializes a QutipOperator.""" + QutipState._validate_eigenstates(eigenstates) + self._eigenstates = tuple(eigenstates) + if not isinstance(operator, qutip.Qobj) or not operator.isoper: + raise TypeError( + "'operator' must be a qutip.Qobj with type 'oper', not " + f"{operator!r}." + ) + QutipState._validate_shape(operator.shape, len(self._eigenstates)) + self._operator = operator + + @property + def eigenstates(self) -> tuple[Eigenstate, ...]: + """The eigenstates that form a qudit's eigenbasis. + + The order of the states should match the order in a numerical (ie + state vector or density matrix) representation. + For example, with eigenstates ("a", "b", ...), "a" will be associated + to eigenvector (1, 0, ...), "b" to (0, 1, ...) and so on. + """ + return tuple(self._eigenstates) + + def to_qobj(self) -> qutip.Qobj: + """Returns a copy of the operators's Qobj representation.""" + return self._operator.copy() + + def apply_to(self, state: QutipStateType, /) -> QutipStateType: + """Apply the operator to a state. + + Args: + state: The state to apply this operator to. + + Returns: + The resulting state. + """ + self._validate_other(state, QutipState, "QutipOperator.apply_to()") + out = self._operator * state._state + if state._state.isoper: + out = out * self._operator.dag() + return type(state)(out, eigenstates=state.eigenstates) + + def expect(self, state: QutipState, /) -> complex: + """Compute the expectation value of self on the given state. + + Args: + state: The state with which to compute. + + Returns: + The expectation value. + """ + self._validate_other(state, QutipState, "QutipOperator.expect()") + return cast(complex, qutip.expect(self._operator, state._state)) + + def __add__( + self: QutipOperatorType, other: QutipOperatorType, / + ) -> QutipOperatorType: + """Computes the sum of two operators. + + Args: + other: The other operator. + + Returns: + The summed operator. + """ + self._validate_other(other, QutipOperator, "__add__") + return type(self)( + self._operator + other._operator, eigenstates=self.eigenstates + ) + + def __rmul__( + self: QutipOperatorType, scalar: SupportsComplex + ) -> QutipOperatorType: + """Scale the operator by a scalar factor. + + Args: + scalar: The scalar factor. + + Returns: + The scaled operator. + """ + return type(self)( + complex(scalar) * self._operator, eigenstates=self.eigenstates + ) + + def __matmul__( + self: QutipOperatorType, other: QutipOperatorType + ) -> QutipOperatorType: + """Compose two operators where 'self' is applied after 'other'. + + Args: + other: The operator to compose with self. + + Returns: + The composed operator. + """ + self._validate_other(other, QutipOperator, "__matmul__") + return type(self)( + self._operator * other._operator, eigenstates=self.eigenstates + ) + + @classmethod + def from_operator_repr( + cls: Type[QutipOperatorType], + *, + eigenstates: Sequence[Eigenstate], + n_qudits: int, + operations: FullOp, + ) -> QutipOperatorType: + """Create an operator from the operator representation. + + The full operator representation (FullOp is a weigthed sum of tensor + operators (TensorOp), written as a sequence of coefficient and tensor + operator pairs, ie + + `FullOp = Sequence[tuple[ScalarType, TensorOp]]` + + Each TensorOp is itself a sequence of qudit operators (QuditOp) applied + to mutually exclusive sets of qudits (represented by their indices), ie + + `TensorOp = Sequence[tuple[QuditOp, Collection[int]]]` + + Qudits without an associated QuditOp are applied the identity operator. + + Finally, each QuditOp is represented as weighted sum of pre-defined + single-qudit operators. It is given as a mapping between a string + representation of the single-qudit operator and its respective + coefficient, ie + + `QuditOp = Mapping[str, ScalarType]` + + By default it identifies strings 'ij' as single-qudit operators, where + i and j are eigenstates that denote |i> qutip.Qobj: + op = qutip.identity(qudit_dim) * 0 + for proj_str, coeff in qudit_op.items(): + if len(proj_str) != 2 or any( + s_ not in eigenstates for s_ in proj_str + ): + raise ValueError( + "Every QuditOp key must be made up of two eigenstates;" + f" instead, got '{proj_str}'." + ) + ket = qutip.basis(qudit_dim, eigenstates.index(proj_str[0])) + bra = qutip.basis( + qudit_dim, eigenstates.index(proj_str[1]) + ).dag() + op += complex(coeff) * ket * bra + return op + + coeffs: list[complex] = [] + tensor_ops: list[qutip.Qobj] = [] + for tensor_op_num, (coeff, tensor_op) in enumerate(operations): + coeffs.append(complex(coeff)) + qudit_ops = [qutip.identity(qudit_dim) for _ in range(n_qudits)] + free_inds = set(range(n_qudits)) + for qudit_op, qudit_inds in tensor_op: + if bad_inds_ := (set(qudit_inds) - free_inds): + raise ValueError( + "Got invalid indices for a system with " + f"{n_qudits} qudits: {bad_inds_}. For TensorOp " + f"#{tensor_op_num}, only indices {free_inds} " + "were still available." + ) + for ind in qudit_inds: + qudit_ops[ind] = build_qudit_op(qudit_op) + free_inds.remove(ind) + tensor_ops.append(qutip.tensor(qudit_ops)) + + full_op: qutip.Qobj = sum(c * t for c, t in zip(coeffs, tensor_ops)) + return cls(full_op, eigenstates=eigenstates) + + def __repr__(self) -> str: + return "\n".join( + [ + "QutipOperator", + "-------------", + f"Eigenstates: {self.eigenstates}", + self._operator.__repr__(), + ] + ) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, QutipOperator): + return False + return ( + self.eigenstates == other.eigenstates + and self._operator == other._operator + ) + + def _validate_other( + self, + other: QutipState | QutipOperator, + expected_type: Type, + op_name: str, + ) -> None: + if not isinstance(other, expected_type): + raise TypeError( + f"'{op_name}' expects a '{expected_type.__name__}' instance, " + f"not {type(other)}." + ) + if self.eigenstates != other.eigenstates: + msg = ( + f"Can't apply {op_name} between a {self.__class__.__name__} " + f"with eigenstates {self.eigenstates} and a " + f"{other.__class__.__name__} with {other.eigenstates}." + ) + if set(self.eigenstates) != set(other.eigenstates): + raise ValueError(msg) + raise NotImplementedError(msg) diff --git a/pulser-simulation/pulser_simulation/qutip_result.py b/pulser-simulation/pulser_simulation/qutip_result.py index 1ae584ae2..f8158df36 100644 --- a/pulser-simulation/pulser_simulation/qutip_result.py +++ b/pulser-simulation/pulser_simulation/qutip_result.py @@ -25,7 +25,6 @@ States, get_states_from_bases, ) -from pulser.register import QubitId from pulser.result import Result @@ -43,10 +42,9 @@ class QutipResult(Result): same as the state's basis. """ - atom_order: tuple[QubitId, ...] - meas_basis: str state: qutip.Qobj matching_meas_basis: bool + evaluation_time: float = 1.0 @property def sampling_errors(self) -> dict[str, float]: diff --git a/pulser-simulation/pulser_simulation/qutip_state.py b/pulser-simulation/pulser_simulation/qutip_state.py new file mode 100644 index 000000000..2dd4090b7 --- /dev/null +++ b/pulser-simulation/pulser_simulation/qutip_state.py @@ -0,0 +1,286 @@ +# Copyright 2024 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. +"""Definition of QutipState and QutipOperator.""" +from __future__ import annotations + +import math +from collections import Counter, defaultdict +from collections.abc import Collection, Mapping, Sequence +from typing import Any, SupportsComplex, Type, TypeVar + +import numpy as np +import qutip + +from pulser.backend.state import Eigenstate, State + +QutipStateType = TypeVar("QutipStateType", bound="QutipState") + +QuditOp = Mapping[str, SupportsComplex] +TensorOp = Sequence[tuple[QuditOp, Collection[int]]] +FullOp = Sequence[tuple[SupportsComplex, TensorOp]] + + +class QutipState(State[SupportsComplex, complex]): + """A quantum state stored as a qutip.Qobj. + + Args: + state: The state as a qutip.Qobj. Can be a statevector + or a density matrix. + eigenstates: The eigenstates that form a qudit's eigenbasis, each + given as an individual character. The order of the eigenstates + matters, as for eigenstates ("a", "b", ...), "a" will be + associated to eigenvector (1, 0, ...), "b" to (0, 1, ...) and + so on. + """ + + def __init__( + self, state: qutip.Qobj, *, eigenstates: Sequence[Eigenstate] + ): + """Initializes a QutipState.""" + self._validate_eigenstates(eigenstates) + self._eigenstates = tuple(eigenstates) + valid_types = ("ket", "bra", "oper") + if not isinstance(state, qutip.Qobj) or state.type not in valid_types: + raise TypeError( + "'state' must be a qutip.Qobj with one of types " + f"{valid_types}, not {state!r}." + ) + self._state = state.dag() if state.isbra else state + self._validate_shape(self._state.shape, self.qudit_dim) + + @property + def n_qudits(self) -> int: + """The number of qudits in the state.""" + return round(math.log(self._state.shape[0], self.qudit_dim)) + + def to_qobj(self) -> qutip.Qobj: + """Returns a copy of the state's Qobj representation.""" + return self._state.copy() + + def overlap(self, other: QutipState) -> float: + """Compute the overlap between this state and another of the same type. + + Generally computes Tr[AB] for mixed states A and B, which + corresponds to ||^2 for pure states A=|a> dict[str, float]: + """Extracts the probabilties of measuring each basis state combination. + + The probabilities are normalized to sum to 1. + + Args: + cutoff: The value below which a probability is considered to be + zero. + + Returns: + A mapping between basis state combinations and their respective + probabilities. + """ + if not self._state.isket: + probs = np.abs(self._state.diag()).real + else: + probs = (np.abs(self._state.full()) ** 2).flatten().real + non_zero = np.argwhere(probs > cutoff).flatten() + # Renormalize to make the non-zero probablilites sum to 1. + probs = probs[non_zero] + probs = probs / np.sum(probs) + return dict(zip(map(self.get_basis_state_from_index, non_zero), probs)) + + def bitstring_probabilities( + self, *, one_state: Eigenstate | None = None, cutoff: float = 1e-12 + ) -> Mapping[str, float]: + """Extracts the probabilties of measuring each bitstring. + + Args: + one_state: The eigenstate that measures to 1. Can be left undefined + if the state's eigenstates form a known eigenbasis with a + defined "one state". + cutoff: The value below which a probability is considered to be + zero. + + Returns: + A mapping between bitstrings and their respective probabilities. + """ + one_state = one_state or self.infer_one_state() + zero_states = set(self.eigenstates) - {one_state} + probs = self.probabilities(cutoff=cutoff) + bitstring_probs: dict[str, float] = defaultdict(float) + for state_str in probs: + bitstring = state_str.replace(one_state, "1") + for s_ in zero_states: + bitstring = bitstring.replace(s_, "0") + bitstring_probs[bitstring] += probs[state_str] + return dict(bitstring_probs) + + def sample( + self, + *, + num_shots: int, + one_state: Eigenstate | None = None, + p_false_pos: float = 0.0, + p_false_neg: float = 0.0, + ) -> Counter[str]: + """Sample bitstrings from the state, taking into account error rates. + + Args: + num_shots: How many bitstrings to sample. + one_state: The eigenstate that measures to 1. Can be left undefined + if the state's eigenstates form a known eigenbasis with a + defined "one state". + p_false_pos: The rate at which a 0 is read as a 1. + p_false_neg: The rate at which a 1 is read as a 0. + + Returns: + The measured bitstrings, by count. + """ + bitstring_probs = self.bitstring_probabilities( + one_state=one_state, cutoff=1 / (1000 * num_shots) + ) + bitstrings = np.array(list(bitstring_probs)) + probs = np.array(list(map(float, bitstring_probs.values()))) + dist = np.random.multinomial(num_shots, probs) + # Filter out bitstrings without counts + non_zero_counts = dist > 0 + bitstrings = bitstrings[non_zero_counts] + dist = dist[non_zero_counts] + if p_false_pos == 0.0 and p_false_neg == 0.0: + return Counter(dict(zip(bitstrings, dist))) + + # Convert bitstrings to a 2D array + bitstr_arr = np.array([list(bs) for bs in bitstrings], dtype=int) + # If 1 is measured, flip_prob=p_false_neg else flip_prob=p_false_pos + flip_probs = np.where(bitstr_arr == 1, p_false_neg, p_false_pos) + # Repeat flip_probs of a bitstring as many times as it was measured + flip_probs_repeated = np.repeat(flip_probs, dist, axis=0) + # Generate random matrix of same shape + random_matrix = np.random.uniform(size=flip_probs_repeated.shape) + # Compare random matrix with flip probabilities to get the flips + flips = random_matrix < flip_probs_repeated + # Apply the flips with an XOR between original array and flips + new_bitstrings = bitstr_arr.repeat(dist, axis=0) ^ flips + + # Count all the new_bitstrings + # Not converting to str right away because tuple indexing is faster + new_counts: Counter = Counter(map(tuple, new_bitstrings)) + return Counter( + {"".join(map(str, k)): v for k, v in new_counts.items()} + ) + + @classmethod + def from_state_amplitudes( + cls: Type[QutipStateType], + *, + eigenstates: Sequence[Eigenstate], + amplitudes: dict[str, SupportsComplex], + ) -> QutipStateType: + """Construct the state from its basis states' amplitudes. + + Args: + eigenstates: The basis states (e.g., ('r', 'g')). + amplitudes: A mapping between basis state combinations and + complex amplitudes. + + Returns: + The state constructed from the amplitudes. + """ + cls._validate_eigenstates(eigenstates) + basis_states = list(amplitudes) + n_qudits = len(basis_states[0]) + if not all( + len(bs) == n_qudits and set(bs) <= set(eigenstates) + for bs in basis_states + ): + raise ValueError( + "All basis states must be combinations of eigenstates with the" + f" same length. Expected combinations of {eigenstates}, each " + f"with {n_qudits} elements." + ) + + qudit_dim = len(eigenstates) + + def make_qobj(basis_state: str) -> qutip.Qobj: + return qutip.tensor( + [ + qutip.basis(qudit_dim, eigenstates.index(s)) + for s in basis_state + ] + ) + + # Start with an empty Qobj with the right dimension + state = make_qobj(eigenstates[0] * n_qudits) * 0 + for basis_state, amp in amplitudes.items(): + state += complex(amp) * make_qobj(basis_state) + + return cls(state, eigenstates=eigenstates) + + def __repr__(self) -> str: + return "\n".join( + [ + "QutipState", + "----------", + f"Eigenstates: {self.eigenstates}", + self._state.__repr__(), + ] + ) + + def __eq__(self, other: Any) -> bool: + if not isinstance(other, QutipState): + return False + return ( + self.eigenstates == other.eigenstates + and self._state == other._state + ) + + @staticmethod + def _validate_shape(shape: tuple[int, int], qudit_dim: int) -> None: + expected_n_qudits = math.log(shape[0], qudit_dim) + if not np.isclose(expected_n_qudits, round(expected_n_qudits)): + raise ValueError( + f"A qutip.Qobj with shape {shape} is incompatible with " + f"a system of {qudit_dim}-level qudits." + ) diff --git a/pulser-simulation/pulser_simulation/simresults.py b/pulser-simulation/pulser_simulation/simresults.py index c4156fc61..e3e490e6c 100644 --- a/pulser-simulation/pulser_simulation/simresults.py +++ b/pulser-simulation/pulser_simulation/simresults.py @@ -20,7 +20,7 @@ from abc import ABC, abstractmethod from collections import Counter from functools import lru_cache -from typing import Mapping, Optional, Tuple, Union, cast +from typing import Mapping, Optional, Tuple, TypeVar, Union, cast import matplotlib.pyplot as plt import numpy as np @@ -28,11 +28,14 @@ from numpy.typing import ArrayLike from qutip.piqs.piqs import isdiagonal -from pulser.result import Results, ResultType, SampledResult +from pulser.backend.results import ResultsSequence +from pulser.result import SampledResult from pulser_simulation.qutip_result import QutipResult +ResultType = TypeVar("ResultType", SampledResult, QutipResult) -class SimulationResults(ABC, Results[ResultType]): + +class SimulationResults(ABC, ResultsSequence[ResultType]): """Results of a simulation run of a pulse sequence. Parent class for NoisyResults and CoherentResults. @@ -224,7 +227,7 @@ def _meas_projector(self, state_n: int) -> qutip.Qobj: return qutip.basis(2, state_n).proj() -class NoisyResults(SimulationResults): +class NoisyResults(SimulationResults[SampledResult]): """Results of a noisy simulation run of a pulse sequence. Contrary to a CoherentResults object, this object contains a list of @@ -275,7 +278,7 @@ def __init__( basis_name_ = "digital" if basis == "all" else basis super().__init__(size, basis_name_, sim_times) self.n_measures = n_measures - self._results = tuple(run_output) + self._results_seq = tuple(run_output) @property def states(self) -> list[qutip.Qobj]: @@ -355,7 +358,7 @@ def get_error_bars() -> Tuple[ArrayLike, ArrayLike]: super().plot(op, fmt, label) -class CoherentResults(SimulationResults): +class CoherentResults(SimulationResults[QutipResult]): """Results of a coherent simulation run of a pulse sequence. Contains methods for studying the states and extracting useful information @@ -403,7 +406,7 @@ def __init__( f"{self._basis_name}' must be '{expected_meas_basis}'." ) self._meas_basis = meas_basis - self._results = tuple(run_output) + self._results_seq = tuple(run_output) if meas_errors is not None: if set(meas_errors) != {"epsilon", "epsilon_prime"}: raise ValueError( diff --git a/pulser-simulation/pulser_simulation/simulation.py b/pulser-simulation/pulser_simulation/simulation.py index c180c7f8b..9f163c6e4 100644 --- a/pulser-simulation/pulser_simulation/simulation.py +++ b/pulser-simulation/pulser_simulation/simulation.py @@ -600,8 +600,9 @@ def _run_solver() -> CoherentResults: self._meas_basis, state, self._meas_basis in self.basis_name, + evaluation_time=t / self._tot_duration * 1e3, ) - for state in result.states + for state, t in zip(result.states, self._eval_times_array) ] return CoherentResults( results, @@ -654,8 +655,7 @@ def _run_solver() -> CoherentResults: update_ham = True # Will return NoisyResults - time_indices = range(len(self._eval_times_array)) - total_count = np.array([Counter() for _ in time_indices]) + total_count = np.array([Counter() for _ in self._eval_times_array]) # We run the system multiple times for i in range(loop_runs): if not update_ham: @@ -687,9 +687,10 @@ def _run_solver() -> CoherentResults: SampledResult( tuple(self._hamiltonian._qdict), self._meas_basis, - total_count[t], + total_count[ind], + evaluation_time=t / self._tot_duration * 1e3, ) - for t in time_indices + for ind, t in enumerate(self._eval_times_array) ] return NoisyResults( results, diff --git a/tests/test_backend.py b/tests/test_backend.py index 94b7e70ef..ebbca7ae0 100644 --- a/tests/test_backend.py +++ b/tests/test_backend.py @@ -16,12 +16,30 @@ import dataclasses import re import typing +import uuid +from collections import Counter +import numpy as np import pytest import pulser -from pulser.backend.abc import Backend -from pulser.backend.config import EmulatorConfig +from pulser.backend.abc import Backend, EmulatorBackend +from pulser.backend.config import ( + BackendConfig, + EmulationConfig, + EmulatorConfig, +) +from pulser.backend.default_observables import ( + BitStrings, + CorrelationMatrix, + Energy, + EnergySecondMoment, + EnergyVariance, + Expectation, + Fidelity, + Occupation, + StateResult, +) from pulser.backend.qpu import QPUBackend from pulser.backend.remote import ( BatchStatus, @@ -31,9 +49,11 @@ RemoteResultsError, _OpenBatchContextManager, ) +from pulser.backend.results import Results from pulser.devices import AnalogDevice, MockDevice from pulser.register import SquareLatticeLayout from pulser.result import Result, SampledResult +from pulser_simulation import QutipOperator, QutipState @pytest.fixture @@ -243,3 +263,456 @@ def test_qpu_backend(sequence): available_results = remote_results.get_available_results() assert available_results == {"abcd": connection.result} + + +def test_emulator_backend(sequence): + + class ConcreteEmulator(EmulatorBackend): + + default_config = EmulationConfig( + observables=(BitStrings(),), with_modulation=True + ) + + def run(self): + pass + + with pytest.raises( + TypeError, match="must be an instance of 'EmulationConfig'" + ): + ConcreteEmulator(sequence, config=EmulatorConfig) + + emu = ConcreteEmulator( + sequence, + config=EmulationConfig( + observables=(BitStrings(),), default_evaluation_times="Full" + ), + ) + assert emu._config.default_evaluation_times == "Full" + assert not emu._config.with_modulation + + # Uses the default config + assert ConcreteEmulator(sequence)._config.with_modulation + + +def test_backend_config(): + with pytest.warns( + UserWarning, + match="'BackendConfig' received unexpected keyword arguments", + ): + config1 = BackendConfig(prefer_device_noise_model=True) + assert config1.prefer_device_noise_model + + with pytest.raises(AttributeError, match="'dt' has not been passed"): + config1.dt + + with pytest.warns( + DeprecationWarning, + match="The 'backend_options' argument of 'BackendConfig' has been " + "deprecated", + ): + config2 = BackendConfig(backend_options={"dt": 10}) + assert config2.backend_options["dt"] == 10 + assert config2.dt == 10 + + +def test_emulation_config(): + with pytest.warns( + UserWarning, + match="'EmulationConfig' was initialized without any observables", + ): + EmulationConfig() + + with pytest.raises( + TypeError, + match="All entries in 'observables' must be instances of Observable", + ): + EmulationConfig(observables=["fidelity"]) + with pytest.raises( + ValueError, + match="Some of the provided 'observables' share identical tags", + ): + EmulationConfig( + observables=[BitStrings(), BitStrings(num_shots=200000)] + ) + with pytest.raises( + ValueError, match="All evaluation times must be between 0. and 1." + ): + EmulationConfig( + observables=(BitStrings(),), + default_evaluation_times=[-1e15, 0.0, 0.5, 1.0], + ) + with pytest.raises(ValueError, match="Evaluation times must be unique"): + EmulationConfig( + observables=(BitStrings(),), + default_evaluation_times=[0.0, 0.5, 0.5, 1.0], + ) + with pytest.raises( + ValueError, match="Evaluation times must be in ascending order" + ): + EmulationConfig( + observables=(BitStrings(),), + default_evaluation_times=[0.0, 1.0, 0.5], + ) + with pytest.raises( + TypeError, match="'initial_state' must be an instance of State" + ): + EmulationConfig(observables=(BitStrings(),), initial_state=[[1], [0]]) + with pytest.raises( + ValueError, + match=re.escape( + "'interaction_matrix' must be a square matrix. Instead, an array" + " of shape (4, 3) was given" + ), + ): + EmulationConfig( + observables=(BitStrings(),), + interaction_matrix=np.arange(12).reshape((4, 3)), + ) + with pytest.raises( + ValueError, + match=re.escape( + "interaction matrix of shape (2, 2) is incompatible with " + "the received initial state of 3 qudits" + ), + ): + EmulationConfig( + observables=(BitStrings(),), + interaction_matrix=np.eye(2), + initial_state=QutipState.from_state_amplitudes( + eigenstates=("r", "g"), amplitudes={"rrr": 1.0} + ), + ) + with pytest.raises( + ValueError, + match="interaction matrix is not symmetric", + ): + matrix_ = np.ones((4, 4)) + matrix_[0, 3] += 1e-4 + EmulationConfig( + observables=(BitStrings(),), + interaction_matrix=matrix_, + ) + with pytest.warns(UserWarning, match="non-zero values in its diagonal"): + EmulationConfig( + observables=(BitStrings(),), + interaction_matrix=np.ones((4, 4)), + ) + with pytest.raises(TypeError, match="must be a NoiseModel"): + EmulationConfig( + observables=(BitStrings(),), noise_model={"p_false_pos": 0.1} + ) + + +def test_results(): + res = Results(atom_order=(), total_duration=0) + assert res.get_result_tags() == [] + assert res.get_tagged_results() == {} + with pytest.raises( + AttributeError, match="'bitstrings' is not in the results" + ): + assert res.bitstrings + with pytest.raises( + ValueError, + match="'bitstrings' is not an Observable instance nor a known " + "observable tag", + ): + assert res.get_result_times("bitstrings") + + obs = BitStrings(tag_suffix="test") + with pytest.raises( + ValueError, + match=f"'bitstrings_test:{obs.uuid}' has not been stored", + ): + assert res.get_result(obs, 1.0) + + obs( + config=EmulationConfig(observables=(BitStrings(),)), + t=1.0, + state=QutipState.from_state_amplitudes( + eigenstates=("r", "g"), amplitudes={"rrr": 1.0} + ), + hamiltonian=QutipOperator.from_operator_repr( + eigenstates=("r", "g"), n_qudits=3, operations=[(1.0, [])] + ), + result=res, + ) + assert res.get_result_tags() == ["bitstrings_test"] + expected_val = [Counter({"111": obs.num_shots})] + assert res.get_tagged_results() == {"bitstrings_test": expected_val} + assert res.bitstrings_test == expected_val + assert ( + res.get_result_times("bitstrings_test") + == res.get_result_times(obs) + == [1.0] + ) + assert ( + res.get_result(obs, 1.0) + == res.get_result("bitstrings_test", 1.0) + == expected_val[0] + ) + with pytest.raises(ValueError, match="not available at time 0.912"): + res.get_result(obs, 0.912) + + +class TestObservables: + @pytest.fixture + def ghz_state(self): + return QutipState.from_state_amplitudes( + eigenstates=("r", "g"), + amplitudes={"rrr": np.sqrt(0.5), "ggg": np.sqrt(0.5)}, + ) + + @pytest.fixture + def ham(self): + return QutipOperator.from_operator_repr( + eigenstates=("r", "g"), + n_qudits=3, + operations=[(1.0, [])], + ) + + @pytest.fixture + def config(self): + return EmulationConfig(observables=(BitStrings(),)) + + @pytest.fixture + def results(self): + return Results(atom_order=("q0", "q1", "q2"), total_duration=1000) + + @pytest.mark.parametrize("tag_suffix", [None, "foo"]) + @pytest.mark.parametrize("eval_times", [None, (0.0, 0.5, 1.0)]) + def test_base_init(self, eval_times, tag_suffix): + # We use StateResult because Observable is an ABC + obs = StateResult(evaluation_times=eval_times, tag_suffix=tag_suffix) + assert isinstance(obs.uuid, uuid.UUID) + assert obs.evaluation_times == eval_times + expected_tag = "state_foo" if tag_suffix else "state" + assert obs.tag == expected_tag + assert repr(obs) == f"{expected_tag}:{obs.uuid}" + with pytest.raises( + ValueError, match="All evaluation times must be between 0. and 1." + ): + StateResult(evaluation_times=[1.000001]) + with pytest.raises( + ValueError, match="Evaluation times must be unique" + ): + StateResult(evaluation_times=[1.0, 1.0]) + with pytest.raises( + ValueError, match="Evaluation times must be in ascending order" + ): + StateResult(evaluation_times=[0.0, 1.0, 0.9999]) + + @pytest.mark.parametrize("eval_times", [None, (0.0, 0.5, 1.0)]) + def test_call( + self, + config: EmulationConfig, + results: Results, + ghz_state, + ham, + eval_times, + ): + assert not results.get_result_tags() # ie it's empty + assert config.default_evaluation_times == (1.0,) + # We use StateResult because Observable is an ABC + obs = StateResult(evaluation_times=eval_times) + assert obs.apply(state=ghz_state) == ghz_state + true_eval_times = eval_times or config.default_evaluation_times + + t_ = 0.1 + assert not config.is_time_in_evaluation_times(t_, true_eval_times) + obs(config, t_, ghz_state, ham, results) + assert not results.get_result_tags() # ie it's still empty + + t_ = 1.0 + expected_tol = 0.5 / results.total_duration + t_minus_tol = t_ - expected_tol + assert config.is_time_in_evaluation_times( + t_minus_tol, true_eval_times, tol=expected_tol + ) + obs(config, t_minus_tol, ghz_state, ham, results) + assert results.get_result_times(obs) == [t_minus_tol] + assert results.get_result(obs, t_minus_tol) == ghz_state + + assert config.is_time_in_evaluation_times(t_, true_eval_times) + obs(config, t_, ghz_state, ham, results) + assert results.get_result_tags() == ["state"] + assert ( + results.get_result_times("state") + == results.get_result_times(obs) + == [t_minus_tol, t_] + ) + assert results.get_result(obs, t_) == ghz_state + with pytest.raises( + RuntimeError, + match="A value is already stored for observable 'state' at time " + f"{t_}", + ): + obs(config, t_, ghz_state, ham, results) + + t_plus_tol = t_ + expected_tol + assert t_plus_tol > 1.0 # ie it's not an evaluation time + assert not config.is_time_in_evaluation_times( + t_plus_tol, true_eval_times, tol=expected_tol + ) + obs(config, t_plus_tol, ghz_state, ham, results) + assert t_plus_tol not in results.get_result_times(obs) + + def test_state_result(self, ghz_state): + obs = StateResult() + assert obs.apply(state=ghz_state) == ghz_state + + @pytest.mark.parametrize("p_false_pos", [None, 0.4]) + @pytest.mark.parametrize("p_false_neg", [None, 0.3]) + @pytest.mark.parametrize("one_state", [None, "g"]) + @pytest.mark.parametrize("num_shots", [None, 100]) + def test_bitstrings( + self, + config: EmulationConfig, + ghz_state: QutipState, + num_shots, + one_state, + p_false_pos, + p_false_neg, + ): + with pytest.raises(ValueError, match="greater than or equal to 1"): + BitStrings(num_shots=0) + kwargs = {} + if num_shots: + kwargs["num_shots"] = num_shots + obs = BitStrings(one_state=one_state, **kwargs) + assert obs.tag == "bitstrings" + noise_model = pulser.NoiseModel( + p_false_pos=p_false_pos, p_false_neg=p_false_neg + ) + config.noise_model = noise_model + assert config.noise_model.noise_types == ( + ("SPAM",) if p_false_pos or p_false_neg else () + ) + np.random.seed(123) + expected_shots = num_shots or obs.num_shots + expected_counts = ghz_state.sample( + num_shots=expected_shots, + one_state=one_state or ghz_state.infer_one_state(), + p_false_pos=p_false_pos or 0, + p_false_neg=p_false_neg or 0, + ) + np.random.seed(123) + counts = obs.apply(config=config, state=ghz_state) + assert isinstance(counts, Counter) + assert sum(counts.values()) == expected_shots + if noise_model == pulser.NoiseModel(): + assert set(counts) == {"000", "111"} + assert counts == expected_counts + + @pytest.mark.parametrize("one_state", [None, "r", "g"]) + def test_correlation_matrix_and_occupation( + self, ghz_state, ham, one_state + ): + corr = CorrelationMatrix(one_state=one_state) + assert corr.tag == "correlation_matrix" + occ = Occupation(one_state=one_state) + assert occ.tag == "occupation" + expected_corr_matrix = np.full((3, 3), 0.5) + np.testing.assert_allclose( + corr.apply(state=ghz_state, hamiltonian=ham), expected_corr_matrix + ) + np.testing.assert_allclose( + occ.apply(state=ghz_state, hamiltonian=ham), + expected_corr_matrix.diagonal(), + ) + + ggg_state = QutipState.from_state_amplitudes( + eigenstates=("r", "g"), amplitudes={"ggg": 1.0} + ) + expected_corr_matrix = np.ones((3, 3)) * int(one_state == "g") + np.testing.assert_allclose( + corr.apply(state=ggg_state, hamiltonian=ham), expected_corr_matrix + ) + np.testing.assert_allclose( + occ.apply(state=ggg_state, hamiltonian=ham), + expected_corr_matrix.diagonal(), + ) + + ggr_state = QutipState.from_state_amplitudes( + eigenstates=("r", "g"), amplitudes={"ggr": 1.0} + ) + if one_state == "g": + expected_corr_matrix = np.array([[1, 1, 0], [1, 1, 0], [0, 0, 0]]) + else: + expected_corr_matrix = np.zeros((3, 3)) + expected_corr_matrix[2, 2] = 1 + np.testing.assert_allclose( + corr.apply(state=ggr_state, hamiltonian=ham), expected_corr_matrix + ) + np.testing.assert_allclose( + occ.apply(state=ggr_state, hamiltonian=ham), + expected_corr_matrix.diagonal(), + ) + + @pytest.fixture + def zzz(self): + return QutipOperator.from_operator_repr( + eigenstates=("r", "g"), + n_qudits=3, + operations=[(1.0, [({"rr": 1.0, "gg": -1.0}, {0, 1, 2})])], + ) + + def test_energy_observables(self, ghz_state, ham, zzz): + energy = Energy() + assert energy.tag == "energy" + var = EnergyVariance() + assert var.tag == "energy_variance" + energy2 = EnergySecondMoment() + assert energy2.tag == "energy_second_moment" + assert np.isclose(energy.apply(state=ghz_state, hamiltonian=ham), 1.0) + assert np.isclose(energy2.apply(state=ghz_state, hamiltonian=ham), 1.0) + assert np.isclose(var.apply(state=ghz_state, hamiltonian=ham), 0.0) + + assert np.isclose(energy.apply(state=ghz_state, hamiltonian=zzz), 0.0) + assert np.isclose(energy2.apply(state=ghz_state, hamiltonian=zzz), 1.0) + assert np.isclose(var.apply(state=ghz_state, hamiltonian=zzz), 1.0) + + custom_op = QutipOperator.from_operator_repr( + eigenstates=("r", "g"), + n_qudits=3, + operations=[(1.0, [({"gg": -1}, {0, 1, 2})])], + ) + assert np.isclose( + energy.apply(state=ghz_state, hamiltonian=custom_op), -0.5 + ) + assert np.isclose( + energy2.apply(state=ghz_state, hamiltonian=custom_op), 0.5 + ) + assert np.isclose( + var.apply(state=ghz_state, hamiltonian=custom_op), 0.25 + ) + + def test_expectation(self, ghz_state, ham, zzz): + with pytest.raises( + TypeError, match="'operator' must be an Operator instance" + ): + Expectation(ham.to_qobj()) + h_exp = Expectation(ham) + assert h_exp.tag == "expectation" + assert h_exp.apply(state=ghz_state) == ham.expect(ghz_state) + z_exp = Expectation(zzz, tag_suffix="zzz") + assert z_exp.tag == "expectation_zzz" + assert z_exp.apply(state=ghz_state) == zzz.expect(ghz_state) + + def test_fidelity(self, ghz_state): + with pytest.raises( + TypeError, match="'state' must be a State instance" + ): + Fidelity(ghz_state.to_qobj()) + + fid_ggg = Fidelity( + QutipState.from_state_amplitudes( + eigenstates=("r", "g"), amplitudes={"ggg": 1.0} + ), + tag_suffix="ggg", + ) + assert fid_ggg.tag == "fidelity_ggg" + assert np.isclose(fid_ggg.apply(state=ghz_state), 0.5) + + fid_ghz = Fidelity(ghz_state) + assert fid_ghz.tag == "fidelity" + assert np.isclose(fid_ghz.apply(state=ghz_state), 1.0) diff --git a/tests/test_pasqal.py b/tests/test_pasqal.py index 4a8e11382..19184ceb3 100644 --- a/tests/test_pasqal.py +++ b/tests/test_pasqal.py @@ -196,7 +196,7 @@ def test_remote_results(fixt, mock_batch, with_job_id): for job in select_jobs ) - assert hasattr(remote_results, "_results") + assert hasattr(remote_results, "_results_seq") fixt.mock_cloud_sdk.get_batch.reset_mock() available_results = remote_results.get_available_results() @@ -472,7 +472,7 @@ def test_submit(fixt, parametrized, emulator, mimic_qpu, seq, mock_batch): ) for _job in mock_batch.ordered_jobs ) - assert hasattr(remote_results, "_results") + assert hasattr(remote_results, "_results_seq") @pytest.mark.parametrize("emu_cls", [EmuTNBackend, EmuFreeBackend]) diff --git a/tests/test_qutip_state_op.py b/tests/test_qutip_state_op.py new file mode 100644 index 000000000..b26164080 --- /dev/null +++ b/tests/test_qutip_state_op.py @@ -0,0 +1,536 @@ +# Copyright 2024 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. +from __future__ import annotations + +import re + +import numpy as np +import pytest +import qutip + +from pulser_simulation import QutipOperator, QutipState + + +@pytest.fixture +def ket_r(): + return QutipState(qutip.basis(2, 0), eigenstates=("r", "g")) + + +@pytest.fixture +def dm_g(): + return QutipState( + qutip.basis(2, 1) * qutip.basis(2, 1).dag(), eigenstates=("r", "g") + ) + + +@pytest.fixture +def ket_plus(): + return QutipState.from_state_amplitudes( + eigenstates=("r", "g"), + amplitudes={"r": 1 / np.sqrt(2), "g": 1 / np.sqrt(2)}, + ) + + +class TestQutipState: + + def test_init(self): + with pytest.raises( + ValueError, + match="eigenstates must be represented by single characters", + ): + QutipState(qutip.basis(2, 0), eigenstates=["ground", "rydberg"]) + with pytest.raises(ValueError, match="can't contain repeated entries"): + QutipState(qutip.basis(2, 0), eigenstates=["r", "g", "r"]) + with pytest.raises(TypeError, match="must be a qutip.Qobj"): + QutipState(np.arange(16), eigenstates=["r", "g"]) + with pytest.raises( + TypeError, match="must be a qutip.Qobj with one of types" + ): + QutipState( + qutip.operator_to_vector(qutip.ket2dm(qutip.basis(2, 0))), + eigenstates=["r", "g"], + ) + + with pytest.raises( + ValueError, match="incompatible with a system of 3-level qudits" + ): + QutipState(qutip.basis(2, 0), eigenstates=["r", "g", "h"]) + state = QutipState( + qutip.basis(3, 0).dag(), eigenstates=["r", "g", "h"] + ) + assert state.n_qudits == 1 + assert state.qudit_dim == 3 + assert state.eigenstates == ("r", "g", "h") + assert state.to_qobj() == qutip.basis(3, 0) + with pytest.raises( + RuntimeError, match="Failed to infer the 'one state'" + ): + state.infer_one_state() + + three_qubit_qobj = qutip.tensor([qutip.basis(2, 1)] * 3) + state = QutipState(three_qubit_qobj, eigenstates=("r", "g")) + assert state.n_qudits == 3 + assert state.qudit_dim == 2 + assert state.eigenstates == ("r", "g") + assert state.to_qobj() == three_qubit_qobj + assert state.infer_one_state() == "r" + + two_qutrit_dm = qutip.ket2dm(qutip.tensor([qutip.basis(3, 0)] * 2)) + state = QutipState(two_qutrit_dm, eigenstates=["r", "g", "h"]) + assert state.n_qudits == 2 + assert state.qudit_dim == 3 + assert state.to_qobj() == two_qutrit_dm + + @pytest.mark.parametrize( + "eigenstates", [("g", "r"), ("g", "h"), ("u", "d"), ("0", "1")] + ) + def test_infer_one_state(self, eigenstates): + assert ( + QutipState( + qutip.basis(2, 0), eigenstates=eigenstates + ).infer_one_state() + == eigenstates[1] + ) + + def test_get_basis_state(self): + n_qudits = 3 + state = QutipState.from_state_amplitudes( + eigenstates=("r", "g", "h"), amplitudes={"g" * n_qudits: 1.0} + ) + assert state.get_basis_state_from_index(0) == "rrr" + assert state.get_basis_state_from_index(1) == "rrg" + assert state.get_basis_state_from_index(2) == "rrh" + assert state.get_basis_state_from_index(3) == "rgr" + assert state.get_basis_state_from_index(4) == "rgg" + assert state.get_basis_state_from_index(9) == "grr" + assert state.get_basis_state_from_index(3**n_qudits - 1) == "hhh" + + with pytest.raises( + ValueError, match="'index' must be a non-negative integer" + ): + state.get_basis_state_from_index(-1) + + def test_overlap( + self, ket_r: QutipState, dm_g: QutipState, ket_plus: QutipState + ): + assert ket_r.overlap(ket_r) == 1.0 + assert dm_g.overlap(ket_r) == ket_r.overlap(dm_g) == 0.0 + assert ket_plus.overlap(ket_r) == ket_r.overlap(ket_plus) + assert np.isclose(ket_plus.overlap(ket_r), 0.5) + assert dm_g.overlap(ket_plus) == ket_plus.overlap(dm_g) + assert np.isclose(dm_g.overlap(ket_plus), 0.5) + + with pytest.raises(TypeError, match="expects another 'QutipState'"): + dm_g.overlap(ket_r.to_qobj()) + + with pytest.raises( + ValueError, + match="Can't calculate the overlap between a state with 1 " + "2-dimensional qudits and another with 2 3-dimensional qudits", + ): + ket_r.overlap( + QutipState.from_state_amplitudes( + eigenstates=("r", "g", "h"), amplitudes={"rr": 1.0} + ) + ) + + err_msg = ( + "Can't calculate the overlap between states with eigenstates " + "('r', 'g') and {}." + ) + with pytest.raises( + ValueError, match=re.escape(err_msg.format(("u", "d"))) + ): + ket_r.overlap( + QutipState(qutip.basis(2, 0), eigenstates=("u", "d")) + ) + with pytest.raises( + NotImplementedError, match=re.escape(err_msg.format(("g", "r"))) + ): + ket_r.overlap( + QutipState(qutip.basis(2, 0), eigenstates=("g", "r")) + ) + + def test_probabilities(self, ket_plus: QutipState): + amps = { + "rr": np.sqrt(0.5), + "gg": 1j * np.sqrt(0.5 - 1e-12), + "gr": 1e-6, + } + state = QutipState.from_state_amplitudes( + eigenstates=("r", "g"), amplitudes=amps + ) + probs = { + state_str: np.abs(amp) ** 2 for state_str, amp in amps.items() + } + state_probs = state.probabilities(cutoff=9e-13) + assert all(np.isclose(probs[k], state_probs[k]) for k in probs) + probs.pop("gr") + sum_ = sum(probs.values()) + probs = {k: v / sum_ for k, v in probs.items()} + state_probs = state.probabilities() + assert all(np.isclose(probs[k], state_probs[k]) for k in probs) + assert state.infer_one_state() == "r" + assert state.bitstring_probabilities() == { + "11": probs["rr"], + "00": probs["gg"], + } + assert state.bitstring_probabilities(one_state="g") == { + "11": probs["gg"], + "00": probs["rr"], + } + + dm_plus = QutipState( + qutip.ket2dm(ket_plus.to_qobj()), eigenstates=ket_plus.eigenstates + ) + assert dm_plus.probabilities() == {"r": 0.5, "g": 0.5} + assert dm_plus.bitstring_probabilities() == {"0": 0.5, "1": 0.5} + + def test_sample(self, ket_r: QutipState, dm_g: QutipState): + shots = 2000 + assert ket_r.sample(num_shots=shots) == {"1": shots} + assert ket_r.sample(num_shots=shots, one_state="g") == {"0": shots} + assert ket_r.sample(num_shots=shots, p_false_pos=0.1) == {"1": shots} + assert ket_r.sample(num_shots=shots, p_false_neg=0.1)["0"] > 0 + + assert dm_g.sample(num_shots=shots) == {"0": shots} + assert dm_g.sample(num_shots=shots, one_state="g") == {"1": shots} + assert dm_g.sample(num_shots=shots, p_false_neg=0.1) == {"0": shots} + assert dm_g.sample(num_shots=shots, p_false_pos=0.1)["1"] > 0 + + @pytest.mark.parametrize( + "amplitudes", + [ + {"rrh": 1.0}, + {"rr": 0.5, "rgg": np.sqrt(0.75)}, + ], + ) + def test_from_state_amplitudes_error(self, amplitudes): + with pytest.raises( + ValueError, + match=re.escape( + "All basis states must be combinations of eigenstates with " + f"the same length. Expected combinations of ('r', 'g'), each " + f"with {len(list(amplitudes)[0])} elements." + ), + ): + QutipState.from_state_amplitudes( + eigenstates=("r", "g"), amplitudes=amplitudes + ) + + def test_from_state_amplitudes(self): + assert QutipState.from_state_amplitudes( + eigenstates=("r", "g"), amplitudes={"g": 1.0} + ).to_qobj() == qutip.basis(2, 1) + assert QutipState.from_state_amplitudes( + eigenstates=("g", "r"), amplitudes={"g": 1.0} + ).to_qobj() == qutip.basis(2, 0) + assert QutipState.from_state_amplitudes( + eigenstates=("r", "g", "h"), amplitudes={"g": 1.0} + ).to_qobj() == qutip.basis(3, 1) + + r = qutip.basis(2, 0) + g = qutip.basis(2, 1) + assert QutipState.from_state_amplitudes( + eigenstates=("r", "g"), + amplitudes={"rr": -0.5j, "gr": 0.5, "rg": 0.5j, "gg": -0.5}, + ).to_qobj() == -0.5j * qutip.tensor([r, r]) + 0.5 * qutip.tensor( + [g, r] + ) + 0.5j * qutip.tensor( + [r, g] + ) - 0.5 * qutip.tensor( + [g, g] + ) + + def test_repr(self, ket_r): + assert repr(ket_r) == ( + "QutipState\n" + + "-" * len("QutipState") + + f"\nEigenstates: {ket_r.eigenstates}\n" + + repr(ket_r.to_qobj()) + ) + + def test_eq(self, ket_r, dm_g): + assert ket_r == QutipState.from_state_amplitudes( + eigenstates=("r", "g"), amplitudes={"r": 1.0} + ) + assert dm_g != QutipState.from_state_amplitudes( + eigenstates=("r", "g"), amplitudes={"g": 1.0} + ) + assert dm_g != qutip.basis(2, 1).proj() + + +class TestQutipOperator: + + def test_init(self): + with pytest.raises( + ValueError, + match="eigenstates must be represented by single characters", + ): + QutipOperator(qutip.sigmaz(), eigenstates=["ground", "rydberg"]) + with pytest.raises(ValueError, match="can't contain repeated entries"): + QutipOperator(qutip.sigmaz(), eigenstates=["r", "g", "r"]) + with pytest.raises(TypeError, match="must be a qutip.Qobj"): + QutipOperator(qutip.sigmaz().full(), eigenstates=["r", "g"]) + with pytest.raises( + TypeError, match="must be a qutip.Qobj with type 'oper'" + ): + QutipOperator(qutip.basis(2, 0), eigenstates=["r", "g"]) + with pytest.raises( + ValueError, match="incompatible with a system of 3-level qudits" + ): + QutipOperator(qutip.sigmaz(), eigenstates=["r", "g", "h"]) + + pauli_z = QutipOperator(qutip.sigmaz(), eigenstates=("r", "g")) + assert pauli_z.eigenstates == ("r", "g") + assert ( + pauli_z.to_qobj() + == qutip.basis(2, 0).proj() - qutip.basis(2, 1).proj() + ) + + @pytest.fixture + def pauli_i(self): + return QutipOperator(qutip.identity(2), eigenstates=("r", "g")) + + @pytest.fixture + def pauli_x(self): + return QutipOperator(qutip.sigmax(), eigenstates=("r", "g")) + + @pytest.fixture + def pauli_y(self): + return QutipOperator(qutip.sigmay(), eigenstates=("r", "g")) + + @pytest.fixture + def pauli_z(self): + return QutipOperator(qutip.sigmaz(), eigenstates=("r", "g")) + + @pytest.mark.parametrize("op_name", ["apply_to", "expect"]) + def test_errors_on_qutip_state(self, pauli_x, op_name): + op = getattr(pauli_x, op_name) + with pytest.raises( + TypeError, + match=re.escape( + f"'QutipOperator.{op_name}()' expects a 'QutipState' instance" + ), + ): + op(qutip.basis(2, 0)) + err_msg = ( + f"Can't apply QutipOperator.{op_name}() between a QutipOperator " + "with eigenstates ('r', 'g') and a QutipState with {}" + ) + with pytest.raises( + ValueError, match=re.escape(err_msg.format(("g", "h"))) + ): + op(QutipState(qutip.basis(2, 0), eigenstates=("g", "h"))) + with pytest.raises( + NotImplementedError, match=re.escape(err_msg.format(("g", "r"))) + ): + op(QutipState(qutip.basis(2, 0), eigenstates=("g", "r"))) + + @pytest.mark.parametrize("op_name", ["__add__", "__matmul__"]) + def test_errors_on_qutip_operator(self, pauli_x, op_name): + op = getattr(pauli_x, op_name) + with pytest.raises( + TypeError, + match=re.escape(f"'{op_name}' expects a 'QutipOperator' instance"), + ): + op(ket_r) + err_msg = ( + f"Can't apply {op_name} between a QutipOperator with eigenstates " + "('r', 'g') and a QutipOperator with {}" + ) + with pytest.raises( + ValueError, match=re.escape(err_msg.format(("g", "h"))) + ): + op(QutipOperator(qutip.basis(2, 0).proj(), eigenstates=("g", "h"))) + + with pytest.raises( + NotImplementedError, match=re.escape(err_msg.format(("g", "r"))) + ): + op(QutipOperator(qutip.basis(2, 0).proj(), eigenstates=("g", "r"))) + + def test_apply_to(self, ket_r, dm_g, pauli_x: QutipOperator): + assert pauli_x.apply_to(ket_r) == QutipState.from_state_amplitudes( + eigenstates=("r", "g"), amplitudes={"g": 1.0} + ) + assert pauli_x.apply_to(dm_g) == QutipState( + qutip.basis(2, 0).proj(), eigenstates=dm_g.eigenstates + ) + + def test_expect( + self, + pauli_x: QutipOperator, + pauli_y: QutipOperator, + pauli_z: QutipOperator, + ket_r, + dm_g, + ket_plus, + ): + assert pauli_x.expect(ket_r) == 0.0 + assert pauli_x.expect(dm_g) == 0.0 + assert np.isclose(pauli_x.expect(ket_plus), 1.0) + ket_minus = pauli_y.apply_to(ket_plus) + assert np.isclose(pauli_x.expect(ket_minus), -1.0) + + assert pauli_z.expect(ket_r) == 1.0 + assert pauli_z.expect(dm_g) == -1.0 + assert np.isclose(pauli_z.expect(ket_plus), 0.0) + + def test_add(self, pauli_x, pauli_y, pauli_z): + r = qutip.basis(2, 0) + g = qutip.basis(2, 1) + assert pauli_x + pauli_y == QutipOperator( + (1 - 1j) * r * g.dag() + (1 + 1j) * g * r.dag(), + eigenstates=pauli_x.eigenstates, + ) + + assert QutipOperator( + qutip.identity(2), eigenstates=pauli_z.eigenstates + ) + pauli_z == QutipOperator( + 2 * r.proj(), eigenstates=pauli_z.eigenstates + ) + + def test_rmul(self, pauli_i, pauli_z): + assert (1 - 2j) * pauli_i == QutipOperator( + (1 - 2j) * qutip.identity(2), eigenstates=pauli_z.eigenstates + ) + assert 0.5 * (pauli_i + pauli_z) == QutipOperator( + qutip.basis(2, 0).proj(), eigenstates=pauli_z.eigenstates + ) + + def test_matmul(self, pauli_i, pauli_x, pauli_y, pauli_z): + assert ( + pauli_x @ pauli_x + == pauli_y @ pauli_y + == pauli_z @ pauli_z + == pauli_i + ) + assert pauli_x @ pauli_z == -1j * pauli_y + assert pauli_z @ pauli_x == 1j * pauli_y + + def test_from_operator_repr(self, pauli_i): + with pytest.raises( + ValueError, + match="QuditOp key must be made up of two eigenstates; instead, " + "got 'gggg'", + ): + QutipOperator.from_operator_repr( + eigenstates=("r", "g"), + n_qudits=2, + operations=[(1.0, [({"gggg": 1.0, "rr": -1.0}, {0})])], + ) + + with pytest.raises( + ValueError, + match="QuditOp key must be made up of two eigenstates; instead, " + "got 'hh'", + ): + QutipOperator.from_operator_repr( + eigenstates=("r", "g"), + n_qudits=2, + operations=[(1.0, [({"hh": 1.0, "rr": -1.0}, {0})])], + ) + + with pytest.raises( + ValueError, + match="QuditOp key must be made up of two eigenstates; instead, " + "got 'hh'", + ): + QutipOperator.from_operator_repr( + eigenstates=("r", "g"), + n_qudits=2, + operations=[(1.0, [({"hh": 1.0, "rr": -1.0}, {0})])], + ) + + with pytest.raises( + ValueError, match="Got invalid indices for a system with 2 qudits" + ): + QutipOperator.from_operator_repr( + eigenstates=("r", "g"), + n_qudits=2, + operations=[(1.0, [({"gg": 1.0, "rr": -1.0}, {3, 5, 9})])], + ) + + with pytest.raises( + ValueError, + match=re.escape("only indices {1} were still available"), + ): + QutipOperator.from_operator_repr( + eigenstates=("r", "g"), + n_qudits=2, + operations=[ + ( + 1.0, + [ + ({"gg": 1.0, "rr": -1.0}, {0}), + ({"rg": 1.0, "rg": 1.0}, {0}), + ], + ) + ], + ) + + assert QutipOperator.from_operator_repr( + eigenstates=("r", "g", "h"), + n_qudits=3, + operations=[ + (1.0, [({"rr": 1.0, "hh": -1.0}, {0}), ({"gr": -1j}, {2})]) + ], + ) == QutipOperator( + qutip.tensor( + [ + qutip.basis(3, 0).proj() - qutip.basis(3, 2).proj(), + qutip.identity(3), + -1j * qutip.basis(3, 1) * qutip.basis(3, 0).dag(), + ] + ), + eigenstates=("r", "g", "h"), + ) + + assert ( + QutipOperator.from_operator_repr( + eigenstates=("r", "g"), + n_qudits=1, + operations=[(1, [])], + ) + == pauli_i + ) + + assert QutipOperator.from_operator_repr( + eigenstates=("r", "g"), + n_qudits=2, + operations=[(0.5, [({"rr": 1.0, "gg": -1.0}, {0})]), (0.5, [])], + ) == QutipOperator( + qutip.tensor( + [ + qutip.basis(2, 0).proj(), + qutip.identity(2), + ] + ), + eigenstates=("r", "g"), + ) + + def test_repr(self, pauli_z): + assert repr(pauli_z) == ( + "QutipOperator\n" + + "-" * len("QutipOperator") + + f"\nEigenstates: {pauli_z.eigenstates}\n" + + repr(pauli_z.to_qobj()) + ) + + def test_eq(self, pauli_i, pauli_z, dm_g): + g_proj = 0.5 * (pauli_i + (-1) * pauli_z) + assert g_proj == QutipOperator( + qutip.basis(2, 1).proj(), eigenstates=pauli_i.eigenstates + ) + assert g_proj != dm_g diff --git a/tests/test_result.py b/tests/test_result.py index feaee1c20..ee74552ef 100644 --- a/tests/test_result.py +++ b/tests/test_result.py @@ -20,6 +20,8 @@ import pytest import qutip +import pulser.result +from pulser.backend.results import ResultsSequence, ResultsType from pulser.result import SampledResult from pulser_simulation.qutip_result import QutipResult @@ -234,3 +236,16 @@ def test_qutip_result_density_matrices(): "10": 0.25, "11": 0.25, } + + +@pytest.mark.parametrize( + "old_name, obj", + [("Results", ResultsSequence), ("ResultType", ResultsType)], +) +def test_legacy_imports(old_name, obj): + with pytest.warns( + DeprecationWarning, + match=f"'pulser.result.{old_name}' class has been renamed " + f"to '{obj.__name__}'", + ): + assert getattr(pulser.result, old_name) == obj diff --git a/tutorials/advanced_features/Backends for Sequence Execution.ipynb b/tutorials/advanced_features/Backends for Sequence Execution.ipynb index 4d98a4aa4..3c9665a31 100644 --- a/tutorials/advanced_features/Backends for Sequence Execution.ipynb +++ b/tutorials/advanced_features/Backends for Sequence Execution.ipynb @@ -183,9 +183,9 @@ "id": "365ed331", "metadata": {}, "source": [ - "Upon creation, all backends require the sequence they will execute. Emulator backends also accept, optionally, a configuration given as an instance of the `EmulationConfig` class. This class allows for setting all the parameters available in `QutipEmulator` and is forward looking, meaning that it envisions that these options will at some point be available on other emulator backends. This also means that trying to change parameters in the configuration of a backend that does not support them yet will raise an error.\n", + "Upon creation, all backends require the sequence they will execute. Emulator backends also accept, optionally, a configuration given as an instance of the `EmulatorConfig` class. This class allows for setting all the parameters available in `QutipEmulator` and is forward looking, meaning that it envisions that these options will at some point be available on other emulator backends. This also means that trying to change parameters in the configuration of a backend that does not support them yet will raise an error.\n", "\n", - "Even so, `EmulationConfig` also has a dedicated `backend_options` for options specific to each backend, which are detailed in the [backends' docstrings](../apidoc/_autosummary/pulser.backends.rst#module-pulser.backends)." + "Even so, `EmulatorConfig` also has a dedicated `backend_options` for options specific to each backend, which are detailed in the [backends' docstrings](../apidoc/_autosummary/pulser.backends.rst#module-pulser.backends)." ] }, { From 04bf588a3f2454bcc003705c8b9da351d073836b Mon Sep 17 00:00:00 2001 From: Antoine Cornillot <61453516+a-corni@users.noreply.github.com> Date: Fri, 14 Feb 2025 17:35:21 +0100 Subject: [PATCH 11/12] Improve handling of ParamObj in InterpolatedWaveform (#813) * Improve gestion of ParamObj in InterpolatedWaveform * Fix regex * Fix tests * Address review comments * Reverting __new__ signature * Fix black --- pulser-core/pulser/waveforms.py | 83 ++++++++++++++++++++++++--------- tests/test_waveforms.py | 36 ++++++++++++++ 2 files changed, 98 insertions(+), 21 deletions(-) diff --git a/pulser-core/pulser/waveforms.py b/pulser-core/pulser/waveforms.py index 45df107b5..0d4072cba 100644 --- a/pulser-core/pulser/waveforms.py +++ b/pulser-core/pulser/waveforms.py @@ -793,9 +793,11 @@ class InterpolatedWaveform(Waveform): Args: duration: The waveform duration (in ns). - values: Values of the interpolation points. + values: Values of the interpolation points. Must be a list of castable + to float or a parametrized object. times: Fractions of the total duration (between 0 - and 1), indicating where to place each value on the time axis. If + and 1), indicating where to place each value on the time axis. Must + be a list of castable to float or a parametrized object. If not given, the values are spread evenly throughout the full duration of the waveform. interpolator: The SciPy interpolation class @@ -804,6 +806,18 @@ class InterpolatedWaveform(Waveform): interpolator class. """ + def __new__(cls, *args, **kwargs): # type: ignore + """Creates InterpolatedWaveform or ParamObj depending on the input.""" + cls._check_values_times( + args[1] if len(args) >= 2 else kwargs["values"], + args[2] if len(args) >= 3 else kwargs.get("times", None), + ) + for x in itertools.chain(args, kwargs.values()): + if isinstance(x, Parametrized): + return ParamObj(cls, *args, **kwargs) + else: + return object.__new__(cls) + def __init__( self, duration: Union[int, Parametrized], @@ -818,25 +832,6 @@ def __init__( if times is not None: times = cast(ArrayLike, times) times_ = np.array(times, dtype=float) - if len(times_) != len(self._values): - raise ValueError( - "When specified, the number of time coordinates in `times`" - f" ({len(times_)}) must match the number of `values` " - f"({len(self._values)})." - ) - if np.any(times_ < 0): - raise ValueError( - "All values in `times` must be greater than or equal to 0." - ) - if np.any(times_ > 1): - raise ValueError( - "All values in `times` must be less than or equal to 1." - ) - unique_times = np.unique(times) # Sorted array of unique values - if len(times_) != len(unique_times): - raise ValueError( - "`times` must be an array of non-repeating values." - ) self._times = times_ else: self._times = np.linspace(0, 1, num=len(self._values)) @@ -865,6 +860,52 @@ def __init__( **interpolator_kwargs, } + @staticmethod + def _check_values_times( + values: Union[ArrayLike, Parametrized], + times: Optional[Union[ArrayLike, Parametrized]] = None, + ) -> None: + """Check whether the types of values and times are valid.""" + + def _err_message(argument_name: str) -> str: + return ( + f"`{argument_name}` must be a parametrized object or a " + "sequence of elements castable to float. To make a sequence" + " of parametrized objects, declare a variable with the " + "desired size." + ) + + if not isinstance(values, Parametrized): + try: + _values = np.array(values, dtype=float) + except TypeError as e: + raise TypeError(_err_message("values")) from e + if times is not None and not isinstance(times, Parametrized): + try: + times = cast(ArrayLike, times) + times_ = np.array(times, dtype=float) + except TypeError as e: + raise TypeError(_err_message("times")) from e + if len(times_) != len(_values): + raise ValueError( + "When specified, the number of time coordinates in `times`" + f" ({len(times_)}) must match the number of `values` " + f"({len(_values)})." + ) + if np.any(times_ < 0): + raise ValueError( + "All values in `times` must be greater than or equal to 0." + ) + if np.any(times_ > 1): + raise ValueError( + "All values in `times` must be less than or equal to 1." + ) + unique_times = np.unique(times) # Sorted array of unique values + if len(times_) != len(unique_times): + raise ValueError( + "`times` must be an array of non-repeating values." + ) + @property def duration(self) -> int: """The duration of the pulse (in ns).""" diff --git a/tests/test_waveforms.py b/tests/test_waveforms.py index 5d46de56b..ba1ffe996 100644 --- a/tests/test_waveforms.py +++ b/tests/test_waveforms.py @@ -19,6 +19,7 @@ import pytest from scipy.interpolate import PchipInterpolator, interp1d +import pulser from pulser.channels import Rydberg from pulser.json.coders import PulserDecoder, PulserEncoder from pulser.parametrized import ParamObj, Variable @@ -266,6 +267,41 @@ def test_interpolated(): ) assert np.all((wf.samples >= 0).as_array()) + # Init an InterpolatedWaveform that always fails at build fails + seq = pulser.Sequence( + pulser.Register.square(2, 5), device=pulser.DigitalAnalogDevice + ) + + values = seq.declare_variable("values", size=5) # a Variable + duration, *other_values = values # a list of Variables + # Init an InterpolatedWaveform with a list of Variable fails + with pytest.raises( + TypeError, + match=( + "`values` must be a parametrized object or a sequence of " + "elements castable to float." + ), + ): + InterpolatedWaveform(1000, values=other_values) + # So this will always fail at build + with pytest.raises( + TypeError, + match=( + "`values` must be a parametrized object or a sequence of " + "elements castable to float." + ), + ): + InterpolatedWaveform(duration, values=other_values) + # this as well + with pytest.raises( + TypeError, + match=( + "`times` must be a parametrized object or a sequence of " + "elements castable to float." + ), + ): + InterpolatedWaveform(duration, [0, 0.1, 0.2, 0.3], other_values) + def test_kaiser(): duration: int = 40 From d7eca4dde901d34ba9dbeaf2363546ad9d638d0a Mon Sep 17 00:00:00 2001 From: HGSilveri Date: Fri, 14 Feb 2025 17:38:07 +0100 Subject: [PATCH 12/12] Bump version to 1.3.0 --- VERSION.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION.txt b/VERSION.txt index 54817c85e..f0bb29e76 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -1.3dev2 +1.3.0
\n", + " \"Rydberg\n", + "
The Rydberg blockade: The energy of the $|rr\\rangle$ state (blue line) increases signficantly for atoms less than a blockade radius ($R_\\text{Blockade}$ in this picture, $R_b$ elsewhere this document) away. As such, the transition to $|rr\\rangle$ is suppressed when $R_{ij} \\ll R_b$. Source: Quantum 6, 629 (2022)\n", + "
\n", + "