Skip to content

Commit d151ad2

Browse files
committed
Merge branch 'main' into 625-establish-documentation-standards
2 parents 15401e3 + ed86fed commit d151ad2

22 files changed

+526
-270
lines changed

pdm.lock

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "bloqade"
3-
version = "0.5.1"
3+
version = "0.6.0"
44
description = "Neutral atom software development kit"
55
authors = [
66
{name = "QuEra Computing Inc.", email = "[email protected]"},
@@ -17,7 +17,7 @@ classifiers = [
1717
dependencies = [
1818
"juliacall>=0.9.14",
1919
"numpy>=1.25.2",
20-
"pydantic>=1.10.12",
20+
"pydantic>=1.10.13",
2121
"scipy>=1.9.3",
2222
"pandas>=2.1.0",
2323
"bokeh>=3.2.2",

src/bloqade/__init__.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
from bloqade.ir import var, cast, Variable, Literal, start
2+
from bloqade.ir import to_waveform as waveform
23
from bloqade.serialize import load, save, loads, dumps
34

4-
from bloqade.builder.factory import (
5+
from bloqade.factory import (
56
piecewise_linear,
67
piecewise_constant,
78
linear,
89
constant,
10+
rydberg_h,
911
)
1012
import bloqade.ir as _ir
1113
from bloqade.constants import RB_C6
1214

15+
import importlib.metadata
16+
17+
__version__ = importlib.metadata.version("bloqade")
18+
1319

1420
def tree_depth(depth: int = None):
1521
"""Setting globally maximum depth for tree printing
@@ -44,4 +50,6 @@ def tree_depth(depth: int = None):
4450
"save",
4551
"loads",
4652
"dumps",
53+
"rydberg_h",
54+
"waveform",
4755
]

src/bloqade/builder/assign.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,10 @@ def __init__(
9393

9494
super().__init__(parent)
9595

96+
if len(assignments) == 0:
97+
self._batch_params = []
98+
return
99+
96100
circuit = self.parse_circuit()
97101
variables = ScanVariablesAnalogCircuit().emit(circuit)
98102

src/bloqade/builder/backend/quera.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def cloud_mock(self):
100100
"""
101101
return self.parse().quera.cloud_mock()
102102

103-
def mock(self, state_file: str = ".mock_state.txt"):
103+
def mock(self, state_file: str = ".mock_state.txt", submission_error: bool = False):
104104
"""
105105
Specify mock, testing locally.
106106
@@ -123,4 +123,6 @@ def mock(self, state_file: str = ".mock_state.txt"):
123123
124124
125125
"""
126-
return self.parse().quera.mock(state_file)
126+
return self.parse().quera.mock(
127+
state_file=state_file, submission_error=submission_error
128+
)

src/bloqade/builder/factory.py

Lines changed: 0 additions & 42 deletions
This file was deleted.

src/bloqade/factory.py

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
from bloqade.ir.routine.base import Routine
2+
from bloqade.ir.control.waveform import Waveform, Linear, Constant
3+
from bloqade.builder.typing import ScalarType
4+
from beartype import beartype
5+
from beartype.typing import List, Optional, Union, Dict, Any
6+
7+
8+
@beartype
9+
def linear(duration: ScalarType, start: ScalarType, stop: ScalarType) -> Linear:
10+
"""Create a Linear waveform.
11+
12+
Args:
13+
duration (ScalarType): duration of linear waveform
14+
start (ScalarType): starting value of linear waveform
15+
stop (ScalarType): ending value of linear waveform
16+
17+
Returns:
18+
Linear: Linear waveform
19+
"""
20+
return Linear(start, stop, duration)
21+
22+
23+
@beartype
24+
def constant(duration: ScalarType, value: ScalarType) -> Constant:
25+
"""Create a Constant waveform.
26+
27+
Args:
28+
duration (ScalarType): _description_
29+
value (ScalarType): _description_
30+
31+
Returns:
32+
Constant: A Constant waveform.
33+
"""
34+
return Constant(value, duration)
35+
36+
37+
@beartype
38+
def piecewise_linear(durations: List[ScalarType], values: List[ScalarType]) -> Waveform:
39+
"""Create a piecewise linear waveform.
40+
41+
Create a piecewise linear waveform from a list of durations and values. The
42+
value `duration[i]` is of the linear segment between `values[i]` and `values[i+1]`.
43+
44+
Args:
45+
durations (List[ScalarType]): The duration of each segment
46+
values (List[ScalarType]): The values for each segment
47+
48+
Raises:
49+
ValueError: If the length of `values` is not one greater than the length of
50+
`durations`.
51+
52+
Returns:
53+
Waveform: The piecewise linear waveform.
54+
"""
55+
56+
if len(durations) + 1 != len(values):
57+
raise ValueError(
58+
"The length of values must be one greater than the length of durations"
59+
)
60+
61+
pwl_wf = None
62+
for duration, start, stop in zip(durations, values[:-1], values[1:]):
63+
if pwl_wf is None:
64+
pwl_wf = Linear(start, stop, duration)
65+
else:
66+
pwl_wf = pwl_wf.append(Linear(start, stop, duration))
67+
68+
return pwl_wf
69+
70+
71+
@beartype
72+
def piecewise_constant(
73+
durations: List[ScalarType], values: List[ScalarType]
74+
) -> Waveform:
75+
"""Create a piecewise linear waveform.
76+
77+
Create a piecewise constant waveform from a list of durations and values. The
78+
value `duration[i]` corresponds to the length of time for the i'th segment
79+
with a value of `values[i]`.
80+
81+
Args:
82+
durations (List[ScalarType]): The duration of each segment
83+
values (List[ScalarType]): The values for each segment
84+
85+
Raises:
86+
ValueError: If the length of `values` is not the same as the length of
87+
`durations`.
88+
89+
Returns:
90+
Waveform: The piecewise linear waveform.
91+
"""
92+
if len(durations) != len(values):
93+
raise ValueError(
94+
"The length of values must be the same as the length of durations"
95+
)
96+
97+
pwc_wf = None
98+
for duration, value in zip(durations, values):
99+
if pwc_wf is None:
100+
pwc_wf = Constant(value, duration)
101+
else:
102+
pwc_wf = pwc_wf.append(Constant(value, duration))
103+
104+
return pwc_wf
105+
106+
107+
@beartype
108+
def rydberg_h(
109+
atoms_positions: Any,
110+
detuning: Optional[Waveform] = None,
111+
amplitude: Optional[Waveform] = None,
112+
phase: Optional[Waveform] = None,
113+
static_params: Dict[str, Any] = {},
114+
batch_params: Union[List[Dict[str, Any]], Dict[str, Any]] = [],
115+
args: List[str] = [],
116+
) -> Routine:
117+
"""Create a rydberg program with uniform detuning, amplitude, and phase.
118+
119+
Args:
120+
atoms_positions (Any): Description of geometry of atoms in system.
121+
detuning (Optional[Waveform], optional): Waveform for detuning.
122+
Defaults to None.
123+
amplitude (Optional[Waveform], optional): Waveform describing the amplitude of
124+
the rabi term. Defaults to None.
125+
phase (Optional[Waveform], optional): Waveform describing the phase of rabi
126+
term. Defaults to None.
127+
static_params (Dict[str, Any], optional): Define static parameters of your
128+
program. Defaults to {}.
129+
batch_params (Union[List[Dict[str, Any]], Dict[str, Any]], optional):
130+
Parmaters for a batch of tasks. Defaults to [].
131+
args (List[str], optional): List of arguments to leave till runtime.
132+
Defaults to [].
133+
134+
Returns:
135+
Routine: An object that can be used to dispatch a rydberg program to
136+
multiple backends.
137+
"""
138+
from bloqade import start
139+
from bloqade.atom_arrangement import AtomArrangement
140+
141+
print(type(atoms_positions))
142+
143+
if isinstance(atoms_positions, AtomArrangement):
144+
prog = atoms_positions
145+
else:
146+
prog = start.add_position(atoms_positions)
147+
148+
if detuning is not None:
149+
prog = prog.rydberg.detuning.uniform.apply(detuning)
150+
151+
if amplitude is not None:
152+
prog = prog.amplitude.uniform.apply(amplitude)
153+
154+
if phase is not None:
155+
prog = prog.phase.uniform.apply(phase)
156+
157+
prog = prog.assign(**static_params)
158+
159+
if isinstance(batch_params, dict):
160+
prog = prog.batch_assign(**batch_params)
161+
else:
162+
prog = prog.batch_assign(batch_params)
163+
164+
prog = prog.args(args)
165+
166+
return prog.parse()

src/bloqade/ir/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
Interpolation,
1212
Sample,
1313
PythonFn,
14-
instruction,
14+
to_waveform,
1515
GaussianKernel,
1616
LogisticKernel,
1717
SigmoidKernel,
@@ -68,7 +68,7 @@
6868
"Sample",
6969
"Interpolation",
7070
"PythonFn",
71-
"instruction",
71+
"to_waveform",
7272
"GaussianKernel",
7373
"LogisticKernel",
7474
"SigmoidKernel",

src/bloqade/ir/control/waveform.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from numbers import Real
2+
from bloqade.builder.typing import ScalarType
23
from bloqade.ir.tree_print import Printer
34
from bloqade.ir.scalar import (
45
Scalar,
@@ -14,6 +15,7 @@
1415
from decimal import Decimal
1516
from pydantic.dataclasses import dataclass
1617
from beartype.typing import Any, Tuple, Union, List, Callable, Dict
18+
from beartype import beartype
1719
from enum import Enum
1820

1921
import numpy as np
@@ -23,7 +25,8 @@
2325
from bloqade.visualization import display_ir
2426

2527

26-
def instruction(duration: Any) -> "PythonFn":
28+
@beartype
29+
def to_waveform(duration: ScalarType) -> Callable[[Callable], "PythonFn"]:
2730
# turn python function into a waveform instruction."""
2831

2932
def waveform_wrapper(fn: Callable) -> "PythonFn":
@@ -486,6 +489,11 @@ def print_node(self):
486489
def children(self):
487490
return {"duration": self.duration, **{p.name: p for p in self.parameters}}
488491

492+
def sample(
493+
self, dt: ScalarType, interpolation: Union[str, "Interpolation"]
494+
) -> "Sample":
495+
return Sample(self, interpolation, cast(dt))
496+
489497

490498
@dataclass
491499
class SmoothingKernel:

src/bloqade/ir/routine/base.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
from bloqade.builder.base import Builder
66
from bloqade.ir.routine.params import Params
77

8-
9-
from dataclasses import dataclass
8+
from pydantic import ConfigDict
9+
from pydantic.dataclasses import dataclass
1010
from typing import TYPE_CHECKING, Union
1111

1212
if TYPE_CHECKING:
@@ -29,7 +29,10 @@ def parse(self: "RoutineBase") -> "Routine":
2929
return self
3030

3131

32-
@dataclass(frozen=True)
32+
__pydantic_dataclass_config__ = ConfigDict(arbitrary_types_allowed=True)
33+
34+
35+
@dataclass(frozen=True, config=__pydantic_dataclass_config__)
3336
class RoutineBase(RoutineParse):
3437
source: Builder
3538
circuit: AnalogCircuit
@@ -43,7 +46,7 @@ def __str__(self):
4346
return out
4447

4548

46-
@dataclass(frozen=True)
49+
@dataclass(frozen=True, config=__pydantic_dataclass_config__)
4750
class Routine(RoutineBase):
4851
"""Result of parsing a completed Builder string."""
4952

0 commit comments

Comments
 (0)