From d6ea35c46a79f0168d6bd5c155b996e1dc4fa4ad Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Wed, 27 Sep 2023 23:52:37 -0400 Subject: [PATCH 01/14] add rydberg_h + add sample to PythonFn. --- src/bloqade/__init__.py | 7 ++- src/bloqade/builder/factory.py | 42 ---------------- src/bloqade/factory.py | 80 ++++++++++++++++++++++++++++++ src/bloqade/ir/__init__.py | 4 +- src/bloqade/ir/control/waveform.py | 10 +++- tests/test_builder.py | 4 +- tests/test_variable_scan.py | 4 +- tests/test_waveform.py | 4 +- 8 files changed, 102 insertions(+), 53 deletions(-) delete mode 100644 src/bloqade/builder/factory.py create mode 100644 src/bloqade/factory.py diff --git a/src/bloqade/__init__.py b/src/bloqade/__init__.py index 7be52e800..cdb623efc 100644 --- a/src/bloqade/__init__.py +++ b/src/bloqade/__init__.py @@ -1,11 +1,12 @@ -from bloqade.ir import var, cast, Variable, Literal, start +from bloqade.ir import var, cast, Variable, Literal, start, waveform from bloqade.serialize import load, save, loads, dumps -from bloqade.builder.factory import ( +from bloqade.factory import ( piecewise_linear, piecewise_constant, linear, constant, + rydberg_h, ) import bloqade.ir as _ir from bloqade.constants import RB_C6 @@ -44,4 +45,6 @@ def tree_depth(depth: int = None): "save", "loads", "dumps", + "rydberg_h", + "waveform", ] diff --git a/src/bloqade/builder/factory.py b/src/bloqade/builder/factory.py deleted file mode 100644 index 91843aeee..000000000 --- a/src/bloqade/builder/factory.py +++ /dev/null @@ -1,42 +0,0 @@ -from bloqade.ir.control.waveform import Waveform, Linear, Constant -from bloqade.builder.typing import ScalarType -from beartype import beartype -from beartype.typing import List - -# this part only for manually build sequences. - - -@beartype -def linear(duration: ScalarType, start: ScalarType, stop: ScalarType): - return Linear(start, stop, duration) - - -@beartype -def constant(duration: ScalarType, value: ScalarType): - return Constant(value, duration) - - -@beartype -def piecewise_linear(durations: List[ScalarType], values: List[ScalarType]) -> Waveform: - pwl_wf = None - for duration, start, stop in zip(durations, values[:-1], values[1:]): - if pwl_wf is None: - pwl_wf = Linear(start, stop, duration) - else: - pwl_wf = pwl_wf.append(Linear(start, stop, duration)) - - return pwl_wf - - -@beartype -def piecewise_constant( - durations: List[ScalarType], values: List[ScalarType] -) -> Waveform: - pwc_wf = None - for duration, value in zip(durations, values): - if pwc_wf is None: - pwc_wf = Constant(value, duration) - else: - pwc_wf = pwc_wf.append(Constant(value, duration)) - - return pwc_wf diff --git a/src/bloqade/factory.py b/src/bloqade/factory.py new file mode 100644 index 000000000..49f1c393a --- /dev/null +++ b/src/bloqade/factory.py @@ -0,0 +1,80 @@ +from bloqade.builder.args import Args +from bloqade.builder.assign import Assign, BatchAssign +from bloqade.ir.control.waveform import Waveform, Linear, Constant +from bloqade.builder.typing import ScalarType +from beartype import beartype +from beartype.typing import List, Optional, Union, Dict, Any + + +@beartype +def linear(duration: ScalarType, start: ScalarType, stop: ScalarType): + return Linear(start, stop, duration) + + +@beartype +def constant(duration: ScalarType, value: ScalarType): + return Constant(value, duration) + + +@beartype +def piecewise_linear(durations: List[ScalarType], values: List[ScalarType]) -> Waveform: + pwl_wf = None + for duration, start, stop in zip(durations, values[:-1], values[1:]): + if pwl_wf is None: + pwl_wf = Linear(start, stop, duration) + else: + pwl_wf = pwl_wf.append(Linear(start, stop, duration)) + + return pwl_wf + + +@beartype +def piecewise_constant( + durations: List[ScalarType], values: List[ScalarType] +) -> Waveform: + pwc_wf = None + for duration, value in zip(durations, values): + if pwc_wf is None: + pwc_wf = Constant(value, duration) + else: + pwc_wf = pwc_wf.append(Constant(value, duration)) + + return pwc_wf + + +@beartype +def rydberg_h( + atoms_positions: Any, + detuning: Optional[Waveform] = None, + amplitude: Optional[Waveform] = None, + phase: Optional[Waveform] = None, + static_params: Dict[str, Any] = {}, + batch_params: Union[List[Dict[str, Any]], Dict[str, Any]] = [], + args: List[str] = [], +) -> Union[Assign, BatchAssign, Args]: + from bloqade import start + + prog = start.add_position(atoms_positions) + + if detuning is not None: + prog = prog.rydberg.detuning.uniform.apply(detuning) + + if amplitude is not None: + prog = prog.amplitude.uniform.apply(amplitude) + + if phase is not None: + prog = prog.phase.uniform.apply(phase) + + if len(static_params) > 0: + prog = prog.assign(**static_params) + + if len(batch_params) > 0: + if isinstance(batch_params, dict): + prog = prog.batch_assign(**batch_params) + else: + prog = prog.batch_assign(batch_params) + + if len(args) > 0: + prog = prog.args(args) + + return prog diff --git a/src/bloqade/ir/__init__.py b/src/bloqade/ir/__init__.py index 7ce494ec8..f79f1174d 100644 --- a/src/bloqade/ir/__init__.py +++ b/src/bloqade/ir/__init__.py @@ -11,7 +11,7 @@ Interpolation, Sample, PythonFn, - instruction, + waveform, GaussianKernel, LogisticKernel, SigmoidKernel, @@ -68,7 +68,7 @@ "Sample", "Interpolation", "PythonFn", - "instruction", + "waveform", "GaussianKernel", "LogisticKernel", "SigmoidKernel", diff --git a/src/bloqade/ir/control/waveform.py b/src/bloqade/ir/control/waveform.py index 0d6bc118d..469c02dcc 100644 --- a/src/bloqade/ir/control/waveform.py +++ b/src/bloqade/ir/control/waveform.py @@ -1,4 +1,5 @@ from numbers import Real +from bloqade.builder.typing import ScalarType from bloqade.ir.tree_print import Printer from bloqade.ir.scalar import ( Scalar, @@ -14,6 +15,7 @@ from decimal import Decimal from pydantic.dataclasses import dataclass from beartype.typing import Any, Tuple, Union, List, Callable, Dict +from beartype import beartype from enum import Enum import numpy as np @@ -23,7 +25,7 @@ from bloqade.visualization import display_ir -def instruction(duration: Any) -> "PythonFn": +def waveform(duration: Any) -> "PythonFn": # turn python function into a waveform instruction.""" def waveform_wrapper(fn: Callable) -> "PythonFn": @@ -498,6 +500,12 @@ def print_node(self): def children(self): return {"duration": self.duration, **{p.name: p for p in self.parameters}} + @beartype + def sample( + self, dt: ScalarType, interpolation: Union[str, "Interpolation"] + ) -> "Sample": + return Sample(self, dt, interpolation) + @dataclass(init=False) class SmoothingKernel: diff --git a/tests/test_builder.py b/tests/test_builder.py index 1d32aafea..fda3b2384 100644 --- a/tests/test_builder.py +++ b/tests/test_builder.py @@ -15,7 +15,7 @@ import bloqade.ir.routine.braket as braket from plum import NotFoundLookupError -from bloqade.ir.control.waveform import instruction +from bloqade.ir.control.waveform import waveform as waveform_decorator from bloqade.ir import rydberg, detuning, hyperfine, rabi from bloqade import start, cast, var @@ -33,7 +33,7 @@ def test_assign_checks(): delta = var("delta") / (2 * np.pi) omega_max = var("omega_max") * 2 * np.pi - @instruction(t_2) + @waveform_decorator(t_2) def detuning(t, u): return np.abs(t) * u diff --git a/tests/test_variable_scan.py b/tests/test_variable_scan.py index fe8f05799..b2a394f36 100644 --- a/tests/test_variable_scan.py +++ b/tests/test_variable_scan.py @@ -6,7 +6,7 @@ ) import numpy as np -from bloqade.ir.control.waveform import instruction +from bloqade.ir.control.waveform import waveform def test_1(): @@ -42,7 +42,7 @@ def test_2(): delta = var("delta") / (2 * np.pi) omega_max = var("omega_max") * 2 * np.pi - @instruction(t_2) + @waveform(t_2) def detuning(t, u): return np.abs(t) * u diff --git a/tests/test_waveform.py b/tests/test_waveform.py index 77cd5fc7e..c07ab661d 100644 --- a/tests/test_waveform.py +++ b/tests/test_waveform.py @@ -6,7 +6,7 @@ AlignedWaveform, Alignment, AlignedValue, - instruction, + waveform, Interpolation, GaussianKernel, LogisticKernel, @@ -81,7 +81,7 @@ def my_func(time, *, omega, phi=0, amplitude): return amplitude * np.cos(omega * time + phi) - @instruction(duration=1.0) + @waveform(duration=1.0) def annot_my_func(time, *, omega, phi=0, amplitude): import numpy as np From 5e9fee6cf96ec50d890e7ab7006cd3478ed0fa33 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Wed, 27 Sep 2023 23:58:29 -0400 Subject: [PATCH 02/14] restricing output of `rydberg_h` to always assign variables. --- src/bloqade/factory.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/bloqade/factory.py b/src/bloqade/factory.py index 49f1c393a..78d5c56d5 100644 --- a/src/bloqade/factory.py +++ b/src/bloqade/factory.py @@ -1,5 +1,4 @@ from bloqade.builder.args import Args -from bloqade.builder.assign import Assign, BatchAssign from bloqade.ir.control.waveform import Waveform, Linear, Constant from bloqade.builder.typing import ScalarType from beartype import beartype @@ -51,7 +50,7 @@ def rydberg_h( static_params: Dict[str, Any] = {}, batch_params: Union[List[Dict[str, Any]], Dict[str, Any]] = [], args: List[str] = [], -) -> Union[Assign, BatchAssign, Args]: +) -> Args: from bloqade import start prog = start.add_position(atoms_positions) @@ -65,16 +64,13 @@ def rydberg_h( if phase is not None: prog = prog.phase.uniform.apply(phase) - if len(static_params) > 0: - prog = prog.assign(**static_params) + prog = prog.assign(**static_params) - if len(batch_params) > 0: - if isinstance(batch_params, dict): - prog = prog.batch_assign(**batch_params) - else: - prog = prog.batch_assign(batch_params) + if isinstance(batch_params, dict): + prog = prog.batch_assign(**batch_params) + else: + prog = prog.batch_assign(batch_params) - if len(args) > 0: - prog = prog.args(args) + prog = prog.args(args) return prog From 6eb99ffd1a107e262b7c950172847c80a1cb7624 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Thu, 28 Sep 2023 00:10:53 -0400 Subject: [PATCH 03/14] allow atom arrangements in `rydberg_h` --- src/bloqade/factory.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/bloqade/factory.py b/src/bloqade/factory.py index 78d5c56d5..c360d79b8 100644 --- a/src/bloqade/factory.py +++ b/src/bloqade/factory.py @@ -52,8 +52,12 @@ def rydberg_h( args: List[str] = [], ) -> Args: from bloqade import start + from bloqade.atom_arrangement import AtomArrangement - prog = start.add_position(atoms_positions) + if isinstance(atoms_positions, AtomArrangement): + prog = atoms_positions + else: + prog = start.add_position(atoms_positions) if detuning is not None: prog = prog.rydberg.detuning.uniform.apply(detuning) From 6808cbc0912952805f6ad87d3748f9fc4762521f Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Thu, 28 Sep 2023 00:11:18 -0400 Subject: [PATCH 04/14] fixing type checks. --- src/bloqade/ir/control/waveform.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bloqade/ir/control/waveform.py b/src/bloqade/ir/control/waveform.py index 469c02dcc..b63b221f2 100644 --- a/src/bloqade/ir/control/waveform.py +++ b/src/bloqade/ir/control/waveform.py @@ -25,7 +25,8 @@ from bloqade.visualization import display_ir -def waveform(duration: Any) -> "PythonFn": +@beartype +def waveform(duration: ScalarType) -> Callable[[Callable], "PythonFn"]: # turn python function into a waveform instruction.""" def waveform_wrapper(fn: Callable) -> "PythonFn": @@ -500,11 +501,10 @@ def print_node(self): def children(self): return {"duration": self.duration, **{p.name: p for p in self.parameters}} - @beartype def sample( self, dt: ScalarType, interpolation: Union[str, "Interpolation"] ) -> "Sample": - return Sample(self, dt, interpolation) + return Sample(self, interpolation, cast(dt)) @dataclass(init=False) From a8e832592009a5ccc1d817dce942c9a9cca83b26 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Thu, 28 Sep 2023 22:54:39 -0400 Subject: [PATCH 05/14] renaming decorator to not overshadow `waveform` module internally --- src/bloqade/__init__.py | 3 ++- src/bloqade/ir/__init__.py | 4 ++-- src/bloqade/ir/control/waveform.py | 2 +- tests/test_builder.py | 4 ++-- tests/test_variable_scan.py | 4 ++-- tests/test_waveform.py | 4 ++-- 6 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/bloqade/__init__.py b/src/bloqade/__init__.py index cdb623efc..2e9085733 100644 --- a/src/bloqade/__init__.py +++ b/src/bloqade/__init__.py @@ -1,4 +1,5 @@ -from bloqade.ir import var, cast, Variable, Literal, start, waveform +from bloqade.ir import var, cast, Variable, Literal, start +from bloqade.ir import to_waveform as waveform from bloqade.serialize import load, save, loads, dumps from bloqade.factory import ( diff --git a/src/bloqade/ir/__init__.py b/src/bloqade/ir/__init__.py index f79f1174d..3a5db739b 100644 --- a/src/bloqade/ir/__init__.py +++ b/src/bloqade/ir/__init__.py @@ -11,7 +11,7 @@ Interpolation, Sample, PythonFn, - waveform, + to_waveform, GaussianKernel, LogisticKernel, SigmoidKernel, @@ -68,7 +68,7 @@ "Sample", "Interpolation", "PythonFn", - "waveform", + "to_waveform", "GaussianKernel", "LogisticKernel", "SigmoidKernel", diff --git a/src/bloqade/ir/control/waveform.py b/src/bloqade/ir/control/waveform.py index b63b221f2..b69fa628b 100644 --- a/src/bloqade/ir/control/waveform.py +++ b/src/bloqade/ir/control/waveform.py @@ -26,7 +26,7 @@ @beartype -def waveform(duration: ScalarType) -> Callable[[Callable], "PythonFn"]: +def to_waveform(duration: ScalarType) -> Callable[[Callable], "PythonFn"]: # turn python function into a waveform instruction.""" def waveform_wrapper(fn: Callable) -> "PythonFn": diff --git a/tests/test_builder.py b/tests/test_builder.py index fda3b2384..562d86ca1 100644 --- a/tests/test_builder.py +++ b/tests/test_builder.py @@ -15,7 +15,7 @@ import bloqade.ir.routine.braket as braket from plum import NotFoundLookupError -from bloqade.ir.control.waveform import waveform as waveform_decorator +from bloqade.ir.control.waveform import to_waveform from bloqade.ir import rydberg, detuning, hyperfine, rabi from bloqade import start, cast, var @@ -33,7 +33,7 @@ def test_assign_checks(): delta = var("delta") / (2 * np.pi) omega_max = var("omega_max") * 2 * np.pi - @waveform_decorator(t_2) + @to_waveform(t_2) def detuning(t, u): return np.abs(t) * u diff --git a/tests/test_variable_scan.py b/tests/test_variable_scan.py index b2a394f36..d250e4d07 100644 --- a/tests/test_variable_scan.py +++ b/tests/test_variable_scan.py @@ -6,7 +6,7 @@ ) import numpy as np -from bloqade.ir.control.waveform import waveform +from bloqade.ir.control.waveform import to_waveform def test_1(): @@ -42,7 +42,7 @@ def test_2(): delta = var("delta") / (2 * np.pi) omega_max = var("omega_max") * 2 * np.pi - @waveform(t_2) + @to_waveform(t_2) def detuning(t, u): return np.abs(t) * u diff --git a/tests/test_waveform.py b/tests/test_waveform.py index c07ab661d..c707a6a9b 100644 --- a/tests/test_waveform.py +++ b/tests/test_waveform.py @@ -6,7 +6,7 @@ AlignedWaveform, Alignment, AlignedValue, - waveform, + to_waveform, Interpolation, GaussianKernel, LogisticKernel, @@ -81,7 +81,7 @@ def my_func(time, *, omega, phi=0, amplitude): return amplitude * np.cos(omega * time + phi) - @waveform(duration=1.0) + @to_waveform(duration=1.0) def annot_my_func(time, *, omega, phi=0, amplitude): import numpy as np From afba84cff7fecd47c56e0c403507c42523ab09f5 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Thu, 28 Sep 2023 23:57:51 -0400 Subject: [PATCH 06/14] fixing bug in batch_Assign with empty arguments. --- src/bloqade/builder/assign.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/bloqade/builder/assign.py b/src/bloqade/builder/assign.py index af43e6945..879fdb122 100644 --- a/src/bloqade/builder/assign.py +++ b/src/bloqade/builder/assign.py @@ -93,6 +93,10 @@ def __init__( super().__init__(parent) + if len(assignments) == 0: + self._batch_params = [] + return + circuit = self.parse_circuit() variables = ScanVariablesAnalogCircuit().emit(circuit) From 077a42ea0aeb127ae746098e82d46a339cf2af5f Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Fri, 29 Sep 2023 08:42:50 -0400 Subject: [PATCH 07/14] adding unit test. --- src/bloqade/factory.py | 2 + tests/test_builder_factory.py | 98 +++++++++++++++++++++++++++++++++-- 2 files changed, 96 insertions(+), 4 deletions(-) diff --git a/src/bloqade/factory.py b/src/bloqade/factory.py index c360d79b8..05b236b78 100644 --- a/src/bloqade/factory.py +++ b/src/bloqade/factory.py @@ -54,6 +54,8 @@ def rydberg_h( from bloqade import start from bloqade.atom_arrangement import AtomArrangement + print(type(atoms_positions)) + if isinstance(atoms_positions, AtomArrangement): prog = atoms_positions else: diff --git a/tests/test_builder_factory.py b/tests/test_builder_factory.py index ae24fad47..65eb3728c 100644 --- a/tests/test_builder_factory.py +++ b/tests/test_builder_factory.py @@ -1,11 +1,28 @@ -""" -from bloqade.builder.factory import ( +from bloqade import ( + waveform, + rydberg_h, piecewise_linear, piecewise_constant, constant, linear, + var, + cast, + start, ) -from bloqade import cast +from bloqade.atom_arrangement import Chain +from bloqade.ir import ( + AnalogCircuit, + Sequence, + rydberg, + Pulse, + rabi, + detuning, + Field, + Uniform, +) +from bloqade.ir.routine.params import Params +from bloqade.ir.routine.base import Routine +import numpy as np def test_ir_piecewise_linear(): @@ -54,4 +71,77 @@ def test_ir_piecewise_constant(): assert A.waveforms[2].duration == cast(0.2) assert A.waveforms[2].value == cast("b") -""" + + +def test_rydberg_h(): + run_time = var("run_time") + + @waveform(run_time + 0.2) + def delta(t, amp, omega): + return np.sin(omega * t) * amp + + delta = delta.sample(0.05, "linear") + ampl = piecewise_linear([0.1, run_time, 0.1], [0, 10, 10, 0]) + phase = piecewise_constant([2, 2], [0, np.pi]) + register = Chain(11, lattice_spacing=6.1) + + static_params = {"amp": 1.0} + batch_params = [{"omega": omega} for omega in [1, 2, 4, 8]] + args = ["run_time"] + + prog = rydberg_h( + register, + detuning=delta, + amplitude=ampl, + phase=phase, + batch_params=batch_params, + static_params=static_params, + args=args, + ) + + detuning_field = Field({Uniform: delta}) + ampl_field = Field({Uniform: ampl}) + phase_field = Field({Uniform: phase}) + + pulse = Pulse( + {detuning: detuning_field, rabi.amplitude: ampl_field, rabi.phase: phase_field} + ) + sequence = Sequence({rydberg: pulse}) + + routine = prog.parse() + + circuit = AnalogCircuit(register, sequence) + params = Params(static_params, batch_params, args) + + assert routine == Routine(prog, circuit, params) + + +def test_rydberg_h_2(): + run_time = var("run_time") + + @waveform(run_time + 0.2) + def delta(t, amp, omega): + return np.sin(omega * t) * amp + + delta = delta.sample(0.05, "linear") + ampl = piecewise_linear([0.1, run_time, 0.1], [0, 10, 10, 0]) + phase = piecewise_constant([2, 2], [0, np.pi]) + register = start.add_position((0, 0)) + + prog = rydberg_h( + (0, 0), detuning=delta, amplitude=ampl, phase=phase, batch_params={} + ) + + detuning_field = Field({Uniform: delta}) + ampl_field = Field({Uniform: ampl}) + phase_field = Field({Uniform: phase}) + + pulse = Pulse( + {detuning: detuning_field, rabi.amplitude: ampl_field, rabi.phase: phase_field} + ) + sequence = Sequence({rydberg: pulse}) + + print(prog.parse_circuit()) + print(AnalogCircuit(register, sequence)) + + assert prog.parse_circuit() == AnalogCircuit(register, sequence) From 9b72495c45c29b846081b3f2e2cddc63ae721443 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Fri, 29 Sep 2023 10:34:02 -0400 Subject: [PATCH 08/14] adding doc strings. Changing return value of `rydberg_h` --- src/bloqade/factory.py | 88 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 5 deletions(-) diff --git a/src/bloqade/factory.py b/src/bloqade/factory.py index 05b236b78..ef299af54 100644 --- a/src/bloqade/factory.py +++ b/src/bloqade/factory.py @@ -1,4 +1,4 @@ -from bloqade.builder.args import Args +from bloqade.ir.routine.base import Routine from bloqade.ir.control.waveform import Waveform, Linear, Constant from bloqade.builder.typing import ScalarType from beartype import beartype @@ -6,17 +6,58 @@ @beartype -def linear(duration: ScalarType, start: ScalarType, stop: ScalarType): +def linear(duration: ScalarType, start: ScalarType, stop: ScalarType) -> Linear: + """Create a Linear waveform. + + Args: + duration (ScalarType): duration of linear waveform + start (ScalarType): starting value of linear waveform + stop (ScalarType): ending value of linear waveform + + Returns: + Linear: Linear waveform + """ return Linear(start, stop, duration) @beartype -def constant(duration: ScalarType, value: ScalarType): +def constant(duration: ScalarType, value: ScalarType) -> Constant: + """Create a Constant waveform. + + Args: + duration (ScalarType): _description_ + value (ScalarType): _description_ + + Returns: + Constant: A Constant waveform. + """ return Constant(value, duration) @beartype def piecewise_linear(durations: List[ScalarType], values: List[ScalarType]) -> Waveform: + """Create a piecewise linear waveform. + + Create a piecewise linear waveform from a list of durations and values. The + value `duration[i]` is of the linear segment between `values[i]` and `values[i+1]`. + + Args: + durations (List[ScalarType]): The duration of each segment + values (List[ScalarType]): The values for each segment + + Raises: + ValueError: If the length of `values` is not one greater than the length of + `durations`. + + Returns: + Waveform: The piecewise linear waveform. + """ + + if len(durations) + 1 != len(values): + raise ValueError( + "The length of values must be one greater than the length of durations" + ) + pwl_wf = None for duration, start, stop in zip(durations, values[:-1], values[1:]): if pwl_wf is None: @@ -31,6 +72,22 @@ def piecewise_linear(durations: List[ScalarType], values: List[ScalarType]) -> W def piecewise_constant( durations: List[ScalarType], values: List[ScalarType] ) -> Waveform: + """Create a piecewise linear waveform. + + Create a piecewise constant waveform from a list of durations and values. The + value `duration[i]` corresponds to the length of time for the i'th segment + with a value of `values[i]`. + + Args: + durations (List[ScalarType]): The duration of each segment + values (List[ScalarType]): The values for each segment + + Note: + that the length of `values` must be the same as the length of `durations`. + + Returns: + Waveform: The piecewise linear waveform. + """ pwc_wf = None for duration, value in zip(durations, values): if pwc_wf is None: @@ -50,7 +107,28 @@ def rydberg_h( static_params: Dict[str, Any] = {}, batch_params: Union[List[Dict[str, Any]], Dict[str, Any]] = [], args: List[str] = [], -) -> Args: +) -> Routine: + """Create a rydberg program with uniform detuning, amplitude, and phase. + + Args: + atoms_positions (Any): Description of geometry of atoms in system. + detuning (Optional[Waveform], optional): Waveform for detuning. + Defaults to None. + amplitude (Optional[Waveform], optional): Waveform describing the amplitude of + the rabi term. Defaults to None. + phase (Optional[Waveform], optional): Waveform describing the phase of rabi + term. Defaults to None. + static_params (Dict[str, Any], optional): Define static parameters of your + program. Defaults to {}. + batch_params (Union[List[Dict[str, Any]], Dict[str, Any]], optional): + Parmaters for a batch of tasks. Defaults to []. + args (List[str], optional): List of arguments to leave till runtime. + Defaults to []. + + Returns: + Routine: An object that can be used to dispatch a rydberg program to + multiple backends. + """ from bloqade import start from bloqade.atom_arrangement import AtomArrangement @@ -79,4 +157,4 @@ def rydberg_h( prog = prog.args(args) - return prog + return prog.parse() From 028b8a61c95099603a33789acc198f67275a6c33 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Fri, 29 Sep 2023 10:35:43 -0400 Subject: [PATCH 09/14] updating pdm.lock --- pdm.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pdm.lock b/pdm.lock index 56552ad54..3cfec7827 100644 --- a/pdm.lock +++ b/pdm.lock @@ -853,7 +853,7 @@ files = [ [[package]] name = "ipython" -version = "8.15.0" +version = "8.16.0" requires_python = ">=3.9" summary = "IPython: Productive Interactive Computing" dependencies = [ @@ -873,8 +873,8 @@ dependencies = [ "typing-extensions; python_version < \"3.10\"", ] files = [ - {file = "ipython-8.15.0-py3-none-any.whl", hash = "sha256:45a2c3a529296870a97b7de34eda4a31bee16bc7bf954e07d39abe49caf8f887"}, - {file = "ipython-8.15.0.tar.gz", hash = "sha256:2baeb5be6949eeebf532150f81746f8333e2ccce02de1c7eedde3f23ed5e9f1e"}, + {file = "ipython-8.16.0-py3-none-any.whl", hash = "sha256:dba644376826a532e362da945a672865be7efda76ecf999219e6021bda85d702"}, + {file = "ipython-8.16.0.tar.gz", hash = "sha256:7a1b2e1e6a3ec5baa466163c451335081f31859883889ff0289c6b21f7a095e2"}, ] [[package]] From d6242b47abfbaf306b724109b9049502f8b5d612 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Fri, 29 Sep 2023 11:08:47 -0400 Subject: [PATCH 10/14] updating pydantic dep. --- pdm.lock | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pdm.lock b/pdm.lock index 3cfec7827..cd1229283 100644 --- a/pdm.lock +++ b/pdm.lock @@ -6,7 +6,7 @@ groups = ["default", "dev", "doc"] cross_platform = true static_urls = false lock_version = "4.3" -content_hash = "sha256:c2995ea995ac3393ea3e9e41bfe2ecbb41d84836e2f0c6aaa3dc81d93c727adc" +content_hash = "sha256:e46096370f188b9383b4e9e8c42fa15ba66ff934140d0fb301a6372e5dc7b56e" [[package]] name = "amazon-braket-default-simulator" diff --git a/pyproject.toml b/pyproject.toml index 347473eb2..ff2226f28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,7 +17,7 @@ classifiers = [ dependencies = [ "juliacall>=0.9.14", "numpy>=1.25.2", - "pydantic>=1.10.12", + "pydantic>=1.10.13", "scipy>=1.9.3", "pandas>=2.1.0", "bokeh>=3.2.2", From 30fc63a30f0e6912c0049c778348c2b360d7ac1c Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Fri, 29 Sep 2023 11:09:17 -0400 Subject: [PATCH 11/14] making Routine's `pydantic` dataclasses. --- src/bloqade/ir/routine/base.py | 11 +++++++---- src/bloqade/ir/routine/bloqade.py | 6 +++--- src/bloqade/ir/routine/braket.py | 8 ++++---- src/bloqade/ir/routine/quera.py | 6 +++--- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/bloqade/ir/routine/base.py b/src/bloqade/ir/routine/base.py index 56338a889..ff0bde798 100644 --- a/src/bloqade/ir/routine/base.py +++ b/src/bloqade/ir/routine/base.py @@ -5,8 +5,8 @@ from bloqade.builder.base import Builder from bloqade.ir.routine.params import Params - -from dataclasses import dataclass +from pydantic import ConfigDict +from pydantic.dataclasses import dataclass from typing import TYPE_CHECKING, Union if TYPE_CHECKING: @@ -29,7 +29,10 @@ def parse(self: "RoutineBase") -> "Routine": return self -@dataclass(frozen=True) +__pydantic_dataclass_config__ = ConfigDict(arbitrary_types_allowed=True) + + +@dataclass(frozen=True, config=__pydantic_dataclass_config__) class RoutineBase(RoutineParse): source: Builder circuit: AnalogCircuit @@ -43,7 +46,7 @@ def __str__(self): return out -@dataclass(frozen=True) +@dataclass(frozen=True, config=__pydantic_dataclass_config__) class Routine(RoutineBase): """Result of parsing a completed Builder string.""" diff --git a/src/bloqade/ir/routine/bloqade.py b/src/bloqade/ir/routine/bloqade.py index 51a09a258..eccc7afaa 100644 --- a/src/bloqade/ir/routine/bloqade.py +++ b/src/bloqade/ir/routine/bloqade.py @@ -1,5 +1,5 @@ from collections import OrderedDict -from bloqade.ir.routine.base import RoutineBase +from bloqade.ir.routine.base import RoutineBase, __pydantic_dataclass_config__ from bloqade.builder.typing import LiteralType from bloqade.task.batch import LocalBatch from beartype import beartype @@ -7,13 +7,13 @@ from dataclasses import dataclass -@dataclass(frozen=True) +@dataclass(frozen=True, config=__pydantic_dataclass_config__) class BloqadeServiceOptions(RoutineBase): def python(self): return BloqadePythonRoutine(self.source, self.circuit, self.params) -@dataclass(frozen=True) +@dataclass(frozen=True, config=__pydantic_dataclass_config__) class BloqadePythonRoutine(RoutineBase): def _compile( self, diff --git a/src/bloqade/ir/routine/braket.py b/src/bloqade/ir/routine/braket.py index c2c718b4b..c0b5f52ae 100644 --- a/src/bloqade/ir/routine/braket.py +++ b/src/bloqade/ir/routine/braket.py @@ -4,14 +4,14 @@ from beartype.typing import Optional, Tuple from bloqade.builder.typing import LiteralType -from bloqade.ir.routine.base import RoutineBase +from bloqade.ir.routine.base import RoutineBase, __pydantic_dataclass_config__ from bloqade.submission.braket import BraketBackend from bloqade.task.batch import LocalBatch, RemoteBatch from bloqade.task.braket_simulator import BraketEmulatorTask from bloqade.task.braket import BraketTask -@dataclass(frozen=True) +@dataclass(frozen=True, config=__pydantic_dataclass_config__) class BraketServiceOptions(RoutineBase): def aquila(self) -> "BraketHardwareRoutine": backend = BraketBackend( @@ -23,7 +23,7 @@ def local_emulator(self): return BraketLocalEmulatorRoutine(self.source, self.circuit, self.params) -@dataclass(frozen=True) +@dataclass(frozen=True, config=__pydantic_dataclass_config__) class BraketHardwareRoutine(RoutineBase): backend: BraketBackend @@ -163,7 +163,7 @@ def __call__( return self.run(shots, args, name, shuffle, **kwargs) -@dataclass(frozen=True) +@dataclass(frozen=True, config=__pydantic_dataclass_config__) class BraketLocalEmulatorRoutine(RoutineBase): def _compile( self, shots: int, args: Tuple[LiteralType, ...] = (), name: Optional[str] = None diff --git a/src/bloqade/ir/routine/quera.py b/src/bloqade/ir/routine/quera.py index 5f6c9fa23..717b90b33 100644 --- a/src/bloqade/ir/routine/quera.py +++ b/src/bloqade/ir/routine/quera.py @@ -3,7 +3,7 @@ import json from bloqade.builder.typing import LiteralType -from bloqade.ir.routine.base import RoutineBase +from bloqade.ir.routine.base import RoutineBase, __pydantic_dataclass_config__ from bloqade.submission.quera import QuEraBackend from bloqade.submission.mock import MockBackend from bloqade.submission.quera_api_client.load_config import load_config @@ -14,7 +14,7 @@ from beartype import beartype -@dataclass(frozen=True) +@dataclass(frozen=True, config=__pydantic_dataclass_config__) class QuEraServiceOptions(RoutineBase): @beartype def device(self, config_file: Optional[str], **api_config): @@ -40,7 +40,7 @@ def mock(self, state_file: str = ".mock_state.txt") -> "QuEraHardwareRoutine": return QuEraHardwareRoutine(self.source, self.circuit, self.params, backend) -@dataclass(frozen=True) +@dataclass(frozen=True, config=__pydantic_dataclass_config__) class QuEraHardwareRoutine(RoutineBase): backend: Union[QuEraBackend, MockBackend] From dd3eedb373d95ffa02fb187c57ba85f85a605a66 Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Fri, 29 Sep 2023 11:09:35 -0400 Subject: [PATCH 12/14] updating unit test for routine return value. --- tests/test_builder_factory.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/tests/test_builder_factory.py b/tests/test_builder_factory.py index 65eb3728c..95d2e9b11 100644 --- a/tests/test_builder_factory.py +++ b/tests/test_builder_factory.py @@ -20,8 +20,8 @@ Field, Uniform, ) -from bloqade.ir.routine.params import Params from bloqade.ir.routine.base import Routine +from bloqade.ir.routine.params import Params import numpy as np @@ -89,7 +89,7 @@ def delta(t, amp, omega): batch_params = [{"omega": omega} for omega in [1, 2, 4, 8]] args = ["run_time"] - prog = rydberg_h( + routine = rydberg_h( register, detuning=delta, amplitude=ampl, @@ -108,12 +108,23 @@ def delta(t, amp, omega): ) sequence = Sequence({rydberg: pulse}) - routine = prog.parse() + source = ( + register.rydberg.detuning.uniform.apply(delta) + .amplitude.uniform.apply(ampl) + .phase.uniform.apply(phase) + .assign(**static_params) + .batch_assign(batch_params) + .args(args) + ) circuit = AnalogCircuit(register, sequence) params = Params(static_params, batch_params, args) + expected_routine = Routine(source, circuit, params) - assert routine == Routine(prog, circuit, params) + # ignore because no equality implemented + # assert routine.source == expected_routine.source + assert routine.circuit == expected_routine.circuit + assert routine.params == expected_routine.params def test_rydberg_h_2(): From c5bf722c9d6e126b81b7ed3f88611a286d1f043d Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Fri, 29 Sep 2023 11:12:25 -0400 Subject: [PATCH 13/14] renaming tests for `bloqade.factory`. --- tests/{test_builder_factory.py => test_factory.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{test_builder_factory.py => test_factory.py} (100%) diff --git a/tests/test_builder_factory.py b/tests/test_factory.py similarity index 100% rename from tests/test_builder_factory.py rename to tests/test_factory.py From 4d133b6dfc8ce1adda8f15e5c83028195b7c2bda Mon Sep 17 00:00:00 2001 From: Phillip Weinberg Date: Fri, 29 Sep 2023 11:14:31 -0400 Subject: [PATCH 14/14] making dataclasses into pydantic.dataclasses. --- src/bloqade/ir/routine/bloqade.py | 2 +- src/bloqade/ir/routine/braket.py | 2 +- src/bloqade/ir/routine/quera.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/bloqade/ir/routine/bloqade.py b/src/bloqade/ir/routine/bloqade.py index eccc7afaa..da87a5ec1 100644 --- a/src/bloqade/ir/routine/bloqade.py +++ b/src/bloqade/ir/routine/bloqade.py @@ -4,7 +4,7 @@ from bloqade.task.batch import LocalBatch from beartype import beartype from beartype.typing import Optional, Tuple -from dataclasses import dataclass +from pydantic.dataclasses import dataclass @dataclass(frozen=True, config=__pydantic_dataclass_config__) diff --git a/src/bloqade/ir/routine/braket.py b/src/bloqade/ir/routine/braket.py index c0b5f52ae..271d5c8ff 100644 --- a/src/bloqade/ir/routine/braket.py +++ b/src/bloqade/ir/routine/braket.py @@ -1,5 +1,5 @@ from collections import OrderedDict -from dataclasses import dataclass +from pydantic.dataclasses import dataclass from beartype import beartype from beartype.typing import Optional, Tuple from bloqade.builder.typing import LiteralType diff --git a/src/bloqade/ir/routine/quera.py b/src/bloqade/ir/routine/quera.py index 717b90b33..4ef8c1e71 100644 --- a/src/bloqade/ir/routine/quera.py +++ b/src/bloqade/ir/routine/quera.py @@ -1,5 +1,5 @@ from collections import OrderedDict -from dataclasses import dataclass +from pydantic.dataclasses import dataclass import json from bloqade.builder.typing import LiteralType