Skip to content

RB_C6 module-attribute

RB_C6 = 2 * pi * 862690

The C6 constant for the Rydberg Interaction of two Rubidium atoms in units of: rad μm^6/μs

start module-attribute

start = ListOfLocations()

A Program starting point, alias of empty [ListOfLocations][bloqade.ir.location.list.ListOfLocations].

  • Next possible steps to build your program are:
  • Specify which level coupling to address with:
    • start.rydberg: for [Rydberg][bloqade.builder.coupling.Rydberg] Level coupling
    • start.hyperfine: for [Hyperfine][bloqade.builder.coupling.Hyperfine] Level coupling
    • LOCKOUT: You cannot add atoms to your geometry after specifying level coupling.
  • continue/start building your geometry with:
    • start.add_position(): to add atom(s) to current register. It will accept:
      • A single coordinate, represented as a tuple (e.g. (5,6)) with a value that can either be:
        • integers: (5,6)
        • floats: (5.1, 2.5)
        • strings (for later variable assignment): ("x", "y")
        • [Scalar][bloqade.ir.scalar.Scalar] objects: (2*cast("x"), 5+cast("y"))
      • A list of coordinates, represented as a list of types mentioned previously.
      • A numpy array with shape (n, 2) where n is the total number of atoms

Literal

Bases: Real

value instance-attribute

value: Decimal

Scalar Literal, which stores a decimaal value instance.

Parameters:

Name Type Description Default
value Decimal

decimal value instance

required

Variable

Bases: Real

Variable, which stores a variable name.

Parameters:

Name Type Description Default
name str

variable instance.

required

cast

cast(py) -> Scalar
  1. cast Real number (or list/tuple of Real numbers) to [Scalar Literal][bloqade.ir.scalar.Literal].

  2. cast str (or list/tuple of Real numbers) to [Scalar Variable][bloqade.ir.scalar.Variable].

Parameters:

Name Type Description Default
py Union[str, Real, Tuple[Real], List[Real]]

python object to cast

required

Returns:

Type Description
Scalar

Scalar

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/scalar.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
def cast(py) -> "Scalar":
    """
    1. cast Real number (or list/tuple of Real numbers)
    to [`Scalar Literal`][bloqade.ir.scalar.Literal].

    2. cast str (or list/tuple of Real numbers)
    to [`Scalar Variable`][bloqade.ir.scalar.Variable].

    Args:
        py (Union[str,Real,Tuple[Real],List[Real]]): python object to cast

    Returns:
        Scalar
    """
    ret = trycast(py)
    if ret is None:
        raise TypeError(f"Cannot cast {type(py)} to Scalar Literal")

    return ret

constant

constant(
    duration: ScalarType, value: ScalarType
) -> Constant

Create a Constant waveform.

Parameters:

Name Type Description Default
duration ScalarType

Duration of the Constant waveform.

required
value ScalarType

Value of the Constant waveform.s

required

Returns:

Name Type Description
Constant Constant

A Constant waveform.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/factory.py
57
58
59
60
61
62
63
64
65
66
67
68
@beartype
def constant(duration: ScalarType, value: ScalarType) -> Constant:
    """Create a Constant waveform.

    Args:
        duration (ScalarType): Duration of the Constant waveform.
        value (ScalarType): Value of the Constant waveform.s

    Returns:
        Constant: A Constant waveform.
    """
    return Constant(value, duration)

dumps

dumps(
    o: Any, use_decimal: bool = True, **json_kwargs
) -> str

Serialize object to string

Parameters:

Name Type Description Default
o Any

the object to serialize

required
use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True
**json_kwargs

other arguments passed to json.dumps

{}

Returns:

Name Type Description
str str

the serialized object as a string

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/serialize.py
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
@beartype
def dumps(
    o: Any,
    use_decimal: bool = True,
    **json_kwargs,
) -> str:
    """Serialize object to string

    Args:
        o (Any): the object to serialize
        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.
        **json_kwargs: other arguments passed to json.dumps

    Returns:
        str: the serialized object as a string
    """
    if not isinstance(o, Serializer.types):
        raise TypeError(
            f"Object of type {type(o)} is not JSON serializable. "
            f"Only {Serializer.types} are supported."
        )
    return json.dumps(o, cls=Serializer, use_decimal=use_decimal, **json_kwargs)

get_capabilities

get_capabilities(
    use_experimental: bool = False,
) -> QuEraCapabilities

Get the device capabilities for Aquila

Parameters:

Name Type Description Default
use_experimental bool

Get experimental capabilities instead of standard ones. By default value is False.

False

Returns:

Name Type Description
QuEraCapabilities QuEraCapabilities

capabilities object for Aquila device.

Note

Units of time, distance, and energy are microseconds (us), micrometers (um), and rad / us, respectively.

For a comprehensive list of capabilities, see the Hardware Reference page

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/factory.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def get_capabilities(use_experimental: bool = False) -> "QuEraCapabilities":
    """Get the device capabilities for Aquila

    Args:
        use_experimental (bool): Get experimental capabilities instead of
            standard ones. By default value is False.

    Returns:
        QuEraCapabilities: capabilities object for Aquila device.


    Note:
        Units of time, distance, and energy are microseconds (us),
        micrometers (um), and rad / us, respectively.

        For a comprehensive list of capabilities,
        see the [Hardware Reference](../../reference/hardware-capabilities.md)
        page
    """

    from bloqade.analog.submission.capabilities import get_capabilities

    # manually convert to units
    return get_capabilities(use_experimental=use_experimental).scale_units(
        Decimal("1e6"), Decimal("1e-6")
    )

linear

linear(
    duration: ScalarType,
    start: ScalarType,
    stop: ScalarType,
) -> Linear

Create a Linear waveform.

Parameters:

Name Type Description Default
duration ScalarType

Duration of linear waveform

required
start ScalarType

Starting value of linear waveform

required
stop ScalarType

Ending value of linear waveform

required

Returns:

Name Type Description
Linear Linear

Linear waveform

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/factory.py
42
43
44
45
46
47
48
49
50
51
52
53
54
@beartype
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)

load

load(
    fp: Union[TextIO, str],
    use_decimal: bool = True,
    **json_kwargs
)

Load object from file

Parameters:

Name Type Description Default
fp Union[TextIO, str]

the file path or file object

required
use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True
**json_kwargs

other arguments passed to json.load

{}

Returns:

Name Type Description
Any

the deserialized object

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/serialize.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
@beartype
def load(fp: Union[TextIO, str], use_decimal: bool = True, **json_kwargs):
    """Load object from file

    Args:
        fp (Union[TextIO, str]): the file path or file object
        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.
        **json_kwargs: other arguments passed to json.load

    Returns:
        Any: the deserialized object
    """
    load_bloqade()
    if isinstance(fp, str):
        with open(fp, "r") as f:
            return json.load(
                f,
                object_hook=Serializer.object_hook,
                use_decimal=use_decimal,
                **json_kwargs,
            )
    else:
        return json.load(
            fp,
            object_hook=Serializer.object_hook,
            use_decimal=use_decimal,
            **json_kwargs,
        )

loads

loads(s: str, use_decimal: bool = True, **json_kwargs)

Load object from string

Parameters:

Name Type Description Default
s str

the string to load

required
use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True
**json_kwargs

other arguments passed to json.loads

{}

Returns:

Name Type Description
Any

the deserialized object

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/serialize.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
@beartype
def loads(s: str, use_decimal: bool = True, **json_kwargs):
    """Load object from string

    Args:
        s (str): the string to load
        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.
        **json_kwargs: other arguments passed to json.loads

    Returns:
        Any: the deserialized object
    """
    load_bloqade()
    return json.loads(
        s, object_hook=Serializer.object_hook, use_decimal=use_decimal, **json_kwargs
    )

piecewise_constant

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].

Parameters:

Name Type Description Default
durations List[ScalarType]

The duration of each segment

required
values List[ScalarType]

The values for each segment

required

Raises:

Type Description
ValueError

If the length of values is not the same as the length of

Returns:

Name Type Description
Waveform Waveform

The piecewise linear waveform.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/factory.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
@beartype
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

    Raises:
        ValueError: If the length of `values` is not the same as the length of
        `durations`.

    Returns:
        Waveform: The piecewise linear waveform.
    """
    if len(durations) != len(values):
        raise ValueError(
            "The length of values must be the same as the length of durations"
        )

    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

piecewise_linear

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].

Parameters:

Name Type Description Default
durations List[ScalarType]

The duration of each segment

required
values List[ScalarType]

The values for each segment

required

Raises:

Type Description
ValueError

If the length of values is not one greater than the length of

Returns:

Name Type Description
Waveform Waveform

The piecewise linear waveform.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/factory.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
@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:
            pwl_wf = Linear(start, stop, duration)
        else:
            pwl_wf = pwl_wf.append(Linear(start, stop, duration))

    return pwl_wf

rydberg_h

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] = [],
) -> Routine

Create a rydberg program with uniform detuning, amplitude, and phase.

Parameters:

Name Type Description Default
atoms_positions Any

Description of geometry of atoms in system.

required
detuning Optional[Waveform]

Waveform for detuning. Defaults to None.

None
amplitude Optional[Waveform]

Waveform describing the amplitude of the rabi term. Defaults to None.

None
phase Optional[Waveform]

Waveform describing the phase of rabi term. Defaults to None.

None
static_params Dict[str, Any]

Define static parameters of your program. Defaults to {}.

{}
batch_params Union[List[Dict[str, Any]], Dict[str, Any]]

Parmaters for a batch of tasks. Defaults to [].

[]
args List[str]

List of arguments to leave till runtime. Defaults to [].

[]

Returns:

Name Type Description
Routine Routine

An object that can be used to dispatch a rydberg program to multiple backends.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/factory.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
@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] = [],
) -> 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.analog import start
    from bloqade.analog.atom_arrangement import AtomArrangement

    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)

    if amplitude is not None:
        prog = prog.amplitude.uniform.apply(amplitude)

    if phase is not None:
        prog = prog.phase.uniform.apply(phase)

    prog = prog.assign(**static_params)

    if isinstance(batch_params, dict):
        prog = prog.batch_assign(**batch_params)
    else:
        prog = prog.batch_assign(batch_params)

    prog = prog.args(args)

    return prog.parse()

save

save(
    o: Any,
    fp: Union[TextIO, str],
    use_decimal=True,
    **json_kwargs
) -> None

Serialize object to file

Parameters:

Name Type Description Default
o Any

the object to serialize

required
fp Union[TextIO, str]

the file path or file object

required
use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True
**json_kwargs

other arguments passed to json.dump

{}

Returns:

Type Description
None

None

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/serialize.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
@beartype
def save(
    o: Any,
    fp: Union[TextIO, str],
    use_decimal=True,
    **json_kwargs,
) -> None:
    """Serialize object to file

    Args:
        o (Any): the object to serialize
        fp (Union[TextIO, str]): the file path or file object
        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.
        **json_kwargs: other arguments passed to json.dump

    Returns:
        None
    """
    if not isinstance(o, Serializer.types):
        raise TypeError(
            f"Object of type {type(o)} is not JSON serializable. "
            f"Only {Serializer.types} are supported."
        )
    if isinstance(fp, str):
        with open(fp, "w") as f:
            json.dump(o, f, cls=Serializer, use_decimal=use_decimal, **json_kwargs)
    else:
        json.dump(o, fp, cls=Serializer, use_decimal=use_decimal, **json_kwargs)

tree_depth

tree_depth(depth: int = None)

Setting globally maximum depth for tree printing

If depth=None, return current depth. If depth is provided, setting current depth to depth

Parameters:

Name Type Description Default
depth int

the user specified depth. Defaults to None.

None

Returns:

Name Type Description
int

current updated depth

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/__init__.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def tree_depth(depth: int = None):
    """Setting globally maximum depth for tree printing

    If `depth=None`, return current depth.
    If `depth` is provided, setting current depth to `depth`

    Args:
        depth (int, optional): the user specified depth. Defaults to None.

    Returns:
        int: current updated depth
    """
    if depth is not None:
        _ir.tree_print.MAX_TREE_DEPTH = depth
    return _ir.tree_print.MAX_TREE_DEPTH

var

var(py: str) -> Variable

cast string (or list/tuple of strings) to [Variable][bloqade.ir.scalar.Variable].

Parameters:

Name Type Description Default
py Union[str, List[str]]

a string or list/tuple of strings

required

Returns:

Type Description
Variable

Union[Variable]

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/scalar.py
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
def var(py: str) -> "Variable":
    """cast string (or list/tuple of strings)
    to [`Variable`][bloqade.ir.scalar.Variable].

    Args:
        py (Union[str, List[str]]): a string or list/tuple of strings

    Returns:
       Union[Variable]
    """
    ret = tryvar(py)
    if ret is None:
        raise TypeError(f"Cannot cast {type(py)} to Variable")

    return ret

atom_arrangement

AtomArrangement

AtomArrangement(parent: Optional[Builder] = None)

Bases: ProgramStart

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

n_atoms property

n_atoms: int

number of atoms (filled sites) in the register.

n_dims property

n_dims: int

number of dimensions in the register.

n_sites property

n_sites: int

number of sites in the register.

n_vacant property

n_vacant: int

number of vacant sites in the register.

add_position

add_position(
    position: Union[
        PositionArray,
        List[Tuple[ScalarType, ScalarType]],
        Tuple[ScalarType, ScalarType],
    ],
    filling: Optional[
        Union[BoolArray, List[bool], bool]
    ] = None,
) -> ListOfLocations

Add a position or multiple positions to a pre-existing geometry.

add_position is capable of accepting: - A single tuple for one atom coordinate: (1.0, 2.5) - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.] - A numpy array of shape (N, 2) where N is the number of atoms

You may also intersperse variables anywhere a value may be present.

You can also pass in an optional argument which determines the atom "filling" (whether or not at a specified coordinate an atom should be present).

Usage Example:
# single coordinate
>>> reg = start.add_position((0,0))
# you may chain add_position calls
>>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])
# you can add variables anywhere a value may be present
>>> reg_with_var = reg_plus_two.add_position(("x", "y"))
# and specify your atom fillings
>>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],
[True, False])
# alternatively you could use one boolean to specify
# all coordinates should be empty/filled
>>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),
(5.2, 2.2)], False)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
def add_position(
    self,
    position: Union[
        PositionArray,
        List[Tuple[ScalarType, ScalarType]],
        Tuple[ScalarType, ScalarType],
    ],
    filling: Optional[Union[BoolArray, List[bool], bool]] = None,
) -> "ListOfLocations":
    """
    Add a position or multiple positions to a pre-existing geometry.

    `add_position` is capable of accepting:
    - A single tuple for one atom coordinate: `(1.0, 2.5)`
    - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.]
    - A numpy array of shape (N, 2) where N is the number of atoms

    You may also intersperse variables anywhere a value may be present.

    You can also pass in an optional argument which determines the atom "filling"
    (whether or not at a specified coordinate an atom should be present).

    ### Usage Example:
    ```
    # single coordinate
    >>> reg = start.add_position((0,0))
    # you may chain add_position calls
    >>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])
    # you can add variables anywhere a value may be present
    >>> reg_with_var = reg_plus_two.add_position(("x", "y"))
    # and specify your atom fillings
    >>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],
    [True, False])
    # alternatively you could use one boolean to specify
    # all coordinates should be empty/filled
    >>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),
    (5.2, 2.2)], False)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...add_position(positions).add_position(positions)`:
            to add more positions
        - `...add_position(positions).apply_defect_count(n_defects)`:
        to randomly drop out n_atoms
        - `...add_position(positions).apply_defect_density(defect_probability)`:
        to drop out atoms with a certain probability
        - `...add_position(positions).scale(scale)`: to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...add_position(positions).rydberg`: to specify Rydberg coupling
        - `...add_position(positions).hyperfine`: to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...add_position(positions).show()`:
        shows your geometry in your web browser

    """

    if is_bearable(position, PositionArray) and is_bearable(
        filling, Optional[BoolArray]
    ):
        return self.add_position_ndarray(position, filling)
    elif is_bearable(position, List[Tuple[ScalarType, ScalarType]]) and is_bearable(
        filling, Optional[List[bool]]
    ):
        return self.add_position_list_tuples(position, filling)
    elif is_bearable(position, Tuple[ScalarType, ScalarType]) and is_bearable(
        filling, Optional[bool]
    ):
        return self.add_position_single_tupe(position, filling)
    else:
        raise TypeError("Invalid input types for add_position provided!")

apply_defect_count

apply_defect_count(
    n_defects: int, rng: Generator = np.random.default_rng()
)

Drop n_defects atoms from the geometry randomly. Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

Usage Example:
>>> from bloqade.analog.atom_arrangement import Chain
>>> import numpy as np
# set a custom seed for a numpy-based RNG
>>> custom_rng = np.random.default_rng(888)
# randomly remove two atoms from the geometry
>>> reg = Chain(11).apply_defect_count(2, custom_rng)
# you may also chain apply_defect_count calls
>>> reg.apply_defect_count(2, custom_rng)
# you can also use apply_defect_count on custom geometries
>>> from bloqade import start
>>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts) .apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
@beartype
def apply_defect_count(
    self, n_defects: int, rng: np.random.Generator = np.random.default_rng()
):
    """
    Drop `n_defects` atoms from the geometry randomly. Internally this occurs
    by setting certain sites to have a SiteFilling set to false indicating
    no atom is present at the coordinate.

    A default numpy-based Random Number Generator is used but you can
    explicitly override this by passing in your own.

    ### Usage Example:

    ```
    >>> from bloqade.analog.atom_arrangement import Chain
    >>> import numpy as np
    # set a custom seed for a numpy-based RNG
    >>> custom_rng = np.random.default_rng(888)
    # randomly remove two atoms from the geometry
    >>> reg = Chain(11).apply_defect_count(2, custom_rng)
    # you may also chain apply_defect_count calls
    >>> reg.apply_defect_count(2, custom_rng)
    # you can also use apply_defect_count on custom geometries
    >>> from bloqade import start
    >>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...apply_defect_count(defect_counts).add_position(positions)`:
            to add more positions
        - `...apply_defect_count(defect_counts)
            .apply_defect_count(n_defects)`: to randomly drop out n_atoms
        - `...apply_defect_count(defect_counts)
            .apply_defect_density(defect_probability)`:
            to drop out atoms with a certain probability
        - `...apply_defect_count(defect_counts).scale(scale)`:
            to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...apply_defect_count(defect_counts).rydberg`: to specify
            Rydberg coupling
        - `...apply_defect_count(defect_counts).hyperfine`:
            to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...apply_defect_count(defect_counts).show()`:
            shows your geometry in your web browser
    """

    location_list = []
    for location_info in self.enumerate():
        location_list.append(location_info)

    filled_sites = []

    for index, location_info in enumerate(location_list):
        if location_info.filling is SiteFilling.filled:
            filled_sites.append(index)

    if n_defects >= len(filled_sites):
        raise ValueError(
            f"n_defects {n_defects} must be less than the number of filled sites "
            f"({len(filled_sites)})"
        )

    for _ in range(n_defects):
        index = rng.choice(filled_sites)
        location_list[index] = LocationInfo.create(
            location_list[index].position,
            (False if location_list[index].filling is SiteFilling.filled else True),
        )
        filled_sites.remove(index)

    return ListOfLocations(location_list)

apply_defect_density

apply_defect_density(
    defect_probability: float,
    rng: Generator = np.random.default_rng(),
)

Drop atoms randomly with defect_probability probability (range of 0 to 1). Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

Usage Example:
>>> from bloqade.analog.atom_arrangement import Chain
>>> import numpy as np
# set a custom seed for a numpy-based RNG
>>> custom_rng = np.random.default_rng(888)
# randomly remove two atoms from the geometry
>>> reg = Chain(11).apply_defect_density(0.2, custom_rng)
# you may also chain apply_defect_density calls
>>> reg.apply_defect_count(0.1, custom_rng)
# you can also use apply_defect_density on custom geometries
>>> from bloqade import start
>>> start.add_position([(0,0), (1,1)])
.apply_defect_density(0.5, custom_rng)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
@beartype
def apply_defect_density(
    self,
    defect_probability: float,
    rng: np.random.Generator = np.random.default_rng(),
):
    """
    Drop atoms randomly with `defect_probability` probability (range of 0 to 1).
    Internally this occurs by setting certain sites to have a SiteFilling
    set to false indicating no atom is present at the coordinate.

    A default numpy-based Random Number Generator is used but you can
    explicitly override this by passing in your own.

    ### Usage Example:

    ```
    >>> from bloqade.analog.atom_arrangement import Chain
    >>> import numpy as np
    # set a custom seed for a numpy-based RNG
    >>> custom_rng = np.random.default_rng(888)
    # randomly remove two atoms from the geometry
    >>> reg = Chain(11).apply_defect_density(0.2, custom_rng)
    # you may also chain apply_defect_density calls
    >>> reg.apply_defect_count(0.1, custom_rng)
    # you can also use apply_defect_density on custom geometries
    >>> from bloqade import start
    >>> start.add_position([(0,0), (1,1)])
    .apply_defect_density(0.5, custom_rng)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...apply_defect_count(defect_counts).add_position(positions)`:
        to add more positions
        - `...apply_defect_count(defect_counts).apply_defect_count(n_defects)`:
        to randomly drop out n_atoms
        - `...apply_defect_count(defect_counts)
        .apply_defect_density(defect_probability)`:
        to drop out atoms with a certain probability
        - `...apply_defect_count(defect_counts).scale(scale)`:
        to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...apply_defect_count(defect_counts).rydberg`:
        to specify Rydberg coupling
        - `...apply_defect_count(defect_counts).hyperfine`:
        to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...apply_defect_count(defect_counts).show()`:
        shows your geometry in your web browser
    """

    p = min(1, max(0, defect_probability))
    location_list = []

    for location_info in self.enumerate():
        if rng.random() < p:
            location_list.append(
                LocationInfo.create(
                    location_info.position,
                    (
                        False
                        if location_info.filling is SiteFilling.filled
                        else True
                    ),
                )
            )
        else:
            location_list.append(location_info)

    return ListOfLocations(location_list=location_list)

enumerate

enumerate() -> Generator[LocationInfo, None, None]

enumerate all locations in the register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
128
129
130
def enumerate(self) -> Generator[LocationInfo, None, None]:
    """enumerate all locations in the register."""
    raise NotImplementedError

figure

figure(fig_kwargs=None, **assignments)

obtain a figure object from the atom arrangement.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
132
133
134
def figure(self, fig_kwargs=None, **assignments):
    """obtain a figure object from the atom arrangement."""
    return get_atom_arrangement_figure(self, fig_kwargs=fig_kwargs, **assignments)

rydberg_interaction

rydberg_interaction(**assignments) -> NDArray

calculate the Rydberg interaction matrix.

Parameters:

Name Type Description Default
**assignments

the values to assign to the variables in the register.

{}

Returns:

Name Type Description
NDArray NDArray

the Rydberg interaction matrix in the lower triangular form.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
def rydberg_interaction(self, **assignments) -> NDArray:
    """calculate the Rydberg interaction matrix.

    Args:
        **assignments: the values to assign to the variables in the register.

    Returns:
        NDArray: the Rydberg interaction matrix in the lower triangular form.

    """

    from bloqade.analog.constants import RB_C6

    # calculate the Interaction matrix
    V_ij = np.zeros((self.n_sites, self.n_sites))
    for i, site_i in enumerate(self.enumerate()):
        pos_i = np.array([float(ele(**assignments)) for ele in site_i.position])

        for j, site_j in enumerate(self.enumerate()):
            if j >= i:
                break  # enforce lower triangular form

            pos_j = np.array([float(ele(**assignments)) for ele in site_j.position])
            r_ij = np.linalg.norm(pos_i - pos_j)

            V_ij[i, j] = RB_C6 / r_ij**6

    return V_ij

scale

scale(scale: ScalarType)

Scale the geometry of your atoms.

Usage Example:
>>> reg = start.add_position([(0,0), (1,1)])
# atom positions are now (0,0), (2,2)
>>> new_reg = reg.scale(2)
# you may also use scale on pre-defined geometries
>>> from bloqade.analog.atom_arrangement import Chain
# atoms in the chain will now be 2 um apart versus
# the default 1 um
>>> Chain(11).scale(2)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
@beartype
def scale(self, scale: ScalarType):
    """
    Scale the geometry of your atoms.

    ### Usage Example:
    ```
    >>> reg = start.add_position([(0,0), (1,1)])
    # atom positions are now (0,0), (2,2)
    >>> new_reg = reg.scale(2)
    # you may also use scale on pre-defined geometries
    >>> from bloqade.analog.atom_arrangement import Chain
    # atoms in the chain will now be 2 um apart versus
    # the default 1 um
    >>> Chain(11).scale(2)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...add_position(positions).add_position(positions)`:
            to add more positions
        - `...add_position(positions).apply_defect_count(n_defects)`:
        to randomly drop out n_atoms
        - `...add_position(positions).apply_defect_density(defect_probability)`:
        to drop out atoms with a certain probability
        - `...add_position(positions).scale(scale)`: to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...add_position(positions).rydberg`:
        to specify Rydberg coupling
        - `...add_position(positions).hyperfine`:
        to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...add_position(positions).show()`:
        shows your geometry in your web browser

    """

    scale = cast(scale)
    location_list = []
    for location_info in self.enumerate():
        x, y = location_info.position
        new_position = (scale * x, scale * y)
        location_list.append(
            LocationInfo.create(new_position, bool(location_info.filling.value))
        )

    return ListOfLocations(location_list)

show

show(**assignments) -> None

Display the current program being defined with the given arguments and batch ID.

Parameters:

Name Type Description Default
*args

Additional arguments for display.

()
batch_id int

The batch ID to be displayed. Defaults to 0.

0
Note

This method uses the display_builder function to render the builder's state.

Example:

>>> class MyBuilder(Show):
...     pass
>>> builder = MyBuilder()
>>> builder.show()
>>> builder.show(batch_id=1)
>>> builder.show('arg1', 'arg2', batch_id=2)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
136
137
def show(self, **assignments) -> None:
    display_ir(self, assignments)

Chain

Chain(
    L: int,
    *,
    lattice_spacing: ScalarType = 1.0,
    vertical_chain: bool = False
)

Bases: BoundedBravais

Chain lattice.

  • 1D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0).
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default
L int

number of sites in the chain

required
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
183
184
185
186
187
188
189
190
@beartype
def __init__(
    self, L: int, *, lattice_spacing: ScalarType = 1.0, vertical_chain: bool = False
):
    self.L = L
    self.lattice_spacing = cast(lattice_spacing)
    self.vertical_chain = vertical_chain
    super().__init__()

Honeycomb

Honeycomb(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Honeycomb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (1/2, sqrt(3)/2)
  • unit cell (2 atom(s))
    • loc1 (0, 0)
    • loc2 (1/2, 1/(2*sqrt(3))

Parameters:

Name Type Description Default
L1 int

number of unit cells in linear direction. n_atoms = L1 * L1 * 2.

required
L2 Optional[int]

number of unit cells in direction a2. n_atoms = L1 * L2 * 2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
420
421
422
423
424
425
426
427
428
429
430
431
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1

    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)

    super().__init__()

Kagome

Kagome(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Kagome lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (1/2, sqrt(3)/2)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0.25 ,0.25sqrt(3))

Parameters:

Name Type Description Default
L1 int

number of sites in linear direction. n_atoms = 3 * L1 * L1.

required
L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
589
590
591
592
593
594
595
596
597
598
599
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1

    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)
    super().__init__()

Lieb

Lieb(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Lieb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (0, 1)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0 ,0.5)

Parameters:

Name Type Description Default
L1 int

number of unit cells in linear direction. n_atoms = 3* L1 * L1.

required
L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
534
535
536
537
538
539
540
541
542
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1
    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)

ListOfLocations

ListOfLocations(
    location_list: List[
        Union[LocationInfo, Tuple[ScalarType, ScalarType]]
    ] = [],
)

Bases: AtomArrangement

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
@beartype
def __init__(
    self,
    location_list: List[Union[LocationInfo, Tuple[ScalarType, ScalarType]]] = [],
):
    self.location_list = []
    for ele in location_list:
        if isinstance(ele, LocationInfo):
            self.location_list.append(ele)
        else:
            self.location_list.append(LocationInfo.create(ele, True))

    if self.location_list:
        self.__n_atoms = sum(
            1 for loc in self.location_list if loc.filling == SiteFilling.filled
        )
        self.__n_sites = len(self.location_list)
        self.__n_vacant = self.__n_sites - self.__n_atoms
        self.__n_dims = len(self.location_list[0].position)
    else:
        self.__n_sites = 0
        self.__n_atoms = 0
        self.__n_dims = None

    super().__init__()

n_atoms property

n_atoms

number of atoms (filled sites) in the register.

n_dims property

n_dims

number of dimensions in the register.

n_sites property

n_sites

number of sites in the register.

n_vacant property

n_vacant

number of vacant sites in the register.

enumerate

enumerate()

enumerate all locations in the register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
661
662
def enumerate(self):
    return iter(self.location_list)

Rectangular

Rectangular(
    width: int,
    height: int,
    *,
    lattice_spacing_x: ScalarType = 1.0,
    lattice_spacing_y: ScalarType = 1.0
)

Bases: BoundedBravais

Rectangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default
width int

number of sites in x direction.

required
height int

number of sites in y direction.

required
lattice_spacing_x (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
lattice_spacing_y (Scalar, Real)

lattice spacing in y direction. optional.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
@beartype
def __init__(
    self,
    width: int,
    height: int,
    *,
    lattice_spacing_x: ScalarType = 1.0,
    lattice_spacing_y: ScalarType = 1.0,
):
    self.width = width
    self.height = height
    self.lattice_spacing_x = cast(lattice_spacing_x)
    self.lattice_spacing_y = (
        cast(lattice_spacing_y)
        if lattice_spacing_y is not None
        else self.lattice_spacing_x
    )

    super().__init__()

Square

Square(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Square lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default
L1 int

number of sites in linear direction. n_atoms = L1 * L1.

required
L2 Optional[int]

number of sites in direction a2. n_atoms = L1 * L2, default is L1

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
240
241
242
243
244
245
246
247
248
249
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1
    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)
    super().__init__()

Triangular

Triangular(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Triangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (1/2, sqrt(3)/2)
  • unit cell (1 atom(s))
    • loc (0, 0)

Parameters:

Name Type Description Default
L int

number of sites in linear direction. n_atoms = L * L.

required
L2 Optional[int]

number of sites along a2 direction, n_atoms = L1 * L2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
477
478
479
480
481
482
483
484
485
486
487
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1
    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)

    super().__init__()

builder

backend

BackendRoute

BackendRoute(parent: Optional[Builder] = None)

Bases: QuEraService, BraketService, BloqadeService

Specify the backend to run your program on via a string (versus more formal builder syntax) of specifying the vendor/product first (Bloqade/Braket) and narrowing it down (e.g: ...device("quera.aquila") versus ...quera.aquila()) - You can pass the following arguments: - "braket.aquila" - "braket.local_emulator" - "bloqade.python" - "bloqade.julia"

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

bloqade

BloqadeDeviceRoute

BloqadeDeviceRoute(parent: Optional[Builder] = None)

Bases: Builder

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent
python
python()

Specify the Bloqade Python backend.

  • Possible Next Steps:
    • ...python().run(shots): to submit to the python emulator and await results
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/backend/bloqade.py
19
20
21
22
23
24
25
26
27
def python(self):
    """
    Specify the Bloqade Python backend.

    - Possible Next Steps:
        - `...python().run(shots)`:
            to submit to the python emulator and await results
    """
    return self.parse().bloqade.python()

BloqadeService

BloqadeService(parent: Optional[Builder] = None)

Bases: Builder

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent
bloqade property
bloqade

Specify the Bloqade backend.

  • Possible Next Steps:
    • ...bloqade.python(): target submission to the Bloqade python backend
    • ...bloqade.julia(): (CURRENTLY NOT IMPLEMENTED!)target submission to the Bloqade.jl backend

braket

BraketDeviceRoute

BraketDeviceRoute(parent: Optional[Builder] = None)

Bases: Builder

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent
aquila
aquila() -> BraketHardwareRoutine

Specify QuEra's Aquila QPU on Braket to submit your program to.

The number of shots you specify in the subsequent .run method will either: - dictate the number of times your program is run - dictate the number of times per parameter your program is run if you have a variable with batch assignments/intend to conduct a parameter sweep

  • Possible next steps are:
    • ...aquila().run(shots): To submit to hardware and WAIT for results (blocking)
    • ...aquila().run_async(shots): To submit to hardware and immediately allow for other operations to occur
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/backend/braket.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def aquila(self) -> "BraketHardwareRoutine":
    """
    Specify QuEra's Aquila QPU on Braket to submit your program to.

    The number of shots you specify in the subsequent `.run` method will either:
        - dictate the number of times your program is run
        - dictate the number of times per parameter your program is run if
          you have a variable with batch assignments/intend to conduct
          a parameter sweep


    - Possible next steps are:
        - `...aquila().run(shots)`: To submit to hardware and WAIT for
            results (blocking)
        - `...aquila().run_async(shots)`: To submit to hardware and immediately
            allow for other operations to occur
    """
    return self.parse().braket.aquila()
device
device(device_arn) -> BraketHardwareRoutine

Specify QPU based on the device ARN on Braket to submit your program to.

The number of shots you specify in the subsequent .run method will either: - dictate the number of times your program is run - dictate the number of times per parameter your program is run if you have a variable with batch assignments/intend to conduct a parameter sweep

  • Possible next steps are:
    • ...device(arn).run(shots): To submit to hardware and WAIT for results (blocking)
    • ...device(arn).run_async(shots): To submit to hardware and immediately allow for other operations to occur
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/backend/braket.py
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
def device(self, device_arn) -> "BraketHardwareRoutine":
    """
    Specify QPU based on the device ARN on Braket to submit your program to.

    The number of shots you specify in the subsequent `.run` method will either:
        - dictate the number of times your program is run
        - dictate the number of times per parameter your program is run if
            you have a variable with batch assignments/intend to conduct
            a parameter sweep


    - Possible next steps are:
        - `...device(arn).run(shots)`: To submit to hardware and WAIT for
            results (blocking)
        - `...device(arn).run_async(shots)`: To submit to hardware and immediately
            allow for other operations to occur
    """
    return self.parse().braket.device(device_arn)
local_emulator
local_emulator() -> BraketLocalEmulatorRoutine

Specify the Braket local emulator to submit your program to.

  • The number of shots you specify in the subsequent .run method will either:
    • dictate the number of times your program is run
    • dictate the number of times per parameter your program is run if you have a variable with batch assignments/intend to conduct a parameter sweep
  • Possible next steps are:
    • ...local_emulator().run(shots): to submit to the emulator and await results
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/backend/braket.py
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def local_emulator(self) -> "BraketLocalEmulatorRoutine":
    """
    Specify the Braket local emulator to submit your program to.

    - The number of shots you specify in the subsequent `.run` method will either:
        - dictate the number of times your program is run
        - dictate the number of times per parameter your program is run if
          you have a variable with batch assignments/intend to
          conduct a parameter sweep
    - Possible next steps are:
        - `...local_emulator().run(shots)`: to submit to the emulator
            and await results

    """
    return self.parse().braket.local_emulator()

BraketService

BraketService(parent: Optional[Builder] = None)

Bases: Builder

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent
braket property
braket

Specify the Braket backend. This allows you to access the AWS Braket local emulator OR go submit things to QuEra hardware on AWS Braket service.

  • Possible Next Steps are:
    • ...braket.aquila(): target submission to the QuEra Aquila QPU
    • ...braket.local_emulator(): target submission to the Braket local emulator

quera

QuEraDeviceRoute

QuEraDeviceRoute(parent: Optional[Builder] = None)

Bases: Builder

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent
aquila
aquila()

Specify QuEra's Aquila QPU

Return

QuEraHardwareRoutine

  • Possible Next:

    -> ...aquila().submit :: submit aync remote job

    -> ...aquila().run :: submit job and wait until job finished and results returned

    -> ...aquila().__callable__ :: submit job and wait until job finished and results returned

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/backend/quera.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
def aquila(self):
    """
    Specify QuEra's Aquila QPU

    Return:
        QuEraHardwareRoutine


    - Possible Next:

        -> `...aquila().submit`
            :: submit aync remote job

        -> `...aquila().run`
            :: submit job and wait until job finished
            and results returned

        -> `...aquila().__callable__`
            :: submit job and wait until job finished
            and results returned


    """
    return self.parse().quera.aquila()
cloud_mock
cloud_mock()

Specify QuEra's Remote Mock QPU

Return

QuEraHardwareRoutine

  • Possible Next:

    -> ...aquila().submit :: submit aync remote job

    -> ...aquila().run :: submit job and wait until job finished and results returned

    -> ...aquila().__callable__ :: submit job and wait until job finished and results returned

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/backend/quera.py
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
def cloud_mock(self):
    """
    Specify QuEra's Remote Mock QPU

    Return:
        QuEraHardwareRoutine

    - Possible Next:

        -> `...aquila().submit`
            :: submit aync remote job

        -> `...aquila().run`
            :: submit job and wait until job finished
            and results returned

        -> `...aquila().__callable__`
            :: submit job and wait until job finished
            and results returned



    """
    return self.parse().quera.cloud_mock()
custom
custom()

Specify custom backend

Return

CustomSubmissionRoutine

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/backend/quera.py
131
132
133
134
135
136
137
138
139
140
def custom(self):
    """
    Specify custom backend

    Return:
        CustomSubmissionRoutine

    """

    return self.parse().quera.custom()
device
device(config_file: Optional[str] = None, **api_config)

Specify QuEra's QPU device,

Parameters:

Name Type Description Default
config_file str

file that speficy the target hardware

None
Return

QuEraHardwareRoutine

  • Possible Next:

    -> ...device().submit :: submit aync remote job

    -> ...device().run :: submit job and wait until job finished and results returned

    -> ...device().__callable__ :: submit job and wait until job finished and results returned

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/backend/quera.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
def device(self, config_file: Optional[str] = None, **api_config):
    """
    Specify QuEra's QPU device,

    Args:
        config_file (str): file that speficy the target hardware

    Return:
        QuEraHardwareRoutine

    - Possible Next:

        -> `...device().submit`
            :: submit aync remote job

        -> `...device().run`
            :: submit job and wait until job finished
            and results returned

        -> `...device().__callable__`
            :: submit job and wait until job finished
            and results returned


    """
    return self.parse().quera.device(config_file, **api_config)
mock
mock(
    state_file: str = ".mock_state.txt",
    submission_error: bool = False,
)

Specify mock, testing locally.

Return

QuEraHardwareRoutine

  • Possible Next:

    -> ...aquila().submit :: submit aync remote job

    -> ...aquila().run :: submit job and wait until job finished and results returned

    -> ...aquila().__callable__ :: submit job and wait until job finished and results returned

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/backend/quera.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
def mock(self, state_file: str = ".mock_state.txt", submission_error: bool = False):
    """
    Specify mock, testing locally.

    Return:
        QuEraHardwareRoutine

    - Possible Next:

        -> `...aquila().submit`
            :: submit aync remote job

        -> `...aquila().run`
            :: submit job and wait until job finished
            and results returned

        -> `...aquila().__callable__`
            :: submit job and wait until job finished
            and results returned



    """
    return self.parse().quera.mock(
        state_file=state_file, submission_error=submission_error
    )

QuEraService

QuEraService(parent: Optional[Builder] = None)

Bases: Builder

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent
quera property
quera
  • Specify Quera backend
  • Possible Next:

    -> ...quera.aquila :: Aquila QPU

    -> ...quera.mock :: mock backend, meant for testings

    -> ...quera.device :: QuEra QPU, specifiy by config_file

coupling

Hyperfine

Hyperfine(parent: Optional[Builder] = None)

Bases: LevelCoupling

This node represents level coupling between hyperfine states.

Examples:

- To reach the node from the start node:

>>> node = bloqade.start.hyperfine
>>> type(node)
<class 'bloqade.builder.coupling.Hyperfine'>

- Hyperfine level coupling has two reachable field nodes:

    - detuning term (See also [`Detuning`][bloqade.builder.field.Detuning])
    - rabi term (See also [`Rabi`][bloqade.builder.field.Rabi])

>>> hyp_detune = bloqade.start.hyperfine.detuning
>>> hyp_rabi = bloqade.start.hyperfine.rabi
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

__bloqade_ir__

__bloqade_ir__()

Generate the intermediate representation (IR) for the Hyperfine level coupling.

Returns:

Name Type Description
IR

An intermediate representation of the Hyperfine level coupling sequence.

Note

This method is used internally by the Bloqade framework.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/coupling.py
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def __bloqade_ir__(self):
    """
    Generate the intermediate representation (IR) for the Hyperfine level coupling.

    Args:
        None

    Returns:
        IR: An intermediate representation of the Hyperfine level coupling sequence.

    Raises:
        None

    Note:
        This method is used internally by the Bloqade framework.
    """
    from bloqade.analog.ir.control.sequence import hyperfine

    return hyperfine

LevelCoupling

LevelCoupling(parent: Optional[Builder] = None)

Bases: Builder

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

detuning property

detuning: Detuning

Specify the [Detuning][bloqade.builder.field.Detuning] [Field][bloqade.builder.field.Field] of your program. You will be able to specify the spatial modulation afterwards.

Returns:

Type Description
Detuning

[Detuning][bloqade.builder.field.Detuning]: A program node representing the detuning field.

Note

The detuning specifies how off-resonant the laser being applied to the atoms is from the atomic energy transition, driven by the Rabi frequency.

Example:

from bloqade import start
geometry = start.add_position((0,0))
coupling = geometry.rydberg
coupling.detuning

  • Next Possible Steps You may continue building your program via:
  • [uniform][bloqade.builder.field.Detuning.uniform]: To address all atoms in the field
  • [location(locations, scales)][bloqade.builder.field.Detuning.location]: To address atoms at specific locations via indices
  • [scale(coeffs)][bloqade.builder.field.Detuning.scale]: To address all atoms with an individual scale factor

rabi property

rabi: Rabi

Specify the complex-valued [Rabi][bloqade.builder.field.Rabi] field of your program.

The Rabi field is composed of a real-valued Amplitude and Phase field.

Returns:

Name Type Description
Rabi Rabi

A program node representing the Rabi field.

Note

Next possible steps to build your program are creating the RabiAmplitude field and RabiPhase field of the field: - ...rabi.amplitude: To create the Rabi amplitude field - ...rabi.phase: To create the Rabi phase field

Rydberg

Rydberg(parent: Optional[Builder] = None)

Bases: LevelCoupling

This node represents level coupling of the Rydberg state.

Examples:

- To reach the node from the start node:

>>> node = bloqade.start.rydberg
>>> type(node)
<class 'bloqade.builder.coupling.Rydberg'>

- Rydberg level coupling has two reachable field nodes:

    - detuning term (See also [`Detuning`][bloqade.builder.field.Detuning])
    - rabi term (See also [`Rabi`][bloqade.builder.field.Rabi])

>>> ryd_detune = bloqade.start.rydberg.detuning
>>> ryd_rabi = bloqade.start.rydberg.rabi
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

__bloqade_ir__

__bloqade_ir__()

Generate the intermediate representation (IR) for the Rydberg level coupling.

Returns:

Name Type Description
IR

An intermediate representation of the Rydberg level coupling sequence.

Note

This method is used internally by the Bloqade framework.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/coupling.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def __bloqade_ir__(self):
    """
    Generate the intermediate representation (IR) for the Rydberg level coupling.

    Args:
        None

    Returns:
        IR: An intermediate representation of the Rydberg level coupling sequence.

    Raises:
        None

    Note:
        This method is used internally by the Bloqade framework.
    """
    from bloqade.analog.ir.control.sequence import rydberg

    return rydberg

drive

Drive

hyperfine property

hyperfine: Hyperfine

Address the Hyperfine level coupling in your program.

  • Next possible steps to build your program are specifying the [Rabi][bloqade.builder.field.Rabi] field or [Detuning][bloqade.builder.field.Detuning] field.
    • ...hyperfine.rabi: for Rabi field
    • ...hyperfine.detuning: for Detuning field
  • In the absence of a field you the value is set to zero by default.

rydberg property

rydberg: Rydberg

Address the Rydberg level coupling in your program.

  • Next possible steps to build your program are specifying the [Rabi][bloqade.builder.field.Rabi] field or [Detuning][bloqade.builder.field.Detuning] field.
    • ...rydberg.rabi: for Rabi field
    • ...rydberg.detuning: for Detuning field
  • In the absence of a field you the value is set to zero by default.

field

Detuning

Detuning(parent: Optional[Builder] = None)

Bases: Field

This node represent detuning field of a specified level coupling (rydberg or hyperfine) type.

Examples:

- To specify detuning of rydberg coupling:

>>> node = bloqade.start.rydberg.detuning
>>> type(node)
<class 'bloqade.builder.field.Detuning'>

- To specify detuning of hyperfine coupling:

>>> node = bloqade.start.hyperfine.detuning
>>> type(node)
<class 'bloqade.builder.field.Detuning'>
Note

This node is a SpatialModulation node. See [SpatialModulation][bloqade.builder.field.SpatialModulation] for additional options.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

Field

Field(parent: Optional[Builder] = None)

Bases: Builder

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

uniform property

uniform: Uniform

Address all atoms as part of defining the spatial modulation component of a drive.

Next steps to build your program include choosing the waveform that will be summed with the spatial modulation to create a drive.

The drive by itself, or the sum of subsequent drives (created by just chaining the construction of drives) will become the field (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.).

  • You can now do:
    • ...uniform.linear(start, stop, duration) : to apply a linear waveform
    • ...uniform.constant(value, duration) : to apply a constant waveform
    • ...uniform.poly([coefficients], duration) : to apply a polynomial waveform
    • ...uniform.apply(wf:bloqade.ir.Waveform): to apply a pre-defined waveform
    • ...uniform.piecewise_linear([durations], [values]): to apply a piecewise linear waveform
    • ...uniform.piecewise_constant([durations], [values]): to apply a piecewise constant waveform
    • ...uniform.fn(f(t,...)): to apply a function as a waveform

location

location(
    labels: Union[List[int], int],
    scales: Union[
        List[ScalarType], ScalarType, None
    ] = None,
) -> Location

Address a single atom (or multiple) atoms.

Address a single atom (or multiple) as part of defining the spatial modulation component of a drive. You can specify the atoms to target as a list of labels and a list of scales. The scales are used to multiply the waveform that is applied to the atom. You can also specify a single label and scale to target a single atom.

Next steps to build your program include choosing the waveform that will be summed with the spatial modulation to create a drive.

The drive by itself, or the sum of subsequent drives (created by just chaining the construction of drives) will become the field. (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.)

Usage Example:
>>> prog = start.add_position([(0,0),(1,4),(2,8)]).rydberg.rabi
# to target a single atom with a waveform
>>> one_location_prog = prog.location(0)
# to target a single atom with a scale
>>> one_location_prog = prog.location(0, 0.5)
# to target multiple atoms with same waveform
>>> multi_location_prog = prog.location([0, 2])
# to target multiple atoms with different scales
>>> multi_location_prog = prog.location([0, 2], [0.5, "scale"])
  • You can now do:
    • ...location(labels, scales).linear(start, stop, duration) : to apply a linear waveform
    • ...location(labels, scales).constant(value, duration) : to apply a constant waveform
    • ...location(labels, scales).poly([coefficients], duration) : to apply a polynomial waveform
    • ...location(labels, scales).apply(wf:bloqade.ir.Waveform): to apply a pre-defined waveform
    • ...location(labels, scales).piecewise_linear([durations], [values]): to apply a piecewise linear waveform
    • ...location(labels, scales).piecewise_constant([durations], [values]): to apply a piecewise constant waveform
    • ...location(labels, scales).fn(f(t,..)): to apply a function as a waveform
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/field.py
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
def location(
    self,
    labels: Union[List[int], int],
    scales: Union[List[ScalarType], ScalarType, None] = None,
) -> "Location":
    """Address a single atom (or multiple) atoms.

    Address a single atom (or multiple) as part of defining the spatial
    modulation component of a drive. You can specify the atoms to target
    as a list of labels and a list of scales. The scales are used to
    multiply the waveform that is applied to the atom. You can also specify
    a single label and scale to target a single atom.

    Next steps to build your program include choosing the waveform that
    will be summed with the spatial modulation to create a drive.

    The drive by itself, or the sum of subsequent drives (created by just
    chaining the construction of drives) will become the field.
    (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.)

    ### Usage Example:
    ```
    >>> prog = start.add_position([(0,0),(1,4),(2,8)]).rydberg.rabi
    # to target a single atom with a waveform
    >>> one_location_prog = prog.location(0)
    # to target a single atom with a scale
    >>> one_location_prog = prog.location(0, 0.5)
    # to target multiple atoms with same waveform
    >>> multi_location_prog = prog.location([0, 2])
    # to target multiple atoms with different scales
    >>> multi_location_prog = prog.location([0, 2], [0.5, "scale"])
    ```

    - You can now do:
        - `...location(labels, scales).linear(start, stop, duration)` : to apply
            a linear waveform
        - `...location(labels, scales).constant(value, duration)` : to apply
            a constant waveform
        - `...location(labels, scales).poly([coefficients], duration)` : to apply
            a polynomial waveform
        - `...location(labels, scales).apply(wf:bloqade.ir.Waveform)`: to apply
            a pre-defined waveform
        - `...location(labels, scales).piecewise_linear([durations], [values])`:
            to apply
            a piecewise linear waveform
        - `...location(labels, scales).piecewise_constant([durations], [values])`:
            to apply
            a piecewise constant waveform
        - `...location(labels, scales).fn(f(t,..))`: to apply a function as a
            waveform

    """
    return self._location(labels, scales)

scale

scale(coeffs: Union[str, List[ScalarType]]) -> Scale

Address all the atoms scaling each atom with an element of the list or define a variable name for the scale list to be assigned later by defining a name and using assign or batch_assign later.

Next steps to build your program include choosing the waveform that will be summed with the spatial modulation to create a drive.

The drive by itself, or the sum of subsequent drives (created by just chaining the construction of drives) will become the field (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.)

Usage Example:
>>> prog = start.add_position([(0,0),(1,4),(2,8)]).rydberg.rabi

# assign a literal list of values to scale each atom
>>> one_location_prog = prog.scale([0.1, 0.2, 0.3])
# assign a variable name to be assigned later
>>> one_location_prog = prog.scale("a")
# "a" can be assigned in the END of the program during variable assignment
# using a list of values, indicating the scaling for each atom
>>> single_assignment = ...assign(a = [0.1, 0.2, 0.3])
# a list of lists, indicating a set of atoms should be targeted
# for each task in a batch.
>>> batch_assignment = ...batch_assign(a = [list_1, list_2, list_3,...])
  • You can now do:
    • ...scale(coeffs).linear(start, stop, duration) : to apply a linear waveform
    • ...scale(coeffs).constant(value, duration) : to apply a constant waveform
    • ...scale(coeffs).poly([coefficients], duration) : to apply a polynomial waveform
    • ...scale(coeffs).apply(wf:bloqade.ir.Waveform): to apply a pre-defined waveform
    • ...scale(coeffs).piecewise_linear(durations, values): to apply a piecewise linear waveform
    • ...scale(coeffs).piecewise_constant(durations, values): to apply a piecewise constant waveform
    • ...scale(coeffs).fn(f(t,..)): to apply a function as a waveform
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/field.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
def scale(self, coeffs: Union[str, List[ScalarType]]) -> "Scale":
    """
    Address all the atoms scaling each atom with an element of the list
    or define a variable name for the scale list to be assigned later by
    defining a `name` and using `assign` or `batch_assign` later.

    Next steps to build your program include choosing the waveform that
    will be summed with the spatial modulation to create a drive.

    The drive by itself, or the sum of subsequent drives (created by just
    chaining the construction of drives) will become the field
    (e.g. Detuning Field, Real-Valued Rabi Amplitude/Rabi Phase Field, etc.)

    ### Usage Example:
    ```
    >>> prog = start.add_position([(0,0),(1,4),(2,8)]).rydberg.rabi

    # assign a literal list of values to scale each atom
    >>> one_location_prog = prog.scale([0.1, 0.2, 0.3])
    # assign a variable name to be assigned later
    >>> one_location_prog = prog.scale("a")
    # "a" can be assigned in the END of the program during variable assignment
    # using a list of values, indicating the scaling for each atom
    >>> single_assignment = ...assign(a = [0.1, 0.2, 0.3])
    # a list of lists, indicating a set of atoms should be targeted
    # for each task in a batch.
    >>> batch_assignment = ...batch_assign(a = [list_1, list_2, list_3,...])

    ```

    - You can now do:
        - `...scale(coeffs).linear(start, stop, duration)` : to apply
            a linear waveform
        - `...scale(coeffs).constant(value, duration)` : to apply
            a constant waveform
        - `...scale(coeffs).poly([coefficients], duration)` : to apply
            a polynomial waveform
        - `...scale(coeffs).apply(wf:bloqade.ir.Waveform)`: to apply
            a pre-defined waveform
        - `...scale(coeffs).piecewise_linear(durations, values)`:  to
            apply a piecewise linear waveform
        - `...scale(coeffs).piecewise_constant(durations, values)`: to
            apply a piecewise constant waveform
        - `...scale(coeffs).fn(f(t,..))`: to apply a function as a waveform

    """
    from bloqade.analog.builder.spatial import Scale

    return Scale(coeffs, self)

Rabi

Rabi(parent: Optional[Builder] = None)

Bases: Builder

This node represent rabi field of a specified level coupling (rydberg or hyperfine) type.

Examples:

- To specify rabi of rydberg coupling:

>>> node = bloqade.start.rydberg.rabi
<class 'bloqade.builder.field.Rabi'>

- To specify rabi of hyperfine coupling:

>>> node = bloqade.start.hyperfine.rabi
>>> type(node)
<class 'bloqade.builder.field.Rabi'>
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

amplitude property

amplitude: RabiAmplitude

Specify the real-valued Rabi Amplitude field.

Next steps to build your program focus on specifying a spatial modulation.

The spatial modulation, when coupled with a waveform, completes the specification of a "Drive". One or more drives can be summed together automatically to create a field such as the Rabi Amplitude here.

  • You can now
    • ...amplitude.uniform: Address all atoms in the field
    • ...amplitude.location(...): Scale atoms by their indices
    • ...amplitude.scale(...): Scale each atom with a value from a list or assign a variable name to be assigned later

phase property

phase: RabiPhase

Specify the real-valued Rabi Phase field.

Next steps to build your program focus on specifying a spatial modulation.

The spatial modulation, when coupled with a waveform, completes the specification of a "Drive". One or more drives can be summed together automatically to create a field such as the Rabi Phase here.

  • You can now
    • ...amplitude.uniform: Address all atoms in the field
    • ...amplitude.location(...): Scale atoms by their indices
    • ...amplitude.scale(...): Scale each atom with a value from a list or assign a variable name to be assigned later

RabiAmplitude

RabiAmplitude(parent: Optional[Builder] = None)

Bases: Field

This node represent amplitude of a rabi field.

Examples:

- To specify rabi amplitude of rydberg coupling:

>>> node = bloqade.start.rydberg.rabi.amplitude
>>> type(node)
<class 'bloqade.builder.field.Amplitude'>

- To specify rabi amplitude of hyperfine coupling:

>>> node = bloqade.start.hyperfine.rabi.amplitude
>>> type(node)
<class 'bloqade.builder.field.Amplitude'>
Note

This node is a SpatialModulation node. See [SpatialModulation][bloqade.builder.field.SpatialModulation] for additional options.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

RabiPhase

RabiPhase(parent: Optional[Builder] = None)

Bases: Field

This node represent phase of a rabi field.

Examples:

- To specify rabi phase of rydberg coupling:

>>> node = bloqade.start.rydberg.rabi.phase
>>> type(node)
<class 'bloqade.builder.field.Phase'>

- To specify rabi phase of hyperfine coupling:

>>> node = bloqade.start.hyperfine.rabi.phase
>>> type(node)
<class 'bloqade.builder.field.Phase'>
Note

This node is a SpatialModulation node. See [SpatialModulation][bloqade.builder.field.SpatialModulation] for additional options.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

parse

builder

Module for parsing builder definitions into intermediate representation (IR) using the bloqade library.

This module provides a Parser class for parsing various components of a quantum computing program, including atom arrangements, pulse sequences, analog circuits, and routines. It also defines utility functions for reading addresses, waveforms, drives, sequences, registers, and pragmas from a builder stream.

Parser

A class for parsing quantum computing program components into intermediate representation (IR).

parse
parse(builder: Builder) -> Routine

Parse a routine from the builder.

Parameters:

Name Type Description Default
builder Builder

The builder instance.

required

Returns:

Name Type Description
Routine Routine

The parsed routine.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/builder.py
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
def parse(self, builder: Builder) -> "Routine":
    """
    Parse a routine from the builder.

    Args:
        builder (Builder): The builder instance.

    Returns:
        Routine: The parsed routine.
    """
    from bloqade.analog.ir.routine.base import Routine
    from bloqade.analog.ir.analog_circuit import AnalogCircuit
    from bloqade.analog.ir.routine.params import Params, ScalarArg, VectorArg
    from bloqade.analog.compiler.analysis.common.scan_variables import ScanVariables

    self.reset(builder)
    self.read_register()
    self.read_sequence()
    self.read_pragmas()

    circuit = AnalogCircuit(self.register, self.sequence)

    var_res = ScanVariables().scan(circuit)
    # mark vector and scalar arguments
    args_list = [
        (VectorArg(name) if name in var_res.vector_vars else ScalarArg(name))
        for name in self.order
    ]

    params = Params(
        n_sites=self.register.n_sites,
        static_params=self.static_params,
        batch_params=self.batch_params,
        args_list=args_list,
    )

    return Routine(builder, circuit, params)
parse_circuit
parse_circuit(builder: Builder) -> AnalogCircuit

Parse an analog circuit from the builder.

Parameters:

Name Type Description Default
builder Builder

The builder instance.

required

Returns:

Name Type Description
AnalogCircuit AnalogCircuit

The parsed analog circuit.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/builder.py
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
def parse_circuit(self, builder: Builder) -> "AnalogCircuit":
    """
    Parse an analog circuit from the builder.

    Args:
        builder (Builder): The builder instance.

    Returns:
        AnalogCircuit: The parsed analog circuit.
    """
    from bloqade.analog.ir.analog_circuit import AnalogCircuit

    self.reset(builder)
    self.read_register()
    self.read_sequence()

    circuit = AnalogCircuit(self.register, self.sequence)

    return circuit
parse_register
parse_register(
    builder: Builder,
) -> Union[ir.AtomArrangement, ir.ParallelRegister]

Parse an atom arrangement register from the builder.

Parameters:

Name Type Description Default
builder Builder

The builder instance.

required

Returns:

Type Description
Union[AtomArrangement, ParallelRegister]

Union[ir.AtomArrangement, ir.ParallelRegister]: The parsed atom arrangement or parallel register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/builder.py
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
def parse_register(
    self, builder: Builder
) -> Union[ir.AtomArrangement, ir.ParallelRegister]:
    """
    Parse an atom arrangement register from the builder.

    Args:
        builder (Builder): The builder instance.

    Returns:
        Union[ir.AtomArrangement, ir.ParallelRegister]: The parsed atom arrangement or parallel register.
    """
    self.reset(builder)
    self.read_register()
    self.read_pragmas()
    return self.register
parse_sequence
parse_sequence(builder: Builder) -> ir.Sequence

Parse a sequence from the builder.

Parameters:

Name Type Description Default
builder Builder

The builder instance.

required

Returns:

Type Description
Sequence

ir.Sequence: The parsed sequence.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/builder.py
268
269
270
271
272
273
274
275
276
277
278
279
280
def parse_sequence(self, builder: Builder) -> ir.Sequence:
    """
    Parse a sequence from the builder.

    Args:
        builder (Builder): The builder instance.

    Returns:
        ir.Sequence: The parsed sequence.
    """
    self.reset(builder)
    self.read_sequence()
    return self.sequence
read_address
read_address(
    stream,
) -> Tuple[LevelCoupling, Field, BuilderNode]

Read an address from the builder stream.

Parameters:

Name Type Description Default
stream

The builder stream.

required

Returns:

Type Description
Tuple[LevelCoupling, Field, BuilderNode]

Tuple[LevelCoupling, Field, BuilderNode]: A tuple containing the level coupling, field, and spatial modulation.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/builder.py
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
def read_address(self, stream) -> Tuple[LevelCoupling, Field, BuilderNode]:
    """
    Read an address from the builder stream.

    Args:
        stream: The builder stream.

    Returns:
        Tuple[LevelCoupling, Field, BuilderNode]: A tuple containing the level coupling, field, and spatial modulation.
    """
    spatial = stream.read_next([Location, Uniform, Scale])
    curr = spatial

    if curr is None:
        return (None, None, None)

    while curr.next is not None:
        if not isinstance(curr.node, SpatialModulation):
            break
        curr = curr.next

    if type(spatial.node.__parent__) in [Detuning, RabiAmplitude, RabiPhase]:
        field = spatial.node.__parent__  # field is updated
        if type(field) in [RabiAmplitude, RabiPhase]:
            coupling = field.__parent__.__parent__  # skip Rabi
        else:
            coupling = field.__parent__

        # coupling not updated
        if type(coupling) not in [Rydberg, Hyperfine]:
            coupling = None
        return (coupling, field, spatial)
    else:  # only spatial is updated
        return (None, None, spatial)
read_drive
read_drive(head) -> ir.Field

Read a drive from the builder stream.

Parameters:

Name Type Description Default
head

The head of the builder stream.

required

Returns:

Type Description
Field

ir.Field: The drive field.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/builder.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
def read_drive(self, head) -> ir.Field:
    """
    Read a drive from the builder stream.

    Args:
        head: The head of the builder stream.

    Returns:
        ir.Field: The drive field.
    """
    if head is None:
        return ir.Field({})

    sm = head.node.__bloqade_ir__()
    wf, _ = self.read_waveform(head.next)

    return ir.Field({sm: wf})
read_pragmas
read_pragmas() -> None

Read pragmas from the builder stream.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/builder.py
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
def read_pragmas(self) -> None:
    """Read pragmas from the builder stream."""
    pragma_types = (
        Assign,
        BatchAssign,
        ListAssign,
        Args,
        Parallelize,
    )

    stream = self.stream.copy()
    curr = stream.read_next(pragma_types)

    while curr is not None:
        node = curr.node

        if isinstance(node, Assign):
            self.static_params = dict(node._static_params)
        elif isinstance(node, BatchAssign) or isinstance(node, ListAssign):
            self.batch_params = node._batch_params
        elif isinstance(node, Args):
            order = node._order

            seen = set()
            dup = []
            for x in order:
                if x not in seen:
                    seen.add(x)
                else:
                    dup.append(x)

            if dup:
                raise ValueError(f"Cannot have duplicate names {dup}.")

            self.order = order

        elif isinstance(node, Parallelize):
            self.register = ir.ParallelRegister(
                self.register, node._cluster_spacing
            )
        else:
            break

        curr = curr.next
read_register
read_register() -> ir.AtomArrangement

Read an atom arrangement register from the builder stream.

Returns:

Type Description
AtomArrangement

ir.AtomArrangement: The parsed atom arrangement.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/builder.py
193
194
195
196
197
198
199
200
201
202
203
204
def read_register(self) -> ir.AtomArrangement:
    """
    Read an atom arrangement register from the builder stream.

    Returns:
        ir.AtomArrangement: The parsed atom arrangement.
    """
    # register is always head of the stream
    register_node = self.stream.read()
    self.register = register_node.node

    return self.register
read_sequence
read_sequence() -> ir.Sequence

Read a sequence from the builder stream.

Returns:

Type Description
Sequence

ir.Sequence: The parsed sequence.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/builder.py
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
def read_sequence(self) -> ir.Sequence:
    """
    Read a sequence from the builder stream.

    Returns:
        ir.Sequence: The parsed sequence.
    """
    if isinstance(self.stream.curr.node, SequenceBuilder):
        # case with sequence builder object.
        self.sequence = self.stream.read().node._sequence
        return self.sequence

    stream = self.stream.copy()
    while stream.curr is not None:
        coupling_builder, field_builder, spatial_head = self.read_address(stream)

        if coupling_builder is not None:
            # update to new pulse coupling
            self.coupling_name = coupling_builder.__bloqade_ir__()

        if field_builder is not None:
            # update to new field coupling
            self.field_name = field_builder.__bloqade_ir__()

        if spatial_head is None:
            break

        pulse = self.sequence.pulses.get(self.coupling_name, ir.Pulse({}))
        field = pulse.fields.get(self.field_name, ir.Field({}))

        drive = self.read_drive(spatial_head)
        field = field.add(drive)

        pulse = ir.Pulse.create(pulse.fields | {self.field_name: field})
        self.sequence = ir.Sequence.create(
            self.sequence.pulses | {self.coupling_name: pulse}
        )

    return self.sequence
read_waveform
read_waveform(
    head: BuilderNode,
) -> Tuple[ir.Waveform, BuilderNode]

Read a waveform from the builder stream.

Parameters:

Name Type Description Default
head BuilderNode

The head of the builder stream.

required

Returns:

Type Description
Tuple[Waveform, BuilderNode]

Tuple[ir.Waveform, BuilderNode]: A tuple containing the waveform and the next builder node.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/builder.py
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def read_waveform(self, head: BuilderNode) -> Tuple[ir.Waveform, BuilderNode]:
    """
    Read a waveform from the builder stream.

    Args:
        head (BuilderNode): The head of the builder stream.

    Returns:
        Tuple[ir.Waveform, BuilderNode]: A tuple containing the waveform and the next builder node.
    """
    curr = head
    waveform = None
    while curr is not None:
        node = curr.node

        if isinstance(node, Slice):
            waveform = waveform[node._start : node._stop]
        elif isinstance(node, Record):
            waveform = waveform.record(node._name)
        elif isinstance(node, Sample):
            interpolation = node._interpolation
            if interpolation is None:
                if self.field_name == ir.rabi.phase:
                    interpolation = ir.Interpolation.Constant
                else:
                    interpolation = ir.Interpolation.Linear
            fn_waveform = node.__parent__.__bloqade_ir__()
            sample_waveform = ir.Sample(fn_waveform, interpolation, node._dt)
            if waveform is None:
                waveform = sample_waveform
            else:
                waveform = waveform.append(sample_waveform)
        elif (
            isinstance(node, Fn)
            and curr.next is not None
            and isinstance(curr.next.node, Sample)
        ):
            pass
        elif isinstance(node, WaveformPrimitive):
            if waveform is None:
                waveform = node.__bloqade_ir__()
            else:
                waveform = waveform.append(node.__bloqade_ir__())
        else:
            break

        curr = curr.next

    return waveform, curr
reset
reset(builder: Builder)

Reset the parser's state.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/builder.py
40
41
42
43
44
45
46
47
48
def reset(self, builder: Builder):
    """Reset the parser's state."""
    self.stream = BuilderStream.create(builder)
    self.vector_node_names = set()
    self.sequence = ir.Sequence.create()
    self.register = None
    self.batch_params = [{}]
    self.static_params = {}
    self.order = ()

stream

Module for managing a stream of builder nodes.

This module provides classes to represent builder nodes and builder streams. A builder node is a single element in the stream, representing a step in a construction process. A builder stream is a sequence of builder nodes, allowing traversal and manipulation of the construction steps.

BuilderNode dataclass

BuilderNode(
    node: Builder, next: Optional[BuilderNode] = None
)

A node in the builder stream.

__repr__
__repr__() -> str

Representation of the BuilderNode.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/stream.py
22
23
24
def __repr__(self) -> str:
    """Representation of the BuilderNode."""
    return repr(self.node)

BuilderStream dataclass

BuilderStream(
    head: BuilderNode, curr: Optional[BuilderNode] = None
)

Represents a stream of builder nodes.

__iter__
__iter__()

Iterator method to iterate over the builder stream.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/stream.py
87
88
89
def __iter__(self):
    """Iterator method to iterate over the builder stream."""
    return self
__next__
__next__()

Next method to get the next item in the builder stream.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/stream.py
91
92
93
94
95
96
def __next__(self):
    """Next method to get the next item in the builder stream."""
    node = self.read()
    if node is None:
        raise StopIteration
    return node
build_nodes staticmethod
build_nodes(node: Builder) -> BuilderNode

Build BuilderNode instances from the provided Builder.

Parameters:

Name Type Description Default
node Builder

The root Builder instance.

required

Returns:

Name Type Description
BuilderNode BuilderNode

The head of the linked list of BuilderNodes.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/stream.py
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
@staticmethod
def build_nodes(node: Builder) -> "BuilderNode":
    """
    Build BuilderNode instances from the provided Builder.

    Args:
        node (Builder): The root Builder instance.

    Returns:
        BuilderNode: The head of the linked list of BuilderNodes.
    """
    curr = node
    node = None
    while curr is not None:
        next = curr
        curr = curr.__parent__ if hasattr(curr, "__parent__") else None
        node = BuilderNode(next, node)

    return node
copy
copy() -> BuilderStream

Create a copy of the builder stream.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/stream.py
34
35
36
def copy(self) -> "BuilderStream":
    """Create a copy of the builder stream."""
    return BuilderStream(head=self.head, curr=self.curr)
create staticmethod
create(builder: Builder) -> BuilderStream

Create a BuilderStream instance from a Builder.

Parameters:

Name Type Description Default
builder Builder

The root Builder instance.

required

Returns:

Name Type Description
BuilderStream BuilderStream

The created BuilderStream instance.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/stream.py
118
119
120
121
122
123
124
125
126
127
128
129
130
@staticmethod
def create(builder: Builder) -> "BuilderStream":
    """
    Create a BuilderStream instance from a Builder.

    Args:
        builder (Builder): The root Builder instance.

    Returns:
        BuilderStream: The created BuilderStream instance.
    """
    head = BuilderStream.build_nodes(builder)
    return BuilderStream(head=head, curr=head)
eat
eat(
    types: List[Type[Builder]],
    skips: Optional[List[Type[Builder]]] = None,
) -> BuilderNode

Move the stream pointer until a node of specified types is found.

Parameters:

Name Type Description Default
types List[Type[Builder]]

List of types to move the stream pointer to.

required
skips List[Type[Builder]] | None

List of types to end the stream scan.

None

Returns:

Name Type Description
BuilderNode BuilderNode

The beginning of the stream which matches a type in types.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/stream.py
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
def eat(
    self, types: List[Type[Builder]], skips: Optional[List[Type[Builder]]] = None
) -> BuilderNode:
    """
    Move the stream pointer until a node of specified types is found.

    Args:
        types (List[Type[Builder]]): List of types to move the stream pointer to.
        skips (List[Type[Builder]] | None, optional): List of types to end the stream scan.

    Returns:
        BuilderNode: The beginning of the stream which matches a type in `types`.
    """
    head = self.read_next(types)
    curr = head
    while curr is not None:
        if type(curr.node) not in types:
            if skips and type(curr.node) not in skips:
                break
        curr = curr.next
    self.curr = curr
    return head
read
read() -> Optional[BuilderNode]

Read the next builder node from the stream.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/stream.py
38
39
40
41
42
43
44
45
def read(self) -> Optional[BuilderNode]:
    """Read the next builder node from the stream."""
    if self.curr is None:
        return None

    node = self.curr
    self.curr = self.curr.next
    return node
read_next
read_next(
    builder_types: List[type[Builder]],
) -> Optional[BuilderNode]

Read the next builder node of specified types from the stream.

Parameters:

Name Type Description Default
builder_types List[type[Builder]]

List of builder types to read from the stream.

required

Returns:

Type Description
Optional[BuilderNode]

Optional[BuilderNode]: The next builder node matching one of the specified types, or None if not found.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/stream.py
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
def read_next(self, builder_types: List[type[Builder]]) -> Optional[BuilderNode]:
    """
    Read the next builder node of specified types from the stream.

    Args:
        builder_types (List[type[Builder]]): List of builder types to read from the stream.

    Returns:
        Optional[BuilderNode]: The next builder node matching one of the specified types, or None if not found.
    """
    node = self.read()
    while node is not None:
        if type(node.node) in builder_types:
            return node
        node = self.read()
    return None

trait

Module for parsing and displaying quantum computing program components using the bloqade library.

Parse

Bases: ParseRegister, ParseSequence, ParseCircuit, ParseRoutine

A composite class inheriting from ParseRegister, ParseSequence, ParseCircuit, and ParseRoutine. Provides a unified interface for parsing different components of the program.

n_atoms property
n_atoms: int

Return the number of atoms in the program.

Returns:

Name Type Description
int int

The number of atoms in the parsed register.

Raises:

Type Description
ValueError

If the register type is unsupported.

Note

If the register is of type ParallelRegister, the number of atoms is extracted from its internal register.

Example:

>>> class MyBuilder(Parse):
...     pass
>>> builder = MyBuilder()
>>> n_atoms = builder.n_atoms

ParseCircuit

A class providing functionality to parse the analog circuit from the program.

Example:

>>> class MyBuilder(ParseCircuit):
...     pass
>>> builder = MyBuilder()
>>> analog_circuit = builder.parse_circuit()
parse_circuit
parse_circuit() -> AnalogCircuit

Parse the analog circuit from the program.

Returns:

Name Type Description
AnalogCircuit AnalogCircuit

The parsed analog circuit.

Raises:

Type Description
ValueError

If the circuit cannot be parsed.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/trait.py
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
def parse_circuit(self: "Builder") -> "AnalogCircuit":
    """
    Parse the analog circuit from the program.

    Returns:
        AnalogCircuit: The parsed analog circuit.

    Raises:
        ValueError: If the circuit cannot be parsed.
    """
    from bloqade.analog.builder.parse.builder import Parser

    return Parser().parse_circuit(self)

ParseRegister

A class providing functionality to parse the arrangement of atoms in the program.

Example:

>>> class MyBuilder(ParseRegister):
...     pass
>>> builder = MyBuilder()
>>> atom_arrangement = builder.parse_register()
parse_register
parse_register() -> (
    Union[AtomArrangement, ParallelRegister]
)

Parse the arrangement of atoms in the program.

Returns:

Type Description
Union[AtomArrangement, ParallelRegister]

Union[AtomArrangement, ParallelRegister]: The parsed atom arrangement or parallel register.

Raises:

Type Description
ValueError

If the register cannot be parsed.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/trait.py
31
32
33
34
35
36
37
38
39
40
41
42
43
def parse_register(self: "Builder") -> Union["AtomArrangement", "ParallelRegister"]:
    """
    Parse the arrangement of atoms in the program.

    Returns:
        Union[AtomArrangement, ParallelRegister]: The parsed atom arrangement or parallel register.

    Raises:
        ValueError: If the register cannot be parsed.
    """
    from bloqade.analog.builder.parse.builder import Parser

    return Parser().parse_register(self)

ParseRoutine

A class providing functionality to parse the program and return a Routine object.

Example:

>>> class MyBuilder(ParseRoutine):
...     pass
>>> builder = MyBuilder()
>>> routine = builder.parse()
parse
parse() -> Routine

Parse the program to return a Routine object.

Returns:

Name Type Description
Routine Routine

The parsed routine object.

Raises:

Type Description
ValueError

If the routine cannot be parsed.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/trait.py
118
119
120
121
122
123
124
125
126
127
128
129
130
def parse(self: "Builder") -> "Routine":
    """
    Parse the program to return a Routine object.

    Returns:
        Routine: The parsed routine object.

    Raises:
        ValueError: If the routine cannot be parsed.
    """
    from bloqade.analog.builder.parse.builder import Parser

    return Parser().parse(self)

ParseSequence

A class providing functionality to parse the pulse sequence part of the program.

Example:

>>> class MyBuilder(ParseSequence):
...     pass
>>> builder = MyBuilder()
>>> sequence = builder.parse_sequence()
parse_sequence
parse_sequence() -> Sequence

Parse the pulse sequence part of the program.

Returns:

Name Type Description
Sequence Sequence

The parsed pulse sequence.

Raises:

Type Description
ValueError

If the sequence cannot be parsed.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/trait.py
60
61
62
63
64
65
66
67
68
69
70
71
72
def parse_sequence(self: "Builder") -> "Sequence":
    """
    Parse the pulse sequence part of the program.

    Returns:
        Sequence: The parsed pulse sequence.

    Raises:
        ValueError: If the sequence cannot be parsed.
    """
    from bloqade.analog.builder.parse.builder import Parser

    return Parser().parse_sequence(self)

Show

A mixin class providing functionality to display the builder with given arguments and batch ID.

show
show(*args, batch_id: int = 0)

Display the current program being defined with the given arguments and batch ID.

Parameters:

Name Type Description Default
*args

Additional arguments for display.

()
batch_id int

The batch ID to be displayed. Defaults to 0.

0
Note

This method uses the display_builder function to render the builder's state.

Example:

>>> class MyBuilder(Show):
...     pass
>>> builder = MyBuilder()
>>> builder.show()
>>> builder.show(batch_id=1)
>>> builder.show('arg1', 'arg2', batch_id=2)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/parse/trait.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
def show(self, *args, batch_id: int = 0):
    """
    Display the current program being defined with the given arguments and batch ID.

    Args:
        *args: Additional arguments for display.
        batch_id (int, optional): The batch ID to be displayed. Defaults to 0.

    Note:
        This method uses the `display_builder` function to render the builder's state.

    Example:

    ```python
    >>> class MyBuilder(Show):
    ...     pass
    >>> builder = MyBuilder()
    >>> builder.show()
    >>> builder.show(batch_id=1)
    >>> builder.show('arg1', 'arg2', batch_id=2)
    ```
    """
    display_builder(self, batch_id, *args)

pragmas

This module provides classes for building and managing quantum programs using the Bloqade library.

AddArgs

args

args(args_list: List[Union[str, Variable]]) -> Args

Add arguments to the current program.

Parameters:

Name Type Description Default
args_list List[Union[str, Variable]]

List of argument names or Variable objects to be added.

required

Returns:

Name Type Description
Args Args

A new instance of the Args class with the specified arguments.

Raises:

Type Description
TypeError

If args_list contains invalid types.

Note

This method is useful for deferring the value assignment of certain variables to runtime.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/pragmas.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
def args(self, args_list: List[Union[str, Variable]]) -> "Args":
    """
    Add arguments to the current program.

    Args:
        args_list (List[Union[str, Variable]]): List of argument names or Variable
            objects to be added.

    Returns:
        Args: A new instance of the Args class with the specified arguments.

    Raises:
        TypeError: If args_list contains invalid types.

    Note:
        This method is useful for deferring the value assignment of certain
        variables to runtime.
    """
    from bloqade.analog.builder.args import Args

    return Args(args_list, self)

Assignable

assign

assign(**assignments) -> Assign

Assign values to variables declared previously in the program.

Parameters:

Name Type Description Default
**assignments

Key-value pairs where the key is the variable name and the value is the value to assign.

{}

Returns:

Name Type Description
Assign Assign

A new instance of the Assign class with the specified assignments.

Raises:

Type Description
ValueError

If an invalid assignment is provided.

Note

This is reserved for variables that should take single values or for spatial modulations created with .scale(str). After assigning values, you can choose a backend for emulation or execution.

Usage Examples:
# define geometry
>>> reg = bloqade.start
...       .add_position([(0,0),(1,1),(2,2),(3,3)])
# define variables in program
>>> seq = reg.rydberg.detuning.uniform
...       .linear(start="ival", stop=1, duration="span_time")
# assign values to variables
>>> seq = seq.assign(span_time=0.5, ival=0.0)
  • Next steps:
    • ...assign(assignments).bloqade: select the bloqade local emulator backend
    • ...assign(assignments).braket: select braket local emulator or QuEra hardware
    • ...assign(assignments).device(specifier_string): select backend by specifying a string
    • ...assign(assignments).batch_assign(assignments): assign multiple values for a parameter sweep
    • ...assign(assignments).parallelize(cluster_spacing): parallelize the program register
    • ...assign(assignments).args([previously_defined_vars]): defer value assignment to runtime
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/pragmas.py
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def assign(self, **assignments) -> "Assign":
    """
    Assign values to variables declared previously in the program.

    Args:
        **assignments: Key-value pairs where the key is the variable name and
            the value is the value to assign.

    Returns:
        Assign: A new instance of the Assign class with the specified
            assignments.

    Raises:
        ValueError: If an invalid assignment is provided.

    Note:
        This is reserved for variables that should take single values or for
        spatial modulations created with `.scale(str)`. After assigning values,
        you can choose a backend for emulation or execution.

    ### Usage Examples:
    ```
    # define geometry
    >>> reg = bloqade.start
    ...       .add_position([(0,0),(1,1),(2,2),(3,3)])
    # define variables in program
    >>> seq = reg.rydberg.detuning.uniform
    ...       .linear(start="ival", stop=1, duration="span_time")
    # assign values to variables
    >>> seq = seq.assign(span_time=0.5, ival=0.0)
    ```

    - Next steps:
        - `...assign(assignments).bloqade`: select the bloqade local emulator backend
        - `...assign(assignments).braket`: select braket local emulator or QuEra hardware
        - `...assign(assignments).device(specifier_string)`: select backend by specifying a
        string
        - `...assign(assignments).batch_assign(assignments)`: assign multiple values for a
        parameter sweep
        - `...assign(assignments).parallelize(cluster_spacing)`: parallelize the program
        register
        - `...assign(assignments).args([previously_defined_vars])`: defer value assignment to
        runtime
    """
    from bloqade.analog.builder.assign import Assign

    return Assign(assignments, parent=self)

BatchAssignable

batch_assign

batch_assign(
    __batch_params: List[Dict[str, ParamType]] = [],
    **assignments: List[ParamType]
) -> Union[BatchAssign, ListAssign]

Assign multiple values to variables for creating a parameter sweep.

Parameters:

Name Type Description Default
__batch_params List[Dict[str, ParamType]]

List of dictionaries where each dictionary contains variable assignments for one set of parameters.

[]
**assignments List[ParamType]

Key-value pairs where the key is the variable name and the value is a list of values to assign.

{}

Returns:

Type Description
Union[BatchAssign, ListAssign]

Union[BatchAssign, ListAssign]: A new instance of BatchAssign or ListAssign class with the specified assignments.

Raises:

Type Description
ValueError

If both __batch_params and assignments are provided.

Note

Bloqade handles the multiple programs generated by this method and treats them as a unified object for easy post-processing. Ensure all lists of values are of the same length as Bloqade will not perform a Cartesian product.

Usage Example:
>>> reg = start.add_position([(0,0), (0, "atom_distance")])
>>> prog = reg.rydberg.rabi.amplitude.uniform.constant("value", 5.0)
>>> var_assigned_prog = prog.batch_assign(value=[1.0, 2.0, 3.0],
atom_distance=[1.0, 2.0, 3.0])
  • Next steps:
    • ...batch_assign(assignments).bloqade: select the bloqade local emulator backend
    • ...batch_assign(assignments).braket: select braket local emulator or QuEra hardware
    • ...batch_assign(assignments).device(specifier_string): select backend by specifying a string
    • ...batch_assign(assignments).parallelize(cluster_spacing): parallelize the program register
    • ...batch_assign(assignments).args([previously_defined_vars]): defer value assignment to runtime
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/pragmas.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
def batch_assign(
    self,
    __batch_params: List[Dict[str, ParamType]] = [],
    **assignments: List[ParamType],
) -> Union["BatchAssign", "ListAssign"]:
    """
    Assign multiple values to variables for creating a parameter sweep.

    Args:
        __batch_params (List[Dict[str, ParamType]], optional): List of dictionaries
            where each dictionary contains variable assignments for one set of parameters.
        **assignments (List[ParamType]): Key-value pairs where the key is the variable
            name and the value is a list of values to assign.

    Returns:
        Union[BatchAssign, ListAssign]: A new instance of BatchAssign or ListAssign
            class with the specified assignments.

    Raises:
        ValueError: If both __batch_params and assignments are provided.

    Note:
        Bloqade handles the multiple programs generated by this method and treats them
        as a unified object for easy post-processing. Ensure all lists of values are of
        the same length as Bloqade will not perform a Cartesian product.

    ### Usage Example:
    ```
    >>> reg = start.add_position([(0,0), (0, "atom_distance")])
    >>> prog = reg.rydberg.rabi.amplitude.uniform.constant("value", 5.0)
    >>> var_assigned_prog = prog.batch_assign(value=[1.0, 2.0, 3.0],
    atom_distance=[1.0, 2.0, 3.0])
    ```

    - Next steps:
        - `...batch_assign(assignments).bloqade`: select the bloqade local emulator backend
        - `...batch_assign(assignments).braket`: select braket local emulator or QuEra hardware
        - `...batch_assign(assignments).device(specifier_string)`: select backend by specifying
        a string
        - `...batch_assign(assignments).parallelize(cluster_spacing)`: parallelize the program
        register
        - `...batch_assign(assignments).args([previously_defined_vars])`: defer value assignment
        to runtime
    """
    from bloqade.analog.builder.assign import ListAssign, BatchAssign

    if len(__batch_params) > 0 and assignments:
        raise ValueError("batch_params and assignments cannot be used together.")

    if len(__batch_params) > 0:
        return ListAssign(__batch_params, parent=self)
    else:
        return BatchAssign(assignments, parent=self)

Parallelizable

parallelize

parallelize(cluster_spacing: LiteralType) -> Parallelize

Parallelize the current problem by duplicating the geometry to utilize all available space/qubits on hardware.

Parameters:

Name Type Description Default
cluster_spacing LiteralType

Specifies the spacing between clusters in micrometers.

required

Returns:

Name Type Description
Parallelize Parallelize

A new instance of the Parallelize class with the specified cluster spacing.

Raises:

Type Description
ValueError

If the cluster_spacing is not a valid value.

Note

After calling this method, you can choose a backend for emulation or execution. Options include bloqade for a local emulator, braket for a local emulator or QuEra hardware on the cloud, or specifying a device with a string.

Usage Example:
>>> reg = start.add_position((0,0)).rydberg.rabi.uniform.amplitude.constant(1.0, 1.0)
# copy-paste the geometry and waveforms
>>> parallelized_prog = reg.parallelize(24)
  • Next steps:
    • ...parallelize(cluster_spacing).bloqade: select the bloqade local emulator backend
    • ...parallelize(cluster_spacing).braket: select braket local emulator or QuEra hardware on the cloud
    • ...parallelize(cluster_spacing).device(specifier_string): select backend by specifying a string
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/pragmas.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def parallelize(self, cluster_spacing: LiteralType) -> "Parallelize":
    """
    Parallelize the current problem by duplicating the geometry to utilize
    all available space/qubits on hardware.

    Args:
        cluster_spacing (LiteralType): Specifies the spacing between clusters
            in micrometers.

    Returns:
        Parallelize: A new instance of the Parallelize class with the specified
            cluster spacing.

    Raises:
        ValueError: If the cluster_spacing is not a valid value.

    Note:
        After calling this method, you can choose a backend for emulation or
        execution. Options include `bloqade` for a local emulator, `braket` for
        a local emulator or QuEra hardware on the cloud, or specifying a device
        with a string.

    ### Usage Example:
    ```
    >>> reg = start.add_position((0,0)).rydberg.rabi.uniform.amplitude.constant(1.0, 1.0)
    # copy-paste the geometry and waveforms
    >>> parallelized_prog = reg.parallelize(24)
    ```

    - Next steps:
        - `...parallelize(cluster_spacing).bloqade`: select the bloqade local emulator backend
        - `...parallelize(cluster_spacing).braket`: select braket local emulator or QuEra
        hardware on the cloud
        - `...parallelize(cluster_spacing).device(specifier_string)`: select backend by
        specifying a string
    """
    from bloqade.analog.builder.parallelize import Parallelize

    return Parallelize(cluster_spacing, self)

spatial

Uniform

Uniform(parent: Optional[Builder] = None)

Bases: SpatialModulation

The node specify a uniform spacial modulation. Which is ready to apply waveform (See [Waveform][bloqade.builder.waveform] for available waveform options)

Examples:

- To hit this node from the start node:

>>> reg = bloqade.start.add_position([(0,0),(1,1),(2,2),(3,3)])
>>> loc = reg.rydberg.detuning.uniform

- Apply Linear waveform:

>>> wv = bloqade.ir.Linear(start=0,stop=1,duration=0.5)
>>> reg = bloqade.start.add_position([(0,0),(1,1),(2,2),(3,3)])
>>> loc = reg.rydberg.detuning.uniform.apply(wv)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

start

ProgramStart

ProgramStart(parent: Optional[Builder] = None)

Bases: Drive, Builder

ProgramStart is the base class for a starting/entry node for building a program.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

apply

apply(sequence: SequenceExpr) -> SequenceBuilder

Apply a pre-built sequence to a program.

This allows you to build a program independent of any geometry and then apply the program to said geometry. Or, if you have a program you would like to try on multiple geometries you can trivially do so with this.

Example Usage:

>>> from numpy import pi
>>> seq = start.rydberg.rabi.amplitude.constant(2.0 * pi, 4.5)
# choose a geometry of interest to apply the program on
>>> from bloqade.analog.atom_arrangement import Chain, Kagome
>>> complete_program = Chain(10).apply(seq)
# you can .apply to as many geometries as you like
>>> another_complete_program = Kagome(3).apply(seq)

  • From here you can now do:
    • ...assign(assignments).bloqade: select the bloqade local emulator backend
    • ...assign(assignments).braket: select braket local emulator or QuEra hardware
    • ...assign(assignments).device(specifier_string): select backend by specifying a string
  • Assign multiple values to a single variable for a parameter sweep:
    • ...assign(assignments).batch_assign(assignments):
  • Parallelize the program register, duplicating the geometry and waveform sequence to take advantage of all available space/qubits on the QPU:
    • ...assign(assignments).parallelize(cluster_spacing)
  • Defer value assignment of certain variables to runtime:
    • ...assign(assignments).args([previously_defined_vars])
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/start.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
@beartype
def apply(self, sequence: SequenceExpr) -> SequenceBuilder:
    """
    Apply a pre-built sequence to a program.

    This allows you to build a program independent of any geometry
    and then `apply` the program to said geometry. Or, if you have a
    program you would like to try on multiple geometries you can
    trivially do so with this.

    Example Usage:
    ```
    >>> from numpy import pi
    >>> seq = start.rydberg.rabi.amplitude.constant(2.0 * pi, 4.5)
    # choose a geometry of interest to apply the program on
    >>> from bloqade.analog.atom_arrangement import Chain, Kagome
    >>> complete_program = Chain(10).apply(seq)
    # you can .apply to as many geometries as you like
    >>> another_complete_program = Kagome(3).apply(seq)
    ```

    - From here you can now do:
        - `...assign(assignments).bloqade`: select the bloqade
            local emulator backend
        - `...assign(assignments).braket`: select braket
            local emulator or QuEra hardware
        - `...assign(assignments).device(specifier_string)`: select
            backend by specifying a string
    - Assign multiple values to a single variable for a parameter sweep:
        - `...assign(assignments).batch_assign(assignments)`:
    - Parallelize the program register, duplicating the geometry and waveform
        sequence to take advantage of all available
      space/qubits on the QPU:
        - `...assign(assignments).parallelize(cluster_spacing)`
    - Defer value assignment of certain variables to runtime:
        - `...assign(assignments).args([previously_defined_vars])`

    """
    return SequenceBuilder(sequence, self)

waveform

Recordable

record

record(name: str) -> Record

Copy or "record" the value at the end of the waveform into a variable so that it can be used in another place.

A common design pattern is to couple this with .slice() considering you may not know exactly what the end value of a .slice() is, especially in parameter sweeps where it becomes cumbersome to handle.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a "drive", one or a sum of drives creating a "field" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

Usage Example:
# define program of interest
>>> from bloqade import start
>>> prog = start.rydberg.rabi.amplitude.uniform
>>> prog_with_wf = prog.piecewise_linear(durations=[0.3, 2.0, 0.3],
values=[0.0, 2.0, 2.0, 0.0])
# We now slice the piecewise_linear from above and record the
# value at the end of that slice. We then use that value
# to construct a new waveform that can be appended to the previous
# one without introducing discontinuity (refer to the
# "Quantum Scar Dynamics" tutorial for how this could be handy)
>>> prog_with_record = prog_with_wf.slice(0.0, 1.0).record("end_of_wf")
>>> record_applied_prog = prog_with_record.linear(start="end_of_wf"
, stop=0.0, duration=0.3)
  • Your next steps include:
  • Continue building your waveform via:
    • ...slice(start, stop).linear(start, stop, duration): to append another linear waveform
    • ...slice(start, stop).constant(value, duration): to append a constant waveform
    • ...slice(start, stop).piecewise_linear(): to append a piecewise linear waveform
    • ...slice(start, stop).piecewise_constant(): to append a piecewise constant waveform
    • ...slice(start, stop).poly([coefficients], duration): to append a polynomial waveform
    • ...slice(start, stop).apply(wf:bloqade.ir.Waveform): to append a pre-defined waveform
    • ...slilce(start, stop).fn(f(t,...)): to append a waveform defined by a python function
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...slice(start, stop).uniform: To address all atoms in the field
    • ...slice(start, stop).location(int): To address an atom at a specific location via index
    • ...slice(start, stop).scale(str)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...slice(start, stop).assign(variable_name = value): to assign a single value to a variable
    • ...slice(start, stop) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...slice(start, stop).args(["previously_defined_var"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...slice(start, stop).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...slice(start, stop).bloqade: to run on the Bloqade local emulator
    • ...slice(start, stop).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...slice(start, stop).parallelize(spacing)
  • Start targeting another level coupling
    • ...slice(start, stop).rydberg: to target the Rydberg level coupling
    • ...slice(start, stop).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...slice(start, stop).amplitude: to target the real-valued Rabi Amplitude field
    • ...slice(start, stop).phase: to target the real-valued Rabi Phase field
    • ...slice(start, stop).detuning: to target the Detuning field
    • ...slice(start, stop).rabi: to target the complex-valued Rabi field ```
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/waveform.py
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
@beartype
def record(self, name: str) -> "Record":
    """
    Copy or "record" the value at the end of the waveform into a variable
    so that it can be used in another place.

    A common design pattern is to couple this with `.slice()` considering
    you may not know exactly what the end value of a `.slice()` is,
    especially in parameter sweeps where it becomes cumbersome to handle.

    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)
    previously without a waveform you will now have completed the construction
    of a "drive", one or a sum of drives creating a "field"
    (e.g. Real-valued Rabi Amplitude/Phase).

    If you have already specified a waveform previously you will now be appending
    this waveform to that previous waveform.

    ### Usage Example:
    ```
    # define program of interest
    >>> from bloqade import start
    >>> prog = start.rydberg.rabi.amplitude.uniform
    >>> prog_with_wf = prog.piecewise_linear(durations=[0.3, 2.0, 0.3],
    values=[0.0, 2.0, 2.0, 0.0])
    # We now slice the piecewise_linear from above and record the
    # value at the end of that slice. We then use that value
    # to construct a new waveform that can be appended to the previous
    # one without introducing discontinuity (refer to the
    # "Quantum Scar Dynamics" tutorial for how this could be handy)
    >>> prog_with_record = prog_with_wf.slice(0.0, 1.0).record("end_of_wf")
    >>> record_applied_prog = prog_with_record.linear(start="end_of_wf"
    , stop=0.0, duration=0.3)
    ```

    - Your next steps include:
    - Continue building your waveform via:
        - `...slice(start, stop).linear(start, stop, duration)`:
            to append another linear waveform
        - `...slice(start, stop).constant(value, duration)`:
            to append a constant waveform
        - `...slice(start, stop).piecewise_linear()`:
            to append a piecewise linear waveform
        - `...slice(start, stop).piecewise_constant()`:
            to append a piecewise constant waveform
        - `...slice(start, stop).poly([coefficients], duration)`:
            to append a polynomial waveform
        - `...slice(start, stop).apply(wf:bloqade.ir.Waveform)`:
            to append a pre-defined waveform
        - `...slilce(start, stop).fn(f(t,...))`:
            to append a waveform defined by a python function
    - Begin constructing another drive by starting a new spatial modulation
        (this drive will be summed to the one you just created):
        - `...slice(start, stop).uniform`:
            To address all atoms in the field
        - `...slice(start, stop).location(int)`:
            To address an atom at a specific location via index
        - `...slice(start, stop).scale(str)`
            - To address an atom at a specific location via variable
            - To address multiple atoms at specific locations by specifying
                a single variable and then assigning it a list of coordinates
    - Assign values to pre-existing variables via:
        - `...slice(start, stop).assign(variable_name = value)`:
            to assign a single value to a variable
        - `...slice(start, stop)
            .batch_assign(variable_name = [value1, ...])`:
            to assign multiple values to a variable
        - `...slice(start, stop).args(["previously_defined_var"])`:
            to defer assignment of a variable to execution time
    - Select the backend you want your program to run on via:
        - `...slice(start, stop).braket`:
            to run on Braket local emulator or QuEra hardware remotely
        - `...slice(start, stop).bloqade`:
            to run on the Bloqade local emulator
        - `...slice(start, stop).device`:
            to specify the backend via string
    - Choose to parallelize your atom geometry,
      duplicating it to fill the whole space:
        - `...slice(start, stop).parallelize(spacing)`
    - Start targeting another level coupling
        - `...slice(start, stop).rydberg`:
            to target the Rydberg level coupling
        - `...slice(start, stop).hyperfine`:
            to target the Hyperfine level coupling
    - Start targeting other fields within your current level coupling
      (previously selected as `rydberg` or `hyperfine`):
        - `...slice(start, stop).amplitude`:
            to target the real-valued Rabi Amplitude field
        - `...slice(start, stop).phase`:
            to target the real-valued Rabi Phase field
        - `...slice(start, stop).detuning`:
            to target the Detuning field
        - `...slice(start, stop).rabi`:
            to target the complex-valued Rabi field
    ```
    """
    return Record(name, self)

Sliceable

slice

slice(
    start: Optional[ScalarType] = None,
    stop: Optional[ScalarType] = None,
) -> Slice

Indicate that you only want a portion of your waveform to be used in the program.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a "drive", one or a sum of drives creating a "field" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

Usage Example:
# define a program with a waveform of interest
>>> from bloqade import start
>>> prog = start.add_position((0,0)).rydberg.rabi.amplitude.uniform
>>> prog_with_wf = prog.piecewise_linear(durations=[0.3, 2.0, 0.3],
values=[0.0, 2.0, 2.0, 0.0])
# instead of using the full waveform we opt to only take the first 1 us
>>> prog_with_slice = prog_with_wf.slice(0.0, 1.0)
# you may use variables as well
>>> prog_with_slice = prog_with_wf.slice("start", "end")
  • Your next steps include:
  • Continue building your waveform via:
    • ...slice(start, stop).linear(start, stop, duration): to append another linear waveform
    • ...slice(start, stop).constant(value, duration): to append a constant waveform
    • ...slice(start, stop).piecewise_linear(): to append a piecewise linear waveform
    • ...slice(start, stop).piecewise_constant(): to append a piecewise constant waveform
    • ...slice(start, stop).poly([coefficients], duration): to append a polynomial waveform
    • ...slice(start, stop).apply(wf:bloqade.ir.Waveform): to append a pre-defined waveform
    • ...slilce(start, stop).fn(f(t,...)): to append a waveform defined by a python function
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...slice(start, stop).uniform: To address all atoms in the field
    • ...slice(start, stop).location(int): To address an atom at a specific location via index
    • ...slice(start, stop).scale(...)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...slice(start, stop).assign(variable_name = value): to assign a single value to a variable
    • ...slice(start, stop) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...slice(start, stop).args(["previously_defined_var"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...slice(start, stop).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...slice(start, stop).bloqade: to run on the Bloqade local emulator
    • ...slice(start, stop).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...slice(start, stop).parallelize(spacing)
  • Start targeting another level coupling
    • ...slice(start, stop).rydberg: to target the Rydberg level coupling
    • ...slice(start, stop).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...slice(start, stop).amplitude: to target the real-valued Rabi Amplitude field
    • ...slice(start, stop).phase: to target the real-valued Rabi Phase field
    • ...slice(start, stop).detuning: to target the Detuning field
    • ...slice(start, stop).rabi: to target the complex-valued Rabi field
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/waveform.py
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
@beartype
def slice(
    self,
    start: Optional[ScalarType] = None,
    stop: Optional[ScalarType] = None,
) -> "Slice":
    """
    Indicate that you only want a portion of your waveform to be used in
    the program.

    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)
    previously without a waveform you will now have completed the construction
    of a "drive", one or a sum of drives creating a "field"
    (e.g. Real-valued Rabi Amplitude/Phase).

    If you have already specified a waveform previously you will now be appending
    this waveform to that previous waveform.


    ### Usage Example:
    ```
    # define a program with a waveform of interest
    >>> from bloqade import start
    >>> prog = start.add_position((0,0)).rydberg.rabi.amplitude.uniform
    >>> prog_with_wf = prog.piecewise_linear(durations=[0.3, 2.0, 0.3],
    values=[0.0, 2.0, 2.0, 0.0])
    # instead of using the full waveform we opt to only take the first 1 us
    >>> prog_with_slice = prog_with_wf.slice(0.0, 1.0)
    # you may use variables as well
    >>> prog_with_slice = prog_with_wf.slice("start", "end")
    ```

    - Your next steps include:
    - Continue building your waveform via:
        - `...slice(start, stop).linear(start, stop, duration)`:
            to append another linear waveform
        - `...slice(start, stop).constant(value, duration)`:
            to append a constant waveform
        - `...slice(start, stop).piecewise_linear()`:
            to append a piecewise linear waveform
        - `...slice(start, stop).piecewise_constant()`:
            to append a piecewise constant waveform
        - `...slice(start, stop).poly([coefficients], duration)`:
            to append a polynomial waveform
        - `...slice(start, stop).apply(wf:bloqade.ir.Waveform)`:
            to append a pre-defined waveform
        - `...slilce(start, stop).fn(f(t,...))`:
            to append a waveform defined by a python function
    - Begin constructing another drive by starting a new spatial modulation
        (this drive will be summed to the one you just created):
        - `...slice(start, stop).uniform`:
            To address all atoms in the field
        - `...slice(start, stop).location(int)`:
            To address an atom at a specific location via index
        - `...slice(start, stop).scale(...)`
            - To address an atom at a specific location via variable
            - To address multiple atoms at specific locations by specifying
                a single variable and then assigning it a list of coordinates
    - Assign values to pre-existing variables via:
        - `...slice(start, stop).assign(variable_name = value)`:
            to assign a single value to a variable
        - `...slice(start, stop)
            .batch_assign(variable_name = [value1, ...])`:
            to assign multiple values to a variable
        - `...slice(start, stop).args(["previously_defined_var"])`:
            to defer assignment of a variable to execution time
    - Select the backend you want your program to run on via:
        - `...slice(start, stop).braket`:
            to run on Braket local emulator or QuEra hardware remotely
        - `...slice(start, stop).bloqade`:
            to run on the Bloqade local emulator
        - `...slice(start, stop).device`:
            to specify the backend via string
    - Choose to parallelize your atom geometry,
      duplicating it to fill the whole space:
        - `...slice(start, stop).parallelize(spacing)`
    - Start targeting another level coupling
        - `...slice(start, stop).rydberg`:
            to target the Rydberg level coupling
        - `...slice(start, stop).hyperfine`:
            to target the Hyperfine level coupling
    - Start targeting other fields within your current level coupling
      (previously selected as `rydberg` or `hyperfine`):
        - `...slice(start, stop).amplitude`:
            to target the real-valued Rabi Amplitude field
        - `...slice(start, stop).phase`:
            to target the real-valued Rabi Phase field
        - `...slice(start, stop).detuning`:
            to target the Detuning field
        - `...slice(start, stop).rabi`:
            to target the complex-valued Rabi field
    """
    return Slice(start, stop, self)

WaveformAttachable

WaveformAttachable(parent: Optional[Builder] = None)

Bases: Builder

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

apply

apply(wf: Waveform) -> Apply

Apply a [Waveform][bloqade.ir.control.Waveform] built previously to current location(s).

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a "drive", one or a sum of drives creating a "field" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

Usage Example:
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform
# build our waveform independently of the main program
>>> from bloqade import piecewise_linear
>>> wf = piecewise_linear(durations=[0.3, 2.5, 0.3],
values=[0.0, 2.0, 2.0, 0.0])
>>> prog.apply(wf)
  • Your next steps include:
  • Continue building your waveform via:
    • ...apply(waveform).linear(start, stop, duration): to append another linear waveform
    • ...apply(waveform).constant(value, duration): to append a constant waveform
    • ...apply(waveform).piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...apply(waveform).piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...apply(waveform).poly([coefficients], duration): to append a polynomial waveform
    • ...apply(waveform).apply(waveform): to append a pre-defined waveform
    • ...apply(waveform).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...apply(waveform).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...apply(waveform).record("you_variable_here")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...apply(waveform).uniform: To address all atoms in the field
    • ...apply(waveform).location(int): To address an atom at a specific location via index
    • ...apply(waveform).scale(...)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...apply(waveform).assign(variable_name = value): to assign a single value to a variable
    • ...apply(waveform).batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...apply(waveform).args(["previously_defined_var"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...apply(waveform).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...apply(waveform).bloqade: to run on the Bloqade local emulator
    • ...apply(waveform).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...apply(waveform).parallelize(spacing)
  • Start targeting another level coupling
    • ...apply(waveform).rydberg: to target the Rydberg level coupling
    • ...apply(waveform).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...apply(waveform).amplitude: to target the real-valued Rabi Amplitude field
    • ...apply(waveform).phase: to target the real-valued Rabi Phase field
    • ...apply(waveform).detuning: to target the Detuning field
    • ...apply(waveform).rabi: to target the complex-valued Rabi field
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/waveform.py
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
@beartype
def apply(self, wf: ir.Waveform) -> "Apply":
    """
    Apply a [`Waveform`][bloqade.ir.control.Waveform] built previously to
    current location(s).

    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)
    previously without a waveform you will now have completed the construction
    of a "drive", one or a sum of drives creating a "field"
    (e.g. Real-valued Rabi Amplitude/Phase).

    If you have already specified a waveform previously you will now be appending
    this waveform to that previous waveform.

    ### Usage Example:
    ```
    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform
    # build our waveform independently of the main program
    >>> from bloqade import piecewise_linear
    >>> wf = piecewise_linear(durations=[0.3, 2.5, 0.3],
    values=[0.0, 2.0, 2.0, 0.0])
    >>> prog.apply(wf)
    ```

    - Your next steps include:
    - Continue building your waveform via:
        - `...apply(waveform).linear(start, stop, duration)`:
            to append another linear waveform
        - `...apply(waveform).constant(value, duration)`:
            to append a constant waveform
        - `...apply(waveform).piecewise_linear([durations], [values])`:
            to append a piecewise linear waveform
        - `...apply(waveform).piecewise_constant([durations], [values])`:
            to append a piecewise constant waveform
        - `...apply(waveform).poly([coefficients], duration)`:
            to append a polynomial waveform
        - `...apply(waveform).apply(waveform)`:
            to append a pre-defined waveform
        - `...apply(waveform).fn(f(t,...))`:
            to append a waveform defined by a python function
    - Slice a portion of the waveform to be used:
        - `...apply(waveform).slice(start, stop, duration)`
    - Save the ending value of your waveform to be reused elsewhere
        - `...apply(waveform).record("you_variable_here")`
    - Begin constructing another drive by starting a new spatial modulation
      (this drive will be summed to the one you just created):
        - `...apply(waveform).uniform`: To address all atoms in the field
        - `...apply(waveform).location(int)`:
            To address an atom at a specific location via index
        - `...apply(waveform).scale(...)`
            - To address an atom at a specific location via variable
            - To address multiple atoms at specific locations by specifying a
                single variable and then assigning it a list of coordinates
    - Assign values to pre-existing variables via:
        - `...apply(waveform).assign(variable_name = value)`:
            to assign a single value to a variable
        - `...apply(waveform).batch_assign(variable_name = [value1, ...])`:
            to assign multiple values to a variable
        - `...apply(waveform).args(["previously_defined_var"])`:
            to defer assignment of a variable to execution time
    - Select the backend you want your program to run on via:
        - `...apply(waveform).braket`:
            to run on Braket local emulator or QuEra hardware remotely
        - `...apply(waveform).bloqade`:
            to run on the Bloqade local emulator
        - `...apply(waveform).device`:
            to specify the backend via string
    - Choose to parallelize your atom geometry,
      duplicating it to fill the whole space:
        - `...apply(waveform).parallelize(spacing)`
    - Start targeting another level coupling
        - `...apply(waveform).rydberg`: to target the Rydberg level coupling
        - `...apply(waveform).hyperfine`: to target the Hyperfine level coupling
    - Start targeting other fields within your current level coupling
      (previously selected as `rydberg` or `hyperfine`):
        - `...apply(waveform).amplitude`:
            to target the real-valued Rabi Amplitude field
        - `...apply(waveform).phase`:
            to target the real-valued Rabi Phase field
        - `...apply(waveform).detuning`:
            to target the Detuning field
        - `...apply(waveform).rabi`:
            to target the complex-valued Rabi field
    """
    return Apply(wf, self)

constant

constant(
    value: ScalarType, duration: ScalarType
) -> Constant

Append or assign a constant waveform to the current location(s).

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a "drive", one or a sum of drives creating a "field" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

Usage Example:
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform
# apply a constant waveform of 1.9 radians/us for 0.5 us
>>> prog.constant(value=1.9,duration=0.5)
  • Your next steps include:
  • Continue building your waveform via:
    • ...constant(value, duration).linear(start, stop, duration): to append another linear waveform
    • ...constant(value, duration).constant(value, duration): to append a constant waveform
    • ...constant(value, duration) .piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...constant(value, duration) .piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...constant(value, duration).poly([coefficients], duration): to append a polynomial waveform
    • ...constant(value, duration).apply(wf:bloqade.ir.Waveform): to append a pre-defined waveform
    • ...constant(value, duration).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...constant(value, duration).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...constant(value, duration).record("you_variable_here")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...constant(value, duration).uniform: To address all atoms in the field
    • ...constant(value, duration).scale(...): To address an atom at a specific location via index
    • ...constant(value, duration).location(int)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...constant(value, duration).assign(variable_name = value): to assign a single value to a variable
    • ...constant(value, duration) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...constant(value, duration).args(["previously_defined_var"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...constant(value, duration).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...constant(value, duration).bloqade: to run on the Bloqade local emulator
    • ...constant(value, duration).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...constant(start, stop, duration).parallelize(spacing)
  • Start targeting another level coupling
    • ...constant(value, duration).rydberg: to target the Rydberg level coupling
    • ...constant(value, duration).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...constant(value, duration).amplitude: to target the real-valued Rabi Amplitude field
    • ...constant(value, duration).phase: to target the real-valued Rabi Phase field
    • ...constant(value, duration).detuning: to target the Detuning field
    • ...constant(value, duration).rabi: to target the complex-valued Rabi field
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/waveform.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
@beartype
def constant(self, value: ScalarType, duration: ScalarType) -> "Constant":
    """
    Append or assign a constant waveform to the current location(s).

    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)
    previously without a waveform you will now have completed the construction
    of a "drive", one or a sum of drives creating a "field"
    (e.g. Real-valued Rabi Amplitude/Phase).

    If you have already specified a waveform previously you will now be appending
    this waveform to that previous waveform.

    ### Usage Example:
    ```
    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform
    # apply a constant waveform of 1.9 radians/us for 0.5 us
    >>> prog.constant(value=1.9,duration=0.5)
    ```

    - Your next steps include:
    - Continue building your waveform via:
        - `...constant(value, duration).linear(start, stop, duration)`:
            to append another linear waveform
        - `...constant(value, duration).constant(value, duration)`:
            to append a constant waveform
        - `...constant(value, duration)
            .piecewise_linear([durations], [values])`:
            to append a piecewise linear waveform
        - `...constant(value, duration)
            .piecewise_constant([durations], [values])`:
            to append a piecewise constant waveform
        - `...constant(value, duration).poly([coefficients], duration)`:
            to append a polynomial waveform
        - `...constant(value, duration).apply(wf:bloqade.ir.Waveform)`:
            to append a pre-defined waveform
        - `...constant(value, duration).fn(f(t,...))`:
            to append a waveform defined by a python function
    - Slice a portion of the waveform to be used:
        - `...constant(value, duration).slice(start, stop, duration)`
    - Save the ending value of your waveform to be reused elsewhere
        - `...constant(value, duration).record("you_variable_here")`
    - Begin constructing another drive by starting a new spatial modulation
        (this drive will be summed to the one you just created):
        - `...constant(value, duration).uniform`:
            To address all atoms in the field
        - `...constant(value, duration).scale(...)`:
            To address an atom at a specific location via index
        - `...constant(value, duration).location(int)`
            - To address an atom at a specific location via variable
            - To address multiple atoms at specific locations by specifying
                a single variable and then assigning it a list of coordinates
    - Assign values to pre-existing variables via:
        - `...constant(value, duration).assign(variable_name = value)`:
            to assign a single value to a variable
        - `...constant(value, duration)
            .batch_assign(variable_name = [value1, ...])`:
            to assign multiple values to a variable
        - `...constant(value, duration).args(["previously_defined_var"])`:
            to defer assignment of a variable to execution time
    - Select the backend you want your program to run on via:
        - `...constant(value, duration).braket`:
            to run on Braket local emulator or QuEra hardware remotely
        - `...constant(value, duration).bloqade`:
            to run on the Bloqade local emulator
        - `...constant(value, duration).device`:
            to specify the backend via string
    - Choose to parallelize your atom geometry,
      duplicating it to fill the whole space:
        - `...constant(start, stop, duration).parallelize(spacing)`
    - Start targeting another level coupling
        - `...constant(value, duration).rydberg`:
            to target the Rydberg level coupling
        - `...constant(value, duration).hyperfine`:
            to target the Hyperfine level coupling
    - Start targeting other fields within your current
      level coupling (previously selected as `rydberg` or `hyperfine`):
        - `...constant(value, duration).amplitude`:
            to target the real-valued Rabi Amplitude field
        - `...constant(value, duration).phase`:
            to target the real-valued Rabi Phase field
        - `...constant(value, duration).detuning`:
            to target the Detuning field
        - `...constant(value, duration).rabi`:
            to target the complex-valued Rabi field

    """
    return Constant(value, duration, self)

fn

fn(fn: Callable, duration: ScalarType) -> Fn

Append or assign a custom function as a waveform.

The function must have its first argument be that of time but can also have other arguments which are treated as variables. You can assign values to later in the program via .assign or .batch_assign.

The function must also return a singular float value.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a "drive", one or a sum of drives creating a "field" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

### Usage Examples:
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform
# define our custom waveform. It must have one argument
# be time followed by any other number of arguments that can
# be assigned a value later in the program via `.assign` or `.batch_assign`
>>> def custom_waveform_function(t, arg1, arg2):
        return arg1*t + arg2
>>> prog = prog.fn(custom_waveform_function, duration = 0.5)
# assign values
>>> assigned_vars_prog = prog.assign(arg1 = 1.0, arg2 = 2.0)
# or go for batching!
>>> assigned_vars_batch_prog = prog.assign(arg1 = 1.0, arg2 = [1.0, 2.0, 3.0])
  • Your next steps include:
  • Continue building your waveform via:
    • ...fn(f(t,...)) .linear(start, stop, duration): to append another linear waveform
    • ...fn(f(t,...)) .constant(value, duration): to append a constant waveform
    • ...fn(f(t,...)) .piecewise_linear(durations, values): to append a piecewise linear waveform
    • ...fn(f(t,...)) .piecewise_constant(durations, values): to append a piecewise constant waveform
    • ...fn(f(t,...)) .poly([coefficients], duration): to append a polynomial waveform
    • ...fn(f(t,...)) .apply(waveform): to append a pre-defined waveform
    • ...fn(f(t,...)) .fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...fn(f(t,...)).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...fn(f(t,...)).record("you_variable_here")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...fn(f(t,...)).uniform: To address all atoms in the field
    • ...fn(f(t,...)).scale(...): To address an atom at a specific location via index
    • ...fn(f(t,...)).location(int)`
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...fn(f(t,...)) .assign(variable_name = value): to assign a single value to a variable
    • ...fn(f(t,...)) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...fn(f(t,...)) .args(["previously_defined_var"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...fn(f(t,...)).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...fn(f(t,...)).bloqade: to run on the Bloqade local emulator
    • ...fn(f(t,...)).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...fn(f(t,...)).parallelize(spacing)
  • Start targeting another level coupling
    • ...fn(f(t,...)).rydberg: to target the Rydberg level coupling
    • ...fn(f(t,...)).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...fn(f(t,...)).amplitude: to target the real-valued Rabi Amplitude field
    • ...fn(f(t,...)).phase: to target the real-valued Rabi Phase field
    • ...fn(f(t,...)).detuning: to target the Detuning field
    • ...fn(f(t,...)).rabi: to target the complex-valued Rabi field
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/waveform.py
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
@beartype
def fn(self, fn: Callable, duration: ScalarType) -> "Fn":
    """
    Append or assign a custom function as a waveform.

    The function must have its first argument be that of time but
    can also have other arguments which are treated as variables.
    You can assign values to later in the program via `.assign` or `.batch_assign`.

    The function must also return a singular float value.

    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)
    previously without a waveform you will now have completed the construction
    of a "drive", one or a sum of drives creating a "field"
    (e.g. Real-valued Rabi Amplitude/Phase).

    If you have already specified a waveform previously you will now be appending
    this waveform to that previous waveform.

    ### ### Usage Examples:
    ```
    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform
    # define our custom waveform. It must have one argument
    # be time followed by any other number of arguments that can
    # be assigned a value later in the program via `.assign` or `.batch_assign`
    >>> def custom_waveform_function(t, arg1, arg2):
            return arg1*t + arg2
    >>> prog = prog.fn(custom_waveform_function, duration = 0.5)
    # assign values
    >>> assigned_vars_prog = prog.assign(arg1 = 1.0, arg2 = 2.0)
    # or go for batching!
    >>> assigned_vars_batch_prog = prog.assign(arg1 = 1.0, arg2 = [1.0, 2.0, 3.0])
    ```

    - Your next steps include:
    - Continue building your waveform via:
        - `...fn(f(t,...))
            .linear(start, stop, duration)`: to append another linear waveform
        - `...fn(f(t,...))
            .constant(value, duration)`: to append a constant waveform
        - `...fn(f(t,...))
            .piecewise_linear(durations, values)`:
            to append a piecewise linear waveform
        - `...fn(f(t,...))
            .piecewise_constant(durations, values)`:
            to append a piecewise constant waveform
        - `...fn(f(t,...))
            .poly([coefficients], duration)`: to append a polynomial waveform
        - `...fn(f(t,...))
            .apply(waveform)`: to append a pre-defined waveform
        - `...fn(f(t,...))
            .fn(f(t,...))`: to append a waveform defined by a python function
    - Slice a portion of the waveform to be used:
        - `...fn(f(t,...)).slice(start, stop, duration)`
    - Save the ending value of your waveform to be reused elsewhere
        - `...fn(f(t,...)).record("you_variable_here")`
    - Begin constructing another drive by starting a new spatial modulation
      (this drive will be summed to the one you just created):
        - `...fn(f(t,...)).uniform`:
            To address all atoms in the field
        - `...fn(f(t,...)).scale(...)`:
            To address an atom at a specific location via index
        - ...fn(f(t,...)).location(int)`
            - To address an atom at a specific location via variable
            - To address multiple atoms at specific locations by
                specifying a single variable and then assigning it a
                list of coordinates
    - Assign values to pre-existing variables via:
        - `...fn(f(t,...))
            .assign(variable_name = value)`: to assign a single value to a variable
        - `...fn(f(t,...))
            .batch_assign(variable_name = [value1, ...])`:
            to assign multiple values to a variable
        - `...fn(f(t,...))
            .args(["previously_defined_var"])`:
            to defer assignment of a variable to execution time
    - Select the backend you want your program to run on via:
        - `...fn(f(t,...)).braket`:
            to run on Braket local emulator or QuEra hardware remotely
        - `...fn(f(t,...)).bloqade`:
            to run on the Bloqade local emulator
        - `...fn(f(t,...)).device`:
            to specify the backend via string
    - Choose to parallelize your atom geometry,
      duplicating it to fill the whole space:
        - `...fn(f(t,...)).parallelize(spacing)`
    - Start targeting another level coupling
        - `...fn(f(t,...)).rydberg`:
            to target the Rydberg level coupling
        - `...fn(f(t,...)).hyperfine`:
            to target the Hyperfine level coupling
    - Start targeting other fields within your current level coupling
      (previously selected as `rydberg` or `hyperfine`):
        - `...fn(f(t,...)).amplitude`:
            to target the real-valued Rabi Amplitude field
        - `...fn(f(t,...)).phase`:
            to target the real-valued Rabi Phase field
        - `...fn(f(t,...)).detuning`:
            to target the Detuning field
        - `...fn(f(t,...)).rabi`:
            to target the complex-valued Rabi field

    """
    return Fn(fn, duration, self)

linear

linear(
    start: ScalarType,
    stop: ScalarType,
    duration: ScalarType,
) -> Linear

Append or assign a linear waveform to the current location(s).

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a "drive", one or a sum of drives creating a "field" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

Usage Example:
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform
# apply a linear waveform that goes from 0 to 1 radians/us in 0.5 us
>>> prog.linear(start=0,stop=1,duration=0.5)
  • Your next steps include:
  • Continue building your waveform via:
    • ...linear(start, stop, duration).linear(start, stop, duration): to append another linear waveform
    • ...linear(start, stop, duration).constant(value, duration): to append a constant waveform
    • ...linear(start, stop, duration) .piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...linear(start, stop, duration) .piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...linear(start, stop, duration).poly([coefficients], duration): to append a polynomial waveform
    • ...linear(start, stop, duration).apply(wf:bloqade.ir.Waveform): to append a pre-defined waveform
    • ...linear(start, stop, duration).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...linear(start, stop, duration).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...linear(start, stop, duration).record("you_variable_here")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...linear(start, stop, duration).uniform: To address all atoms in the field
    • ...linear(start, stop, duration).location(int): To address atoms at specific location with scaling
    • ...linear(start, stop, duration).scale(...)
      • To address atoms at specific location with scaling
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...linear(start, stop, duration).assign(variable_name = value): to assign a single value to a variable
    • ...linear(start, stop, duration) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...linear(start, stop, duration).args(["previously_defined_var"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...linear(start, stop, duration).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...linear(start, stop, duration).bloqade: to run on the Bloqade local emulator
    • ...linear(start, stop, duration).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...linear(start, stop, duration).parallelize(spacing)
  • Start targeting another level coupling
    • ...linear(start, stop, duration).rydberg: to target the Rydberg level coupling
    • ...linear(start, stop, duration).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...linear(start, stop, duration).amplitude: to target the real-valued Rabi Amplitude field
    • ...linear(start, stop, duration).phase: to target the real-valued Rabi Phase field
    • ...linear(start, stop, duration).detuning: to target the Detuning field
    • ...linear(start, stop, duration).rabi: to target the complex-valued Rabi field
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/waveform.py
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
@beartype
def linear(
    self, start: ScalarType, stop: ScalarType, duration: ScalarType
) -> "Linear":
    """

    Append or assign a linear waveform to the current location(s).

    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)
    previously without a waveform you will now have completed the construction
    of a "drive", one or a sum of drives creating a "field"
    (e.g. Real-valued Rabi Amplitude/Phase).

    If you have already specified a waveform previously you will now be appending
    this waveform to that previous waveform.

    ### Usage Example:
    ```
    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform
    # apply a linear waveform that goes from 0 to 1 radians/us in 0.5 us
    >>> prog.linear(start=0,stop=1,duration=0.5)
    ```

    - Your next steps include:
    - Continue building your waveform via:
        - `...linear(start, stop, duration).linear(start, stop, duration)`:
            to append another linear waveform
        - `...linear(start, stop, duration).constant(value, duration)`:
            to append a constant waveform
        - `...linear(start, stop, duration)
            .piecewise_linear([durations], [values])`:
            to append a piecewise linear waveform
        - `...linear(start, stop, duration)
            .piecewise_constant([durations], [values])`:
            to append a piecewise constant waveform
        - `...linear(start, stop, duration).poly([coefficients], duration)`:
            to append a polynomial waveform
        - `...linear(start, stop, duration).apply(wf:bloqade.ir.Waveform)`:
            to append a pre-defined waveform
        - `...linear(start, stop, duration).fn(f(t,...))`:
            to append a waveform defined by a python function
    - Slice a portion of the waveform to be used:
        - `...linear(start, stop, duration).slice(start, stop, duration)`
    - Save the ending value of your waveform to be reused elsewhere
        - `...linear(start, stop, duration).record("you_variable_here")`
    - Begin constructing another drive by starting a new spatial modulation
        (this drive will be summed to the one you just created):
        - `...linear(start, stop, duration).uniform`:
            To address all atoms in the field
        - `...linear(start, stop, duration).location(int)`:
            To address atoms at specific location with scaling
        - `...linear(start, stop, duration).scale(...)`
            - To address atoms at specific location with scaling
            - To address multiple atoms at specific locations by specifying
                a single variable and then assigning it a list of coordinates
    - Assign values to pre-existing variables via:
        - `...linear(start, stop, duration).assign(variable_name = value)`:
            to assign a single value to a variable
        - `...linear(start, stop, duration)
            .batch_assign(variable_name = [value1, ...])`:
            to assign multiple values to a variable
        - `...linear(start, stop, duration).args(["previously_defined_var"])`:
            to defer assignment of a variable to execution time
    - Select the backend you want your program to run on via:
        - `...linear(start, stop, duration).braket`:
            to run on Braket local emulator or QuEra hardware remotely
        - `...linear(start, stop, duration).bloqade`:
            to run on the Bloqade local emulator
        - `...linear(start, stop, duration).device`:
            to specify the backend via string
    - Choose to parallelize your atom geometry,
      duplicating it to fill the whole space:
        - `...linear(start, stop, duration).parallelize(spacing)`
    - Start targeting another level coupling
        - `...linear(start, stop, duration).rydberg`:
            to target the Rydberg level coupling
        - `...linear(start, stop, duration).hyperfine`:
            to target the Hyperfine level coupling
    - Start targeting other fields within your current level coupling
      (previously selected as `rydberg` or `hyperfine`):
        - `...linear(start, stop, duration).amplitude`:
            to target the real-valued Rabi Amplitude field
        - `...linear(start, stop, duration).phase`:
            to target the real-valued Rabi Phase field
        - `...linear(start, stop, duration).detuning`:
            to target the Detuning field
        - `...linear(start, stop, duration).rabi`:
            to target the complex-valued Rabi field
    """

    return Linear(start, stop, duration, self)

piecewise_constant

piecewise_constant(
    durations: List[ScalarType], values: List[ScalarType]
) -> PiecewiseConstant

Append or assign a piecewise constant waveform to current location(s).

The durations argument should have number of elements = len(values). durations should be the duration PER section of the waveform, NON-CUMULATIVE.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a "drive", one or a sum of drives creating a "field" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

Usage Example:
>>> prog = start.add_position((0,0)).rydberg.rabi.phase.uniform
# create a staircase, we hold 0.0 rad/us for 1.0 us, then
# to 1.0 rad/us for 0.5 us before stopping at 0.8 rad/us for 0.9 us.
>>> prog.piecewise_linear(durations=[0.3, 2.0, 0.3], values=[1.0, 0.5, 0.9])
  • Your next steps including:
  • Continue building your waveform via:
    • ...piecewise_constant([durations], [values]) .linear(start, stop, duration): to append another linear waveform
    • ...piecewise_constant([durations], [values]) .constant(value, duration): to append a constant waveform
    • ...piecewise_constant([durations], [values]) .piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...piecewise_constant([durations], [values]) .piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...piecewise_constant([durations], [values]) .poly([coefficients], duration): to append a polynomial waveform
    • ...piecewise_constant([durations], [values]) .apply(waveform): to append a pre-defined waveform
    • ...piecewise_constant([durations], [values]).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...piecewise_constant([durations], [values]) .slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...piecewise_constant([durations], [values]) .record("you_variable_here")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...piecewise_constant([durations], [values]).uniform: To address all atoms in the field
    • ...piecewise_constant([durations], [values]).location(int): To address an atom at a specific location via index
    • ...piecewise_constant([durations], [values]).scale(...)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...piecewise_constant([durations], [values]) .assign(variable_name = value): to assign a single value to a variable
    • ...piecewise_constant([durations], [values]) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...piecewise_constant([durations], [values]) .args(["previously_defined_var"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...piecewise_constant([durations], [values]).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...piecewise_constant([durations], [values]).bloqade: to run on the Bloqade local emulator
    • ...piecewise_constant([durations], [values]).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...piecewise_constat([durations], [values]).parallelize(spacing)
  • Start targeting another level coupling
    • ...piecewise_constant([durations], [values]).rydberg: to target the Rydberg level coupling
    • ...piecewise_constant([durations], [values]).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...piecewise_constant(durations, values).amplitude: to target the real-valued Rabi Amplitude field
    • ...piecewise_constant([durations], [values]).phase: to target the real-valued Rabi Phase field
    • ...piecewise_constant([durations], [values]).detuning: to target the Detuning field
    • ...piecewise_constant([durations], [values]).rabi: to target the complex-valued Rabi field
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/waveform.py
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
@beartype
def piecewise_constant(
    self, durations: List[ScalarType], values: List[ScalarType]
) -> "PiecewiseConstant":
    """
    Append or assign a piecewise constant waveform to current location(s).

    The `durations` argument should have number of elements = len(values).
    `durations` should be the duration PER section of the waveform,
    NON-CUMULATIVE.

    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)
    previously without a waveform you will now have completed the construction
    of a "drive", one or a sum of drives creating a "field"
    (e.g. Real-valued Rabi Amplitude/Phase).

    If you have already specified a waveform previously you will now be appending
    this waveform to that previous waveform.

    ### Usage Example:
    ```
    >>> prog = start.add_position((0,0)).rydberg.rabi.phase.uniform
    # create a staircase, we hold 0.0 rad/us for 1.0 us, then
    # to 1.0 rad/us for 0.5 us before stopping at 0.8 rad/us for 0.9 us.
    >>> prog.piecewise_linear(durations=[0.3, 2.0, 0.3], values=[1.0, 0.5, 0.9])
    ```

    - Your next steps including:
    - Continue building your waveform via:
        - `...piecewise_constant([durations], [values])
            .linear(start, stop, duration)`: to append another linear waveform
        - `...piecewise_constant([durations], [values])
            .constant(value, duration)`: to append a constant waveform
        - `...piecewise_constant([durations], [values])
            .piecewise_linear([durations], [values])`:
            to append a piecewise linear waveform
        - `...piecewise_constant([durations], [values])
            .piecewise_constant([durations], [values])`:
            to append a piecewise constant waveform
        - `...piecewise_constant([durations], [values])
            .poly([coefficients], duration)`: to append a polynomial waveform
        - `...piecewise_constant([durations], [values])
            .apply(waveform)`: to append a pre-defined waveform
        - `...piecewise_constant([durations], [values]).fn(f(t,...))`:
            to append a waveform defined by a python function
    - Slice a portion of the waveform to be used:
        - `...piecewise_constant([durations], [values])
            .slice(start, stop, duration)`
    - Save the ending value of your waveform to be reused elsewhere
        - `...piecewise_constant([durations], [values])
            .record("you_variable_here")`
    - Begin constructing another drive by starting a new spatial modulation
      (this drive will be summed to the one you just created):
        - `...piecewise_constant([durations], [values]).uniform`:
            To address all atoms in the field
        - `...piecewise_constant([durations], [values]).location(int)`:
            To address an atom at a specific location via index
        - `...piecewise_constant([durations], [values]).scale(...)`
            - To address an atom at a specific location via variable
            - To address multiple atoms at specific locations by
                specifying a single variable and then assigning it a
                list of coordinates
    - Assign values to pre-existing variables via:
        - `...piecewise_constant([durations], [values])
            .assign(variable_name = value)`: to assign a single value to a variable
        - `...piecewise_constant([durations], [values])
            .batch_assign(variable_name = [value1, ...])`:
            to assign multiple values to a variable
        - `...piecewise_constant([durations], [values])
            .args(["previously_defined_var"])`: to defer assignment
            of a variable to execution time
    - Select the backend you want your program to run on via:
        - `...piecewise_constant([durations], [values]).braket`:
            to run on Braket local emulator or QuEra hardware remotely
        - `...piecewise_constant([durations], [values]).bloqade`:
            to run on the Bloqade local emulator
        - `...piecewise_constant([durations], [values]).device`:
            to specify the backend via string
    - Choose to parallelize your atom geometry,
      duplicating it to fill the whole space:
        - `...piecewise_constat([durations], [values]).parallelize(spacing)`
    - Start targeting another level coupling
        - `...piecewise_constant([durations], [values]).rydberg`:
            to target the Rydberg level coupling
        - `...piecewise_constant([durations], [values]).hyperfine`:
            to target the Hyperfine level coupling
    - Start targeting other fields within your current level coupling
      (previously selected as `rydberg` or `hyperfine`):
        - `...piecewise_constant(durations, values).amplitude`:
            to target the real-valued Rabi Amplitude field
        - `...piecewise_constant([durations], [values]).phase`:
            to target the real-valued Rabi Phase field
        - `...piecewise_constant([durations], [values]).detuning`:
            to target the Detuning field
        - `...piecewise_constant([durations], [values]).rabi`:
            to target the complex-valued Rabi field
    """
    return PiecewiseConstant(durations, values, self)

piecewise_linear

piecewise_linear(
    durations: List[ScalarType], values: List[ScalarType]
) -> PiecewiseLinear

Append or assign a piecewise linear waveform to current location(s), where the waveform is formed by connecting values[i], values[i+1] with linear segments.

The durations argument should have # of elements = len(values) - 1. durations should be the duration PER section of the waveform, NON-CUMULATIVE.

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a "drive", one or a sum of drives creating a "field" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

Usage Example:
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform
# ramp our waveform up to a certain value, hold it
# then ramp down. In this case, we ramp up to 2.0 rad/us in 0.3 us,
# then hold it for 1.5 us before ramping down in 0.3 us back to 0.0 rad/us.
>>> prog.piecewise_linear(durations=[0.3, 2.0, 0.3],
values=[0.0, 2.0, 2.0, 0.0])
  • Your next steps include:
  • Continue building your waveform via:
    • ...piecewise_linear([durations], [values]) .linear(start, stop, duration): to append another linear waveform
    • ...piecewise_linear([durations], [values]).constant(value, duration): to append a constant waveform
    • ...piecewise_linear([durations], [values]) .piecewise_linear(durations, values): to append a piecewise linear waveform
    • ...piecewise_linear([durations], [values]) .piecewise_constant([durations], [values]): to append a piecewise constant waveform
    • ...piecewise_linear([durations], [values]) .poly([coefficients], duration): to append a polynomial waveform
    • ...piecewise_linear([durations], [values]).apply(waveform): to append a pre-defined waveform
    • ...piecewise_linear([durations], [values]).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...piecewise_linear([durations], [values]) .slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...piecewise_linear([durations], [values]) .record("you_variable_here")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...piecewise_linear([durations], [values]).uniform: To address all atoms in the field
    • ...piecewise_linear([durations], [values]).scale(...): To address an atom at a specific location via index
    • ...piecewise_linear([durations], [values]).location(int)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...piecewise_linear([durations], [values]) .assign(variable_name = value): to assign a single value to a variable
    • ...piecewise_linear([durations], [values]) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...piecewise_linear([durations], [values]) .args(["previously_defined_var"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...piecewise_linear([durations], [values]).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...piecewise_linear([durations], [values]).bloqade: to run on the Bloqade local emulator
    • ...piecewise_linear([durations], [values]).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...piecewise_linear([durations], [values]).parallelize(spacing)
  • Start targeting another level coupling
    • ...piecewise_linear([durations], [values]).rydberg: to target the Rydberg level coupling
    • ...piecewise_linear([durations], [values]).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...piecewise_linear([durations], [values]).amplitude: to target the real-valued Rabi Amplitude field
    • ...piecewise_linear([durations], [values]).phase: to target the real-valued Rabi Phase field
    • ...piecewise_linear([durations], [values]).detuning: to target the Detuning field
    • ....rabi: to target the complex-valued Rabi field
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/waveform.py
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
@beartype
def piecewise_linear(
    self, durations: List[ScalarType], values: List[ScalarType]
) -> "PiecewiseLinear":
    """
    Append or assign a piecewise linear waveform to current location(s),
    where the waveform is formed by connecting `values[i], values[i+1]`
    with linear segments.

    The `durations` argument should have # of elements = len(values) - 1.
    `durations` should be the duration PER section of the waveform, NON-CUMULATIVE.

    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)
    previously without a waveform you will now have completed the construction
    of a "drive", one or a sum of drives creating a "field"
    (e.g. Real-valued Rabi Amplitude/Phase).

    If you have already specified a waveform previously you will now be appending
    this waveform to that previous waveform.

    ### Usage Example:
    ```
    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform
    # ramp our waveform up to a certain value, hold it
    # then ramp down. In this case, we ramp up to 2.0 rad/us in 0.3 us,
    # then hold it for 1.5 us before ramping down in 0.3 us back to 0.0 rad/us.
    >>> prog.piecewise_linear(durations=[0.3, 2.0, 0.3],
    values=[0.0, 2.0, 2.0, 0.0])
    ```

    - Your next steps include:
    - Continue building your waveform via:
        - `...piecewise_linear([durations], [values])
            .linear(start, stop, duration)`:
            to append another linear waveform
        - `...piecewise_linear([durations], [values]).constant(value, duration)`:
            to append a constant waveform
        - `...piecewise_linear([durations], [values])
            .piecewise_linear(durations, values)`:
            to append a piecewise linear waveform
        - `...piecewise_linear([durations], [values])
            .piecewise_constant([durations], [values])`:
            to append a piecewise constant waveform
        - `...piecewise_linear([durations], [values])
            .poly([coefficients], duration)`: to append a polynomial waveform
        - `...piecewise_linear([durations], [values]).apply(waveform)`:
            to append a pre-defined waveform
        - `...piecewise_linear([durations], [values]).fn(f(t,...))`:
            to append a waveform defined by a python function
    - Slice a portion of the waveform to be used:
        - `...piecewise_linear([durations], [values])
            .slice(start, stop, duration)`
    - Save the ending value of your waveform to be reused elsewhere
        - `...piecewise_linear([durations], [values])
            .record("you_variable_here")`
    - Begin constructing another drive by starting a new spatial modulation
      (this drive will be summed to the one you just created):
        - `...piecewise_linear([durations], [values]).uniform`:
            To address all atoms in the field
        - `...piecewise_linear([durations], [values]).scale(...)`:
            To address an atom at a specific location via index
        - `...piecewise_linear([durations], [values]).location(int)`
            - To address an atom at a specific location via variable
            - To address multiple atoms at specific locations by
                specifying a single variable and then assigning it a
                list of coordinates
    - Assign values to pre-existing variables via:
        - `...piecewise_linear([durations], [values])
            .assign(variable_name = value)`:
            to assign a single value to a variable
        - `...piecewise_linear([durations], [values])
            .batch_assign(variable_name = [value1, ...])`:
            to assign multiple values to a variable
        - `...piecewise_linear([durations], [values])
            .args(["previously_defined_var"])`:
            to defer assignment of a variable to execution time
    - Select the backend you want your program to run on via:
        - `...piecewise_linear([durations], [values]).braket`:
            to run on Braket local emulator or QuEra hardware remotely
        - `...piecewise_linear([durations], [values]).bloqade`:
            to run on the Bloqade local emulator
        - `...piecewise_linear([durations], [values]).device`:
            to specify the backend via string
    - Choose to parallelize your atom geometry,
      duplicating it to fill the whole space:
        - `...piecewise_linear([durations], [values]).parallelize(spacing)`
    - Start targeting another level coupling
        - `...piecewise_linear([durations], [values]).rydberg`:
            to target the Rydberg level coupling
        - `...piecewise_linear([durations], [values]).hyperfine`:
            to target the Hyperfine level coupling
    - Start targeting other fields within your current level coupling
      (previously selected as `rydberg` or `hyperfine`):
        - `...piecewise_linear([durations], [values]).amplitude`:
            to target the real-valued Rabi Amplitude field
        - `...piecewise_linear([durations], [values]).phase`:
            to target the real-valued Rabi Phase field
        - `...piecewise_linear([durations], [values]).detuning`:
            to target the Detuning field
        - `....rabi`: to target the complex-valued Rabi field
    """
    return PiecewiseLinear(durations, values, self)

poly

poly(
    coeffs: List[ScalarType], duration: ScalarType
) -> Poly

Append or assign a waveform with a polynomial profile to current location(s).

You pass in a list of coefficients and a duration to this method which obeys the following expression:

wv(t) = coeffs[0] + coeffs[1]*t + coeffs[2]*t^2 + ... + coeffs[n]*t^n

If you specified a spatial modulation (e.g. uniform, location,scale) previously without a waveform you will now have completed the construction of a "drive", one or a sum of drives creating a "field" (e.g. Real-valued Rabi Amplitude/Phase).

If you have already specified a waveform previously you will now be appending this waveform to that previous waveform.

Usage Example:
>>> prog = start.add_position((0,0)).rydberg.detuning.uniform
>>> coeffs = [-1, 0.5, 1.2]
# resulting polynomial is:
# f(t) = -1 + 0.5*t + 1.2*t^2 with duration of
# 0.5 us
>>> prog.poly(coeffs, duration=0.5)
  • Your next steps include:
  • Continue building your waveform via:
    • ...poly([coeffs], duration).linear(start, stop, duration): to append another linear waveform
    • ...poly([coeffs], duration).constant(value, duration): to append a constant waveform
    • ...poly([coeffs], duration) .piecewise_linear([durations], [values]): to append a piecewise linear waveform
    • ...poly([coeffs], duration) .piecewise_constant([durations],[values]): to append a piecewise constant waveform
    • ...poly([coeffs], duration).poly([coefficients], duration): to append a polynomial waveform
    • ...poly([coeffs], duration).apply(waveform): to append a pre-defined waveform
    • ...poly([coeffs], duration).fn(f(t,...)): to append a waveform defined by a python function
  • Slice a portion of the waveform to be used:
    • ...poly([coeffs], duration).slice(start, stop, duration)
  • Save the ending value of your waveform to be reused elsewhere
    • ...poly([coeffs], duration).record("you_variable_here")
  • Begin constructing another drive by starting a new spatial modulation (this drive will be summed to the one you just created):
    • ...poly([coeffs], duration).uniform: To address all atoms in the field
    • ...poly([coeffs], duration).location(int): To address an atom at a specific location via index
    • ...poly([coeffs], duration).scale(...)
      • To address an atom at a specific location via variable
      • To address multiple atoms at specific locations by specifying a single variable and then assigning it a list of coordinates
  • Assign values to pre-existing variables via:
    • ...poly([coeffs], duration).assign(variable_name = value): to assign a single value to a variable
    • ...poly([coeffs], duration) .batch_assign(variable_name = [value1, ...]): to assign multiple values to a variable
    • ...poly([coeffs], duration).args(["previously_defined_var"]): to defer assignment of a variable to execution time
  • Select the backend you want your program to run on via:
    • ...poly([coeffs], duration).braket: to run on Braket local emulator or QuEra hardware remotely
    • ...poly([coeffs], duration).bloqade: to run on the Bloqade local emulator
    • ...poly([coeffs], duration).device: to specify the backend via string
  • Choose to parallelize your atom geometry, duplicating it to fill the whole space:
    • ...poly([coeffs], duration).parallelize(spacing)
  • Start targeting another level coupling
    • ...poly([coeffs], duration).rydberg: to target the Rydberg level coupling
    • ...poly([coeffs], duration).hyperfine: to target the Hyperfine level coupling
  • Start targeting other fields within your current level coupling (previously selected as rydberg or hyperfine):
    • ...poly([coeffs], duration).amplitude: to target the real-valued Rabi Amplitude field
    • ...poly([coeffs], duration).phase: to target the real-valued Rabi Phase field
    • ...poly([coeffs], duration).detuning: to target the Detuning field
    • ...poly([coeffs], duration).rabi: to target the complex-valued Rabi field
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/waveform.py
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
@beartype
def poly(self, coeffs: List[ScalarType], duration: ScalarType) -> "Poly":
    """
    Append or assign a waveform with a polynomial profile to current location(s).

    You pass in a list of coefficients and a duration to this method which obeys
    the following expression:

    `
    wv(t) = coeffs[0] + coeffs[1]*t + coeffs[2]*t^2 + ... + coeffs[n]*t^n
    `

    If you specified a spatial modulation (e.g. `uniform`, `location`,`scale`)
    previously without a waveform you will now have completed the construction
    of a "drive", one or a sum of drives creating a "field"
    (e.g. Real-valued Rabi Amplitude/Phase).

    If you have already specified a waveform previously you will now be appending
    this waveform to that previous waveform.

    ### Usage Example:
    ```
    >>> prog = start.add_position((0,0)).rydberg.detuning.uniform
    >>> coeffs = [-1, 0.5, 1.2]
    # resulting polynomial is:
    # f(t) = -1 + 0.5*t + 1.2*t^2 with duration of
    # 0.5 us
    >>> prog.poly(coeffs, duration=0.5)
    ```

    - Your next steps include:
    - Continue building your waveform via:
        - `...poly([coeffs], duration).linear(start, stop, duration)`:
            to append another linear waveform
        - `...poly([coeffs], duration).constant(value, duration)`:
            to append a constant waveform
        - `...poly([coeffs], duration)
            .piecewise_linear([durations], [values])`:
            to append a piecewise linear waveform
        - `...poly([coeffs], duration)
            .piecewise_constant([durations],[values])`:
            to append a piecewise constant waveform
        - `...poly([coeffs], duration).poly([coefficients], duration)`:
            to append a polynomial waveform
        - `...poly([coeffs], duration).apply(waveform)`:
            to append a pre-defined waveform
        - `...poly([coeffs], duration).fn(f(t,...))`:
            to append a waveform defined by a python function
    - Slice a portion of the waveform to be used:
        - `...poly([coeffs], duration).slice(start, stop, duration)`
    - Save the ending value of your waveform to be reused elsewhere
        - `...poly([coeffs], duration).record("you_variable_here")`
    - Begin constructing another drive by starting a new spatial modulation
      (this drive will be summed to the one you just created):
        - `...poly([coeffs], duration).uniform`:
            To address all atoms in the field
        - `...poly([coeffs], duration).location(int)`:
            To address an atom at a specific location via index
        - `...poly([coeffs], duration).scale(...)`
            - To address an atom at a specific location via variable
            - To address multiple atoms at specific locations by
                specifying a single variable and then assigning
                it a list of coordinates
    - Assign values to pre-existing variables via:
        - `...poly([coeffs], duration).assign(variable_name = value)`:
            to assign a single value to a variable
        - `...poly([coeffs], duration)
            .batch_assign(variable_name = [value1, ...])`:
            to assign multiple values to a variable
        - `...poly([coeffs], duration).args(["previously_defined_var"])`:
            to defer assignment of a variable to execution time
    - Select the backend you want your program to run on via:
        - `...poly([coeffs], duration).braket`:
            to run on Braket local emulator or QuEra hardware remotely
        - `...poly([coeffs], duration).bloqade`:
            to run on the Bloqade local emulator
        - `...poly([coeffs], duration).device`:
            to specify the backend via string
    - Choose to parallelize your atom geometry,
      duplicating it to fill the whole space:
        - `...poly([coeffs], duration).parallelize(spacing)`
    - Start targeting another level coupling
        - `...poly([coeffs], duration).rydberg`:
            to target the Rydberg level coupling
        - `...poly([coeffs], duration).hyperfine`:
            to target the Hyperfine level coupling
    - Start targeting other fields within your current level
      coupling (previously selected as `rydberg` or `hyperfine`):
        - `...poly([coeffs], duration).amplitude`:
            to target the real-valued Rabi Amplitude field
        - `...poly([coeffs], duration).phase`:
            to target the real-valued Rabi Phase field
        - `...poly([coeffs], duration).detuning`:
            to target the Detuning field
        - `...poly([coeffs], duration).rabi`:
            to target the complex-valued Rabi field
    """
    return Poly(coeffs, duration, self)

compiler

analysis

hardware

BasicLatticeValidation

BasicLatticeValidation(capabilities: QuEraCapabilities)

Bases: BloqadeIRVisitor

This visitor checks that the AtomArrangement is within the bounds of the lattice and that the number of sites is within the maximum number of sites.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/analysis/hardware/lattice.py
14
15
def __init__(self, capabilities: QuEraCapabilities):
    self.capabilities = capabilities

ValidateChannels

ValidateChannels()

Bases: BloqadeIRVisitor

Checks to make sure the given sequence can be compiled to hardware.

This check looks at the spatial modulations and the level coupling to determine if the sequence can be compiled to hardware.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/analysis/hardware/channels.py
18
19
def __init__(self):
    self.field_name = None

channels

ValidateChannels
ValidateChannels()

Bases: BloqadeIRVisitor

Checks to make sure the given sequence can be compiled to hardware.

This check looks at the spatial modulations and the level coupling to determine if the sequence can be compiled to hardware.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/analysis/hardware/channels.py
18
19
def __init__(self):
    self.field_name = None

lattice

BasicLatticeValidation
BasicLatticeValidation(capabilities: QuEraCapabilities)

Bases: BloqadeIRVisitor

This visitor checks that the AtomArrangement is within the bounds of the lattice and that the number of sites is within the maximum number of sites.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/analysis/hardware/lattice.py
14
15
def __init__(self, capabilities: QuEraCapabilities):
    self.capabilities = capabilities

codegen

hardware

PiecewiseLinear

PiecewiseLinear represents a piecewise linear function.

Contains methods for evaluating the function at a given time and slicing since these are common operations in the code generation process.

piecewise_linear

PiecewiseLinear

PiecewiseLinear represents a piecewise linear function.

Contains methods for evaluating the function at a given time and slicing since these are common operations in the code generation process.

passes

hardware

analyze_channels

analyze_channels(circuit: AnalogCircuit) -> Dict
  1. Scan channels

This pass checks to make sure that: * There is no hyperfine coupling in the sequence * There are no non-uniform spatial modulation for rabi phase and amplitude * there is no more than one non-uniform spatial modulation for detuning

Parameters:

Name Type Description Default
circuit AnalogCircuit

AnalogCircuit to analyze

required

Returns:

Name Type Description
level_couplings Dict

Dictionary containing the required channels for the sequence. Note that this will insert a uniform field for any missing channels.

Raises:

Type Description
ValueError

If there is hyperfine coupling in the sequence.

ValueError

If there is more than one non-uniform spatial modulation for detuning.

ValueError

If there are non-uniform spatial modulations for rabi phase and amplitude.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def analyze_channels(circuit: analog_circuit.AnalogCircuit) -> Dict:
    """1. Scan channels

    This pass checks to make sure that:
    * There is no hyperfine coupling in the sequence
    * There are no non-uniform spatial modulation for rabi phase and amplitude
    * there is no more than one non-uniform spatial modulation for detuning

    Args:
        circuit: AnalogCircuit to analyze

    Returns:
        level_couplings: Dictionary containing the required channels for the
            sequence. Note that this will insert a uniform field for any missing
            channels.

    Raises:
        ValueError: If there is hyperfine coupling in the sequence.
        ValueError: If there is more than one non-uniform spatial modulation for
            detuning.
        ValueError: If there are non-uniform spatial modulations for rabi phase
            and amplitude.

    """
    from bloqade.analog.compiler.analysis.common import ScanChannels
    from bloqade.analog.compiler.analysis.hardware import ValidateChannels

    ValidateChannels().scan(circuit)
    level_couplings = ScanChannels().scan(circuit)

    # add missing channels
    fields = level_couplings[sequence.rydberg]
    # detuning, phase and amplitude are required
    # to have at least a uniform field
    updated_fields = {
        field_name: fields.get(field_name, {field.Uniform}).union({field.Uniform})
        for field_name in [pulse.detuning, pulse.rabi.amplitude, pulse.rabi.phase]
    }

    return {sequence.rydberg: updated_fields}

assign_circuit

assign_circuit(
    circuit: AnalogCircuit,
    assignments: Dict[str, ParamType],
) -> Tuple[analog_circuit.AnalogCircuit, Dict]
  1. Assign variables and validate assignment

This pass assigns variables to the circuit and validates that all variables have been assigned.

Parameters:

Name Type Description Default
circuit AnalogCircuit

AnalogCircuit to assign variables to

required
assignments Dict[str, ParamType]

Dictionary containing the assignments for the variables in the circuit.

required

Returns:

Name Type Description
assigned_circuit Tuple[AnalogCircuit, Dict]

AnalogCircuit with variables assigned.

Raises:

Type Description
ValueError

If there are any variables that have not been assigned.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def assign_circuit(
    circuit: analog_circuit.AnalogCircuit, assignments: Dict[str, ParamType]
) -> Tuple[analog_circuit.AnalogCircuit, Dict]:
    """3. Assign variables and validate assignment

    This pass assigns variables to the circuit and validates that all variables
    have been assigned.

    Args:
        circuit: AnalogCircuit to assign variables to
        assignments: Dictionary containing the assignments for the variables in
            the circuit.

    Returns:
        assigned_circuit: AnalogCircuit with variables assigned.

    Raises:
        ValueError: If there are any variables that have not been assigned.

    """
    from bloqade.analog.compiler.rewrite.common import AssignBloqadeIR
    from bloqade.analog.compiler.analysis.common import ScanVariables, AssignmentScan

    final_assignments = AssignmentScan(assignments).scan(circuit)

    assigned_circuit = AssignBloqadeIR(final_assignments).visit(circuit)

    assignment_analysis = ScanVariables().scan(assigned_circuit)

    if not assignment_analysis.is_assigned:
        missing_vars = assignment_analysis.scalar_vars.union(
            assignment_analysis.vector_vars
        )
        raise ValueError(
            "Missing assignments for variables:\n"
            + ("\n".join(f"{var}" for var in missing_vars))
            + "\n"
        )

    return assigned_circuit, final_assignments

canonicalize_circuit

canonicalize_circuit(
    circuit: AnalogCircuit, level_couplings: Dict
) -> analog_circuit.AnalogCircuit
  1. Insert zero waveform in the explicit time intervals missing a waveform

This pass inserts a zero waveform in the explicit time intervals missing a waveform. This is required for later analysis passes to check that the waveforms are compatible with the hardware.

Parameters:

Name Type Description Default
circuit AnalogCircuit

AnalogCircuit to add padding to

required
level_couplings Dict

Dictionary containing the given channels for the sequence.

required

Return circuit: AnalogCircuit with zero waveforms inserted in the explicit time intervals missing a waveform.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def canonicalize_circuit(
    circuit: analog_circuit.AnalogCircuit, level_couplings: Dict
) -> analog_circuit.AnalogCircuit:
    """2. Insert zero waveform in the explicit time intervals missing a waveform

    This pass inserts a zero waveform in the explicit time intervals missing a
    waveform. This is required for later analysis passes to check that the
    waveforms are compatible with the hardware.

    Args:
        circuit: AnalogCircuit to add padding to
        level_couplings: Dictionary containing the given channels for the
            sequence.

    Return
        circuit: AnalogCircuit with zero waveforms inserted in the explicit time
            intervals missing a waveform.

    """
    from bloqade.analog.compiler.rewrite.common import (
        AddPadding,
        Canonicalizer,
        AssignToLiteral,
    )

    circuit = AddPadding(level_couplings).visit(circuit)
    # these two passes are equivalent to a constant propagation pass
    circuit = AssignToLiteral().visit(circuit)
    circuit = Canonicalizer().visit(circuit)

    return circuit

generate_ahs_code

generate_ahs_code(
    capabilities: Optional[QuEraCapabilities],
    level_couplings: Dict,
    circuit: AnalogCircuit,
) -> AHSComponents
  1. generate ahs code

Generates the AHS code for the given circuit. This includes generating the lattice data, global detuning, global amplitude, global phase, local detuning and lattice site coefficients (if applicable).

Parameters:

Name Type Description Default
capabilities QuEraCapabilities | None

Capabilities of the hardware.

required
level_couplings Dict

Dictionary containing the given channels for the sequence.

required
circuit AnalogCircuit

AnalogCircuit to generate AHS code for.

required

Returns:

Name Type Description
ahs_components AHSComponents

A collection of the AHS components generated for the given circuit. Can be used to generate the QuEra and Braket IR.

Raises:

Type Description
ValueError

If the capabilities are not provided but the circuit has a ParallelRegister. This is because the ParallelRegister requires the capabilities to generate the lattice data.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
def generate_ahs_code(
    capabilities: Optional[QuEraCapabilities],
    level_couplings: Dict,
    circuit: analog_circuit.AnalogCircuit,
) -> AHSComponents:
    """5. generate ahs code

    Generates the AHS code for the given circuit. This includes generating the
    lattice data, global detuning, global amplitude, global phase, local
    detuning and lattice site coefficients (if applicable).

    Args:
        capabilities (QuEraCapabilities | None): Capabilities of the hardware.
        level_couplings (Dict): Dictionary containing the given channels for the
            sequence.
        circuit (AnalogCircuit): AnalogCircuit to generate AHS code for.

    Returns:
        ahs_components (AHSComponents): A collection of the AHS components
            generated for the given circuit. Can be used to generate the QuEra
            and Braket IR.

    Raises:
        ValueError: If the capabilities are not provided but the circuit has
            a ParallelRegister. This is because the ParallelRegister requires
            the capabilities to generate the lattice data.

    """
    from bloqade.analog.compiler.codegen.hardware import (
        GenerateLattice,
        GeneratePiecewiseLinearChannel,
        GenerateLatticeSiteCoefficients,
        GeneratePiecewiseConstantChannel,
    )
    from bloqade.analog.compiler.analysis.hardware import BasicLatticeValidation

    if capabilities is not None:
        # only validate the lattice if capabilities are provided
        BasicLatticeValidation(capabilities).visit(circuit)

    ahs_lattice_data = GenerateLattice(capabilities).emit(circuit)

    global_detuning = GeneratePiecewiseLinearChannel(
        sequence.rydberg, pulse.detuning, field.Uniform
    ).visit(circuit)

    global_amplitude = GeneratePiecewiseLinearChannel(
        sequence.rydberg, pulse.rabi.amplitude, field.Uniform
    ).visit(circuit)

    global_phase = GeneratePiecewiseConstantChannel(
        sequence.rydberg, pulse.rabi.phase, field.Uniform
    ).visit(circuit)

    local_detuning = None
    lattice_site_coefficients = None

    extra_sm = set(level_couplings[sequence.rydberg][pulse.detuning]) - {field.Uniform}

    if extra_sm:
        if capabilities is not None and capabilities.capabilities.rydberg.local is None:
            raise ValueError(
                "Device does not support local detuning, but the program has a "
                "non-uniform spatial modulation for detuning."
            )

        sm = extra_sm.pop()

        lattice_site_coefficients = GenerateLatticeSiteCoefficients(
            parallel_decoder=ahs_lattice_data.parallel_decoder
        ).emit(circuit)

        local_detuning = GeneratePiecewiseLinearChannel(
            sequence.rydberg, pulse.detuning, sm
        ).visit(circuit)

    return AHSComponents(
        lattice_data=ahs_lattice_data,
        global_detuning=global_detuning,
        global_amplitude=global_amplitude,
        global_phase=global_phase,
        local_detuning=local_detuning,
        lattice_site_coefficients=lattice_site_coefficients,
    )

generate_braket_ir

generate_braket_ir(
    ahs_components: AHSComponents, shots: int
) -> BraketTaskSpecification
  1. generate braket ir

This pass takes the AHS components and generates the Braket IR.

Parameters:

Name Type Description Default
ahs_components AHSComponents

A collection of the AHS components generated for the given circuit.

required
shots int

Number of shots to run the circuit for.

required

Returns:

Name Type Description
task_specification BraketTaskSpecification

Braket IR for the given circuit.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
def generate_braket_ir(
    ahs_components: AHSComponents, shots: int
) -> BraketTaskSpecification:
    """7. generate braket ir

    This pass takes the AHS components and generates the Braket IR.

    Args:
        ahs_components (AHSComponents): A collection of the AHS components
            generated for the given circuit.
        shots (int): Number of shots to run the circuit for.

    Returns:
        task_specification (BraketTaskSpecification): Braket IR for the given
            circuit.

    """
    import braket.ir.ahs as ahs

    from bloqade.analog.compiler.passes.hardware.units import (
        convert_time_units,
        convert_energy_units,
        convert_coordinate_units,
    )

    ahs_register = ahs.AtomArrangement(
        sites=list(map(convert_coordinate_units, ahs_components.lattice_data.sites)),
        filling=ahs_components.lattice_data.filling,
    )

    global_detuning_time_series = ahs.TimeSeries(
        times=list(map(convert_time_units, ahs_components.global_detuning.times)),
        values=list(map(convert_energy_units, ahs_components.global_detuning.values)),
    )

    local_detuning_time_series = None
    if ahs_components.lattice_site_coefficients is not None:
        local_detuning_time_series = ahs.TimeSeries(
            times=list(map(convert_time_units, ahs_components.local_detuning.times)),
            values=list(
                map(convert_energy_units, ahs_components.local_detuning.values)
            ),
        )

    amplitude_time_series = ahs.TimeSeries(
        times=list(map(convert_time_units, ahs_components.global_amplitude.times)),
        values=list(map(convert_energy_units, ahs_components.global_amplitude.values)),
    )

    phase_time_series = ahs.TimeSeries(
        times=list(map(convert_time_units, ahs_components.global_phase.times)),
        values=ahs_components.global_phase.values,
    )

    detuning = ahs.PhysicalField(
        time_series=global_detuning_time_series,
        pattern="uniform",
    )

    amplitude = ahs.PhysicalField(
        time_series=amplitude_time_series,
        pattern="uniform",
    )

    phase = ahs.PhysicalField(
        time_series=phase_time_series,
        pattern="uniform",
    )

    local_detuning = None
    if ahs_components.lattice_site_coefficients is not None:
        local_detuning = ahs.PhysicalField(
            time_series=local_detuning_time_series,
            pattern=ahs_components.lattice_site_coefficients,
        )

    driving_field = ahs.DrivingField(
        detuning=detuning,
        amplitude=amplitude,
        phase=phase,
    )

    shiftingFields = []
    if ahs_components.lattice_site_coefficients is not None:
        shiftingFields = [ahs.ShiftingField(magnitude=local_detuning)]

    program = ahs.Program(
        setup=ahs.Setup(ahs_register=ahs_register),
        hamiltonian=ahs.Hamiltonian(
            drivingFields=[driving_field],
            shiftingFields=shiftingFields,
        ),
    )

    return BraketTaskSpecification(nshots=shots, program=program)

generate_quera_ir

generate_quera_ir(
    ahs_components: AHSComponents, shots: int
) -> QuEraTaskSpecification
  1. generate quera ir

This pass takes the AHS components and generates the QuEra IR.

Parameters:

Name Type Description Default
ahs_components AHSComponents

A collection of the AHS components generated for the given circuit.

required
shots int

Number of shots to run the circuit for.

required

Returns:

Name Type Description
task_specification QuEraTaskSpecification

QuEra IR for the given circuit.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
def generate_quera_ir(
    ahs_components: AHSComponents, shots: int
) -> QuEraTaskSpecification:
    """7. generate quera ir

    This pass takes the AHS components and generates the QuEra IR.

    Args:
        ahs_components (AHSComponents): A collection of the AHS components
            generated for the given circuit.
        shots (int): Number of shots to run the circuit for.

    Returns:
        task_specification (QuEraTaskSpecification): QuEra IR for the given
            circuit.

    """
    import bloqade.analog.submission.ir.task_specification as task_spec
    from bloqade.analog.compiler.passes.hardware.units import (
        convert_time_units,
        convert_energy_units,
        convert_coordinate_units,
    )

    lattice = task_spec.Lattice(
        sites=list(
            map(
                convert_coordinate_units,
                ahs_components.lattice_data.sites,
            )
        ),
        filling=ahs_components.lattice_data.filling,
    )

    global_detuning = task_spec.GlobalField(
        times=list(map(convert_time_units, ahs_components.global_detuning.times)),
        values=list(map(convert_energy_units, ahs_components.global_detuning.values)),
    )

    local_detuning = None

    if ahs_components.lattice_site_coefficients is not None:
        local_detuning = task_spec.LocalField(
            times=list(map(convert_time_units, ahs_components.local_detuning.times)),
            values=list(
                map(convert_energy_units, ahs_components.local_detuning.values)
            ),
            lattice_site_coefficients=ahs_components.lattice_site_coefficients,
        )

    rabi_frequency_amplitude_field = task_spec.GlobalField(
        times=list(map(convert_time_units, ahs_components.global_amplitude.times)),
        values=list(map(convert_energy_units, ahs_components.global_amplitude.values)),
    )

    rabi_frequency_phase_field = task_spec.GlobalField(
        times=list(map(convert_time_units, ahs_components.global_phase.times)),
        values=ahs_components.global_phase.values,
    )

    detuning = task_spec.Detuning(
        global_=global_detuning,
        local=local_detuning,
    )

    rabi_frequency_amplitude = task_spec.RabiFrequencyAmplitude(
        global_=rabi_frequency_amplitude_field,
    )

    rabi_frequency_phase = task_spec.RabiFrequencyPhase(
        global_=rabi_frequency_phase_field,
    )

    rydberg = task_spec.RydbergHamiltonian(
        rabi_frequency_amplitude=rabi_frequency_amplitude,
        rabi_frequency_phase=rabi_frequency_phase,
        detuning=detuning,
    )

    effective_hamiltonian = task_spec.EffectiveHamiltonian(
        rydberg=rydberg,
    )

    return task_spec.QuEraTaskSpecification(
        nshots=shots,
        lattice=lattice,
        effective_hamiltonian=effective_hamiltonian,
    )

validate_waveforms

validate_waveforms(
    level_couplings: Dict, circuit: AnalogCircuit
) -> None
  1. validate piecewise linear and piecewise constant pieces of pulses

This pass check to make sure that the waveforms are compatible with the hardware. This includes checking that the waveforms are piecewise linear or piecewise constant. It also checks that the waveforms are compatible with the given channels.

Parameters:

Name Type Description Default
circuit AnalogCircuit

AnalogCircuit to validate waveforms for

required
level_couplings Dict

Dictionary containing the given channels for the sequence.

required

Raises:

Type Description
ValueError

If the waveforms are not piecewise linear or piecewise constant, e.g. the waveform is not continuous.

ValueError

If a waveform segment is not compatible with the given channels.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
def validate_waveforms(
    level_couplings: Dict, circuit: analog_circuit.AnalogCircuit
) -> None:
    """4. validate piecewise linear and piecewise constant pieces of pulses

    This pass check to make sure that the waveforms are compatible with the
    hardware. This includes checking that the waveforms are piecewise linear or
    piecewise constant. It also checks that the waveforms are compatible with
    the given channels.

    Args:
        circuit: AnalogCircuit to validate waveforms for
        level_couplings: Dictionary containing the given channels for the
            sequence.

    Raises:
        ValueError: If the waveforms are not piecewise linear or piecewise
            constant, e.g. the waveform is not continuous.
        ValueError: If a waveform segment is not compatible with the given
            channels.

    """
    from bloqade.analog.compiler.analysis.common import CheckSlices
    from bloqade.analog.compiler.analysis.hardware import (
        ValidatePiecewiseLinearChannel,
        ValidatePiecewiseConstantChannel,
    )

    channel_iter = (
        (level_coupling, field_name, sm)
        for level_coupling, fields in level_couplings.items()
        for field_name, spatial_modulations in fields.items()
        for sm in spatial_modulations
    )
    for channel in channel_iter:
        if channel[1] in [pulse.detuning, pulse.rabi.amplitude]:
            ValidatePiecewiseLinearChannel(*channel).visit(circuit)
        else:
            ValidatePiecewiseConstantChannel(*channel).visit(circuit)

    CheckSlices().visit(circuit)

    if circuit.sequence.duration() == 0:
        raise ValueError("Circuit Duration must be be non-zero")

define

analyze_channels
analyze_channels(circuit: AnalogCircuit) -> Dict
  1. Scan channels

This pass checks to make sure that: * There is no hyperfine coupling in the sequence * There are no non-uniform spatial modulation for rabi phase and amplitude * there is no more than one non-uniform spatial modulation for detuning

Parameters:

Name Type Description Default
circuit AnalogCircuit

AnalogCircuit to analyze

required

Returns:

Name Type Description
level_couplings Dict

Dictionary containing the required channels for the sequence. Note that this will insert a uniform field for any missing channels.

Raises:

Type Description
ValueError

If there is hyperfine coupling in the sequence.

ValueError

If there is more than one non-uniform spatial modulation for detuning.

ValueError

If there are non-uniform spatial modulations for rabi phase and amplitude.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
def analyze_channels(circuit: analog_circuit.AnalogCircuit) -> Dict:
    """1. Scan channels

    This pass checks to make sure that:
    * There is no hyperfine coupling in the sequence
    * There are no non-uniform spatial modulation for rabi phase and amplitude
    * there is no more than one non-uniform spatial modulation for detuning

    Args:
        circuit: AnalogCircuit to analyze

    Returns:
        level_couplings: Dictionary containing the required channels for the
            sequence. Note that this will insert a uniform field for any missing
            channels.

    Raises:
        ValueError: If there is hyperfine coupling in the sequence.
        ValueError: If there is more than one non-uniform spatial modulation for
            detuning.
        ValueError: If there are non-uniform spatial modulations for rabi phase
            and amplitude.

    """
    from bloqade.analog.compiler.analysis.common import ScanChannels
    from bloqade.analog.compiler.analysis.hardware import ValidateChannels

    ValidateChannels().scan(circuit)
    level_couplings = ScanChannels().scan(circuit)

    # add missing channels
    fields = level_couplings[sequence.rydberg]
    # detuning, phase and amplitude are required
    # to have at least a uniform field
    updated_fields = {
        field_name: fields.get(field_name, {field.Uniform}).union({field.Uniform})
        for field_name in [pulse.detuning, pulse.rabi.amplitude, pulse.rabi.phase]
    }

    return {sequence.rydberg: updated_fields}
assign_circuit
assign_circuit(
    circuit: AnalogCircuit,
    assignments: Dict[str, ParamType],
) -> Tuple[analog_circuit.AnalogCircuit, Dict]
  1. Assign variables and validate assignment

This pass assigns variables to the circuit and validates that all variables have been assigned.

Parameters:

Name Type Description Default
circuit AnalogCircuit

AnalogCircuit to assign variables to

required
assignments Dict[str, ParamType]

Dictionary containing the assignments for the variables in the circuit.

required

Returns:

Name Type Description
assigned_circuit Tuple[AnalogCircuit, Dict]

AnalogCircuit with variables assigned.

Raises:

Type Description
ValueError

If there are any variables that have not been assigned.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def assign_circuit(
    circuit: analog_circuit.AnalogCircuit, assignments: Dict[str, ParamType]
) -> Tuple[analog_circuit.AnalogCircuit, Dict]:
    """3. Assign variables and validate assignment

    This pass assigns variables to the circuit and validates that all variables
    have been assigned.

    Args:
        circuit: AnalogCircuit to assign variables to
        assignments: Dictionary containing the assignments for the variables in
            the circuit.

    Returns:
        assigned_circuit: AnalogCircuit with variables assigned.

    Raises:
        ValueError: If there are any variables that have not been assigned.

    """
    from bloqade.analog.compiler.rewrite.common import AssignBloqadeIR
    from bloqade.analog.compiler.analysis.common import ScanVariables, AssignmentScan

    final_assignments = AssignmentScan(assignments).scan(circuit)

    assigned_circuit = AssignBloqadeIR(final_assignments).visit(circuit)

    assignment_analysis = ScanVariables().scan(assigned_circuit)

    if not assignment_analysis.is_assigned:
        missing_vars = assignment_analysis.scalar_vars.union(
            assignment_analysis.vector_vars
        )
        raise ValueError(
            "Missing assignments for variables:\n"
            + ("\n".join(f"{var}" for var in missing_vars))
            + "\n"
        )

    return assigned_circuit, final_assignments
canonicalize_circuit
canonicalize_circuit(
    circuit: AnalogCircuit, level_couplings: Dict
) -> analog_circuit.AnalogCircuit
  1. Insert zero waveform in the explicit time intervals missing a waveform

This pass inserts a zero waveform in the explicit time intervals missing a waveform. This is required for later analysis passes to check that the waveforms are compatible with the hardware.

Parameters:

Name Type Description Default
circuit AnalogCircuit

AnalogCircuit to add padding to

required
level_couplings Dict

Dictionary containing the given channels for the sequence.

required

Return circuit: AnalogCircuit with zero waveforms inserted in the explicit time intervals missing a waveform.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
def canonicalize_circuit(
    circuit: analog_circuit.AnalogCircuit, level_couplings: Dict
) -> analog_circuit.AnalogCircuit:
    """2. Insert zero waveform in the explicit time intervals missing a waveform

    This pass inserts a zero waveform in the explicit time intervals missing a
    waveform. This is required for later analysis passes to check that the
    waveforms are compatible with the hardware.

    Args:
        circuit: AnalogCircuit to add padding to
        level_couplings: Dictionary containing the given channels for the
            sequence.

    Return
        circuit: AnalogCircuit with zero waveforms inserted in the explicit time
            intervals missing a waveform.

    """
    from bloqade.analog.compiler.rewrite.common import (
        AddPadding,
        Canonicalizer,
        AssignToLiteral,
    )

    circuit = AddPadding(level_couplings).visit(circuit)
    # these two passes are equivalent to a constant propagation pass
    circuit = AssignToLiteral().visit(circuit)
    circuit = Canonicalizer().visit(circuit)

    return circuit
generate_ahs_code
generate_ahs_code(
    capabilities: Optional[QuEraCapabilities],
    level_couplings: Dict,
    circuit: AnalogCircuit,
) -> AHSComponents
  1. generate ahs code

Generates the AHS code for the given circuit. This includes generating the lattice data, global detuning, global amplitude, global phase, local detuning and lattice site coefficients (if applicable).

Parameters:

Name Type Description Default
capabilities QuEraCapabilities | None

Capabilities of the hardware.

required
level_couplings Dict

Dictionary containing the given channels for the sequence.

required
circuit AnalogCircuit

AnalogCircuit to generate AHS code for.

required

Returns:

Name Type Description
ahs_components AHSComponents

A collection of the AHS components generated for the given circuit. Can be used to generate the QuEra and Braket IR.

Raises:

Type Description
ValueError

If the capabilities are not provided but the circuit has a ParallelRegister. This is because the ParallelRegister requires the capabilities to generate the lattice data.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
def generate_ahs_code(
    capabilities: Optional[QuEraCapabilities],
    level_couplings: Dict,
    circuit: analog_circuit.AnalogCircuit,
) -> AHSComponents:
    """5. generate ahs code

    Generates the AHS code for the given circuit. This includes generating the
    lattice data, global detuning, global amplitude, global phase, local
    detuning and lattice site coefficients (if applicable).

    Args:
        capabilities (QuEraCapabilities | None): Capabilities of the hardware.
        level_couplings (Dict): Dictionary containing the given channels for the
            sequence.
        circuit (AnalogCircuit): AnalogCircuit to generate AHS code for.

    Returns:
        ahs_components (AHSComponents): A collection of the AHS components
            generated for the given circuit. Can be used to generate the QuEra
            and Braket IR.

    Raises:
        ValueError: If the capabilities are not provided but the circuit has
            a ParallelRegister. This is because the ParallelRegister requires
            the capabilities to generate the lattice data.

    """
    from bloqade.analog.compiler.codegen.hardware import (
        GenerateLattice,
        GeneratePiecewiseLinearChannel,
        GenerateLatticeSiteCoefficients,
        GeneratePiecewiseConstantChannel,
    )
    from bloqade.analog.compiler.analysis.hardware import BasicLatticeValidation

    if capabilities is not None:
        # only validate the lattice if capabilities are provided
        BasicLatticeValidation(capabilities).visit(circuit)

    ahs_lattice_data = GenerateLattice(capabilities).emit(circuit)

    global_detuning = GeneratePiecewiseLinearChannel(
        sequence.rydberg, pulse.detuning, field.Uniform
    ).visit(circuit)

    global_amplitude = GeneratePiecewiseLinearChannel(
        sequence.rydberg, pulse.rabi.amplitude, field.Uniform
    ).visit(circuit)

    global_phase = GeneratePiecewiseConstantChannel(
        sequence.rydberg, pulse.rabi.phase, field.Uniform
    ).visit(circuit)

    local_detuning = None
    lattice_site_coefficients = None

    extra_sm = set(level_couplings[sequence.rydberg][pulse.detuning]) - {field.Uniform}

    if extra_sm:
        if capabilities is not None and capabilities.capabilities.rydberg.local is None:
            raise ValueError(
                "Device does not support local detuning, but the program has a "
                "non-uniform spatial modulation for detuning."
            )

        sm = extra_sm.pop()

        lattice_site_coefficients = GenerateLatticeSiteCoefficients(
            parallel_decoder=ahs_lattice_data.parallel_decoder
        ).emit(circuit)

        local_detuning = GeneratePiecewiseLinearChannel(
            sequence.rydberg, pulse.detuning, sm
        ).visit(circuit)

    return AHSComponents(
        lattice_data=ahs_lattice_data,
        global_detuning=global_detuning,
        global_amplitude=global_amplitude,
        global_phase=global_phase,
        local_detuning=local_detuning,
        lattice_site_coefficients=lattice_site_coefficients,
    )
generate_braket_ir
generate_braket_ir(
    ahs_components: AHSComponents, shots: int
) -> BraketTaskSpecification
  1. generate braket ir

This pass takes the AHS components and generates the Braket IR.

Parameters:

Name Type Description Default
ahs_components AHSComponents

A collection of the AHS components generated for the given circuit.

required
shots int

Number of shots to run the circuit for.

required

Returns:

Name Type Description
task_specification BraketTaskSpecification

Braket IR for the given circuit.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
def generate_braket_ir(
    ahs_components: AHSComponents, shots: int
) -> BraketTaskSpecification:
    """7. generate braket ir

    This pass takes the AHS components and generates the Braket IR.

    Args:
        ahs_components (AHSComponents): A collection of the AHS components
            generated for the given circuit.
        shots (int): Number of shots to run the circuit for.

    Returns:
        task_specification (BraketTaskSpecification): Braket IR for the given
            circuit.

    """
    import braket.ir.ahs as ahs

    from bloqade.analog.compiler.passes.hardware.units import (
        convert_time_units,
        convert_energy_units,
        convert_coordinate_units,
    )

    ahs_register = ahs.AtomArrangement(
        sites=list(map(convert_coordinate_units, ahs_components.lattice_data.sites)),
        filling=ahs_components.lattice_data.filling,
    )

    global_detuning_time_series = ahs.TimeSeries(
        times=list(map(convert_time_units, ahs_components.global_detuning.times)),
        values=list(map(convert_energy_units, ahs_components.global_detuning.values)),
    )

    local_detuning_time_series = None
    if ahs_components.lattice_site_coefficients is not None:
        local_detuning_time_series = ahs.TimeSeries(
            times=list(map(convert_time_units, ahs_components.local_detuning.times)),
            values=list(
                map(convert_energy_units, ahs_components.local_detuning.values)
            ),
        )

    amplitude_time_series = ahs.TimeSeries(
        times=list(map(convert_time_units, ahs_components.global_amplitude.times)),
        values=list(map(convert_energy_units, ahs_components.global_amplitude.values)),
    )

    phase_time_series = ahs.TimeSeries(
        times=list(map(convert_time_units, ahs_components.global_phase.times)),
        values=ahs_components.global_phase.values,
    )

    detuning = ahs.PhysicalField(
        time_series=global_detuning_time_series,
        pattern="uniform",
    )

    amplitude = ahs.PhysicalField(
        time_series=amplitude_time_series,
        pattern="uniform",
    )

    phase = ahs.PhysicalField(
        time_series=phase_time_series,
        pattern="uniform",
    )

    local_detuning = None
    if ahs_components.lattice_site_coefficients is not None:
        local_detuning = ahs.PhysicalField(
            time_series=local_detuning_time_series,
            pattern=ahs_components.lattice_site_coefficients,
        )

    driving_field = ahs.DrivingField(
        detuning=detuning,
        amplitude=amplitude,
        phase=phase,
    )

    shiftingFields = []
    if ahs_components.lattice_site_coefficients is not None:
        shiftingFields = [ahs.ShiftingField(magnitude=local_detuning)]

    program = ahs.Program(
        setup=ahs.Setup(ahs_register=ahs_register),
        hamiltonian=ahs.Hamiltonian(
            drivingFields=[driving_field],
            shiftingFields=shiftingFields,
        ),
    )

    return BraketTaskSpecification(nshots=shots, program=program)
generate_quera_ir
generate_quera_ir(
    ahs_components: AHSComponents, shots: int
) -> QuEraTaskSpecification
  1. generate quera ir

This pass takes the AHS components and generates the QuEra IR.

Parameters:

Name Type Description Default
ahs_components AHSComponents

A collection of the AHS components generated for the given circuit.

required
shots int

Number of shots to run the circuit for.

required

Returns:

Name Type Description
task_specification QuEraTaskSpecification

QuEra IR for the given circuit.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
def generate_quera_ir(
    ahs_components: AHSComponents, shots: int
) -> QuEraTaskSpecification:
    """7. generate quera ir

    This pass takes the AHS components and generates the QuEra IR.

    Args:
        ahs_components (AHSComponents): A collection of the AHS components
            generated for the given circuit.
        shots (int): Number of shots to run the circuit for.

    Returns:
        task_specification (QuEraTaskSpecification): QuEra IR for the given
            circuit.

    """
    import bloqade.analog.submission.ir.task_specification as task_spec
    from bloqade.analog.compiler.passes.hardware.units import (
        convert_time_units,
        convert_energy_units,
        convert_coordinate_units,
    )

    lattice = task_spec.Lattice(
        sites=list(
            map(
                convert_coordinate_units,
                ahs_components.lattice_data.sites,
            )
        ),
        filling=ahs_components.lattice_data.filling,
    )

    global_detuning = task_spec.GlobalField(
        times=list(map(convert_time_units, ahs_components.global_detuning.times)),
        values=list(map(convert_energy_units, ahs_components.global_detuning.values)),
    )

    local_detuning = None

    if ahs_components.lattice_site_coefficients is not None:
        local_detuning = task_spec.LocalField(
            times=list(map(convert_time_units, ahs_components.local_detuning.times)),
            values=list(
                map(convert_energy_units, ahs_components.local_detuning.values)
            ),
            lattice_site_coefficients=ahs_components.lattice_site_coefficients,
        )

    rabi_frequency_amplitude_field = task_spec.GlobalField(
        times=list(map(convert_time_units, ahs_components.global_amplitude.times)),
        values=list(map(convert_energy_units, ahs_components.global_amplitude.values)),
    )

    rabi_frequency_phase_field = task_spec.GlobalField(
        times=list(map(convert_time_units, ahs_components.global_phase.times)),
        values=ahs_components.global_phase.values,
    )

    detuning = task_spec.Detuning(
        global_=global_detuning,
        local=local_detuning,
    )

    rabi_frequency_amplitude = task_spec.RabiFrequencyAmplitude(
        global_=rabi_frequency_amplitude_field,
    )

    rabi_frequency_phase = task_spec.RabiFrequencyPhase(
        global_=rabi_frequency_phase_field,
    )

    rydberg = task_spec.RydbergHamiltonian(
        rabi_frequency_amplitude=rabi_frequency_amplitude,
        rabi_frequency_phase=rabi_frequency_phase,
        detuning=detuning,
    )

    effective_hamiltonian = task_spec.EffectiveHamiltonian(
        rydberg=rydberg,
    )

    return task_spec.QuEraTaskSpecification(
        nshots=shots,
        lattice=lattice,
        effective_hamiltonian=effective_hamiltonian,
    )
validate_waveforms
validate_waveforms(
    level_couplings: Dict, circuit: AnalogCircuit
) -> None
  1. validate piecewise linear and piecewise constant pieces of pulses

This pass check to make sure that the waveforms are compatible with the hardware. This includes checking that the waveforms are piecewise linear or piecewise constant. It also checks that the waveforms are compatible with the given channels.

Parameters:

Name Type Description Default
circuit AnalogCircuit

AnalogCircuit to validate waveforms for

required
level_couplings Dict

Dictionary containing the given channels for the sequence.

required

Raises:

Type Description
ValueError

If the waveforms are not piecewise linear or piecewise constant, e.g. the waveform is not continuous.

ValueError

If a waveform segment is not compatible with the given channels.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/compiler/passes/hardware/define.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
def validate_waveforms(
    level_couplings: Dict, circuit: analog_circuit.AnalogCircuit
) -> None:
    """4. validate piecewise linear and piecewise constant pieces of pulses

    This pass check to make sure that the waveforms are compatible with the
    hardware. This includes checking that the waveforms are piecewise linear or
    piecewise constant. It also checks that the waveforms are compatible with
    the given channels.

    Args:
        circuit: AnalogCircuit to validate waveforms for
        level_couplings: Dictionary containing the given channels for the
            sequence.

    Raises:
        ValueError: If the waveforms are not piecewise linear or piecewise
            constant, e.g. the waveform is not continuous.
        ValueError: If a waveform segment is not compatible with the given
            channels.

    """
    from bloqade.analog.compiler.analysis.common import CheckSlices
    from bloqade.analog.compiler.analysis.hardware import (
        ValidatePiecewiseLinearChannel,
        ValidatePiecewiseConstantChannel,
    )

    channel_iter = (
        (level_coupling, field_name, sm)
        for level_coupling, fields in level_couplings.items()
        for field_name, spatial_modulations in fields.items()
        for sm in spatial_modulations
    )
    for channel in channel_iter:
        if channel[1] in [pulse.detuning, pulse.rabi.amplitude]:
            ValidatePiecewiseLinearChannel(*channel).visit(circuit)
        else:
            ValidatePiecewiseConstantChannel(*channel).visit(circuit)

    CheckSlices().visit(circuit)

    if circuit.sequence.duration() == 0:
        raise ValueError("Circuit Duration must be be non-zero")

rewrite

common

AssignToLiteral

Bases: BloqadeIRTransformer

Transform all assigned variables to literals.

assign_to_literal

AssignToLiteral

Bases: BloqadeIRTransformer

Transform all assigned variables to literals.

constants

RB_C6 module-attribute

RB_C6 = 2 * pi * 862690

The C6 constant for the Rydberg Interaction of two Rubidium atoms in units of: rad μm^6/μs

emulate

codegen

hamiltonian

CompileCache dataclass

CompileCache(
    operator_cache: Dict[
        Tuple[Register, LevelCoupling, OperatorData],
        MatrixTypes,
    ] = dict(),
    space_cache: Dict[
        Register, Tuple[Space, NDArray]
    ] = dict(),
)

This class is used to cache the results of the code generation.

ir

emulator

Register dataclass

Register(
    atom_type: AtomType,
    sites: List[Tuple[Decimal, Decimal]],
    blockade_radius: Decimal,
    geometry: Optional[Geometry] = None,
)

This class represents the of the atoms in the system.

state_vector

AnalogGate dataclass

AnalogGate(hamiltonian: RydbergHamiltonian)
run
run(
    shots: int = 1,
    solver_name: str = "dop853",
    atol: float = 1e-14,
    rtol: float = 1e-07,
    nsteps: int = 2147483647,
    interaction_picture: bool = False,
    project_hyperfine: bool = True,
) -> NDArray[np.uint8]

Run the emulation with all atoms in the ground state, sampling the final state vector.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/emulate/ir/state_vector.py
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
@beartype
def run(
    self,
    shots: int = 1,
    solver_name: str = "dop853",
    atol: float = 1e-14,
    rtol: float = 1e-7,
    nsteps: int = 2_147_483_647,
    interaction_picture: bool = False,
    project_hyperfine: bool = True,
) -> NDArray[np.uint8]:
    """Run the emulation with all atoms in the ground state,
    sampling the final state vector."""

    options = dict(
        solver_name=solver_name,
        atol=atol,
        rtol=rtol,
        nsteps=nsteps,
        interaction_picture=interaction_picture,
    )

    state = self.hamiltonian.space.zero_state()
    (result,) = self.apply(state, **options)
    result.normalize()

    return result.sample(shots, project_hyperfine=project_hyperfine)

RydbergHamiltonian dataclass

RydbergHamiltonian(
    emulator_ir: EmulatorProgram,
    space: Space,
    rydberg: NDArray,
    detuning_ops: List[DetuningOperator] = list(),
    rabi_ops: List[RabiOperator] = list(),
)

Hamiltonian for a given task. With the RydbergHamiltonian you can convert the Hamiltonian to CSR matrix form as well as obtaining the average energy/variance of a register.

Attributes:

Name Type Description
emulator_ir EmulatorProgram

A copy of the original program used to generate the RydbergHamiltonian

space Space

The Hilbert space of the Hamiltonian, should align with the register the Hamiltonian is being applied on for average energy/variance

rydberg NDArray

Rydberg interaction operator

detuning_ops List[DetuningOperator]

Detuning Operators of the Hamiltonian

rabi_ops List[RabiOperator]

Rabi Operators of the Hamiltonian

average
average(
    register: StateVector, time: Optional[float] = None
) -> float

Get energy average from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default
register StateVector

The state vector to take average with

required
time Optional[float]

Time value to evaluate average at.

None

Returns:

Name Type Description
float float

average energy at time time

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/emulate/ir/state_vector.py
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
@beartype
def average(
    self,
    register: StateVector,
    time: Optional[float] = None,
) -> float:
    """Get energy average from RydbergHamiltonian object at time `time` with
    register `register`

    Args:
        register (StateVector): The state vector to take average with
        time (Optional[float], optional): Time value to evaluate average at.
        Defaults to duration of RydbergHamiltonian.

    Returns:
        float: average energy at time `time`
    """
    return np.vdot(register.data, self._apply(register.data, time)).real
average_and_variance
average_and_variance(
    register: StateVector, time: Optional[float] = None
) -> Tuple[float, float]

Get energy average and variance from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default
register StateVector

The state vector to take average and variance with

required
time Optional[float]

Time value to evaluate average at.

None

Returns:

Type Description
float

Tuple[float, float]: average and variance of energy at time time

float

respectively.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/emulate/ir/state_vector.py
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
@beartype
def average_and_variance(
    self,
    register: StateVector,
    time: Optional[float] = None,
) -> Tuple[float, float]:
    """Get energy average and variance from RydbergHamiltonian object at time `time`
    with register `register`

    Args:
        register (StateVector): The state vector to take average and variance with
        time (Optional[float], optional): Time value to evaluate average at.
        Defaults to duration of RydbergHamiltonian.

    Returns:
        Tuple[float, float]: average and variance of energy at time `time`
        respectively.
    """
    H_register_data = self._apply(register.data, time)

    average = np.vdot(register.data, H_register_data).real
    square_average = np.vdot(H_register_data, H_register_data).real

    return average, square_average - average**2
tocsr
tocsr(time: float) -> csr_matrix

Return the Hamiltonian as a csr matrix at time time.

Parameters:

Name Type Description Default
time float

time to evaluate the Hamiltonian at.

required

Returns:

Name Type Description
csr_matrix csr_matrix

The Hamiltonian as a csr matrix.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/emulate/ir/state_vector.py
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
def tocsr(self, time: float) -> csr_matrix:
    """Return the Hamiltonian as a csr matrix at time `time`.

    Args:
        time (float): time to evaluate the Hamiltonian at.

    Returns:
        csr_matrix: The Hamiltonian as a csr matrix.

    """
    diagonal = sum(
        (detuning.get_diagonal(time) for detuning in self.detuning_ops),
        start=self.rydberg,
    )

    hamiltonian = diags(diagonal).tocsr()
    for rabi_op in self.rabi_ops:
        hamiltonian = hamiltonian + rabi_op.tocsr(time)

    return hamiltonian
variance
variance(
    register: StateVector, time: Optional[float] = None
) -> float

Get the energy variance from RydbergHamiltonian object at time time with register register

Parameters:

Name Type Description Default
register StateVector

The state vector to take variance with

required
time Optional[float]

Time value to evaluate average at.

None

Returns:

Name Type Description
complex float

variance of energy at time time respectively.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/emulate/ir/state_vector.py
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
@beartype
def variance(
    self,
    register: StateVector,
    time: Optional[float] = None,
) -> float:
    """Get the energy variance from RydbergHamiltonian object at
    time `time` with register `register`

    Args:
        register (StateVector): The state vector to take variance with
        time (Optional[float], optional): Time value to evaluate average at.
        Defaults to duration of RydbergHamiltonian.

    Returns:
        complex: variance of energy at time `time` respectively.
    """

    _, var = self.average_and_variance(register, time)
    return var

StateVector dataclass

StateVector(data: NDArray, space: Space)
local_trace
local_trace(
    matrix: ndarray, site_index: Union[int, Tuple[int, int]]
) -> complex

return trace of an operator over the StateVector.

Parameters:

Name Type Description Default
matrix ndarray

Square matrix representing operator in the local hilbert space.

required
site_index int | Tuple[int, int]

sites to apply one body operator to.

required

Returns:

Name Type Description
complex complex

the trace of the operator over the state-vector.

Raises:

Type Description
ValueError

Error is raised when the dimension of operator is not

ValueError

Error is raised when the site argument is out of bounds.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/emulate/ir/state_vector.py
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
@plum.dispatch
def local_trace(  # noqa: F811
    self, matrix: np.ndarray, site_index: Union[int, Tuple[int, int]]
) -> complex:  # noqa: F811
    """return trace of an operator over the StateVector.

    Args:
        matrix (np.ndarray): Square matrix representing operator in the local
            hilbert space.
        site_index (int | Tuple[int, int]): sites to apply one body operator to.

    Returns:
        complex: the trace of the operator over the state-vector.

    Raises:
        ValueError: Error is raised when the dimension of `operator` is not
        consistent with `site` argument. The size of the operator must fit
        the size of the local hilbert space of `site` depending on the number
        of sites and the number of levels inside each atom, e.g. for two site
        expectation value with a three level atom the operator must be a 9 by
        9 array.

        ValueError: Error is raised when the `site` argument is out of bounds.

    """
    ...
norm
norm() -> float

Return the norm of the state vector.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/emulate/ir/state_vector.py
215
216
217
def norm(self) -> float:
    """Return the norm of the state vector."""
    return np.linalg.norm(self.data)
normalize
normalize() -> None

Normalize the state vector.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/emulate/ir/state_vector.py
210
211
212
213
def normalize(self) -> None:
    """Normalize the state vector."""
    data = self.data
    data /= np.linalg.norm(data)
sample
sample(
    shots: int, project_hyperfine: bool = True
) -> NDArray

Sample the state vector and return bitstrings.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/emulate/ir/state_vector.py
204
205
206
207
208
def sample(self, shots: int, project_hyperfine: bool = True) -> NDArray:
    """Sample the state vector and return bitstrings."""
    return self.space.sample_state_vector(
        self.data, shots, project_hyperfine=project_hyperfine
    )

factory

constant

constant(
    duration: ScalarType, value: ScalarType
) -> Constant

Create a Constant waveform.

Parameters:

Name Type Description Default
duration ScalarType

Duration of the Constant waveform.

required
value ScalarType

Value of the Constant waveform.s

required

Returns:

Name Type Description
Constant Constant

A Constant waveform.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/factory.py
57
58
59
60
61
62
63
64
65
66
67
68
@beartype
def constant(duration: ScalarType, value: ScalarType) -> Constant:
    """Create a Constant waveform.

    Args:
        duration (ScalarType): Duration of the Constant waveform.
        value (ScalarType): Value of the Constant waveform.s

    Returns:
        Constant: A Constant waveform.
    """
    return Constant(value, duration)

get_capabilities

get_capabilities(
    use_experimental: bool = False,
) -> QuEraCapabilities

Get the device capabilities for Aquila

Parameters:

Name Type Description Default
use_experimental bool

Get experimental capabilities instead of standard ones. By default value is False.

False

Returns:

Name Type Description
QuEraCapabilities QuEraCapabilities

capabilities object for Aquila device.

Note

Units of time, distance, and energy are microseconds (us), micrometers (um), and rad / us, respectively.

For a comprehensive list of capabilities, see the Hardware Reference page

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/factory.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def get_capabilities(use_experimental: bool = False) -> "QuEraCapabilities":
    """Get the device capabilities for Aquila

    Args:
        use_experimental (bool): Get experimental capabilities instead of
            standard ones. By default value is False.

    Returns:
        QuEraCapabilities: capabilities object for Aquila device.


    Note:
        Units of time, distance, and energy are microseconds (us),
        micrometers (um), and rad / us, respectively.

        For a comprehensive list of capabilities,
        see the [Hardware Reference](../../reference/hardware-capabilities.md)
        page
    """

    from bloqade.analog.submission.capabilities import get_capabilities

    # manually convert to units
    return get_capabilities(use_experimental=use_experimental).scale_units(
        Decimal("1e6"), Decimal("1e-6")
    )

linear

linear(
    duration: ScalarType,
    start: ScalarType,
    stop: ScalarType,
) -> Linear

Create a Linear waveform.

Parameters:

Name Type Description Default
duration ScalarType

Duration of linear waveform

required
start ScalarType

Starting value of linear waveform

required
stop ScalarType

Ending value of linear waveform

required

Returns:

Name Type Description
Linear Linear

Linear waveform

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/factory.py
42
43
44
45
46
47
48
49
50
51
52
53
54
@beartype
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)

piecewise_constant

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].

Parameters:

Name Type Description Default
durations List[ScalarType]

The duration of each segment

required
values List[ScalarType]

The values for each segment

required

Raises:

Type Description
ValueError

If the length of values is not the same as the length of

Returns:

Name Type Description
Waveform Waveform

The piecewise linear waveform.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/factory.py
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
@beartype
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

    Raises:
        ValueError: If the length of `values` is not the same as the length of
        `durations`.

    Returns:
        Waveform: The piecewise linear waveform.
    """
    if len(durations) != len(values):
        raise ValueError(
            "The length of values must be the same as the length of durations"
        )

    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

piecewise_linear

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].

Parameters:

Name Type Description Default
durations List[ScalarType]

The duration of each segment

required
values List[ScalarType]

The values for each segment

required

Raises:

Type Description
ValueError

If the length of values is not one greater than the length of

Returns:

Name Type Description
Waveform Waveform

The piecewise linear waveform.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/factory.py
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
@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:
            pwl_wf = Linear(start, stop, duration)
        else:
            pwl_wf = pwl_wf.append(Linear(start, stop, duration))

    return pwl_wf

rydberg_h

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] = [],
) -> Routine

Create a rydberg program with uniform detuning, amplitude, and phase.

Parameters:

Name Type Description Default
atoms_positions Any

Description of geometry of atoms in system.

required
detuning Optional[Waveform]

Waveform for detuning. Defaults to None.

None
amplitude Optional[Waveform]

Waveform describing the amplitude of the rabi term. Defaults to None.

None
phase Optional[Waveform]

Waveform describing the phase of rabi term. Defaults to None.

None
static_params Dict[str, Any]

Define static parameters of your program. Defaults to {}.

{}
batch_params Union[List[Dict[str, Any]], Dict[str, Any]]

Parmaters for a batch of tasks. Defaults to [].

[]
args List[str]

List of arguments to leave till runtime. Defaults to [].

[]

Returns:

Name Type Description
Routine Routine

An object that can be used to dispatch a rydberg program to multiple backends.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/factory.py
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
@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] = [],
) -> 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.analog import start
    from bloqade.analog.atom_arrangement import AtomArrangement

    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)

    if amplitude is not None:
        prog = prog.amplitude.uniform.apply(amplitude)

    if phase is not None:
        prog = prog.phase.uniform.apply(phase)

    prog = prog.assign(**static_params)

    if isinstance(batch_params, dict):
        prog = prog.batch_assign(**batch_params)
    else:
        prog = prog.batch_assign(batch_params)

    prog = prog.args(args)

    return prog.parse()

ir

start module-attribute

start = ListOfLocations()

A Program starting point, alias of empty [ListOfLocations][bloqade.ir.location.list.ListOfLocations].

  • Next possible steps to build your program are:
  • Specify which level coupling to address with:
    • start.rydberg: for [Rydberg][bloqade.builder.coupling.Rydberg] Level coupling
    • start.hyperfine: for [Hyperfine][bloqade.builder.coupling.Hyperfine] Level coupling
    • LOCKOUT: You cannot add atoms to your geometry after specifying level coupling.
  • continue/start building your geometry with:
    • start.add_position(): to add atom(s) to current register. It will accept:
      • A single coordinate, represented as a tuple (e.g. (5,6)) with a value that can either be:
        • integers: (5,6)
        • floats: (5.1, 2.5)
        • strings (for later variable assignment): ("x", "y")
        • [Scalar][bloqade.ir.scalar.Scalar] objects: (2*cast("x"), 5+cast("y"))
      • A list of coordinates, represented as a list of types mentioned previously.
      • A numpy array with shape (n, 2) where n is the total number of atoms

AlignedWaveform

Bases: Waveform

<padded waveform> ::= <waveform> | <waveform> <alignment> <value>

<alignment> ::= 'left aligned' | 'right aligned'
<value> ::= 'left value' | 'right value' | <scalar expr>

AnalogCircuit

AnalogCircuit is a dummy type that bundle register and sequence together.

register property

register

Get the register of the program.

Returns:

Type Description

register (Union["AtomArrangement", "ParallelRegister"])

Note

If the program is built with [parallelize()][bloqade.builder.emit.Emit.parallelize], The the register will be a [ParallelRegister][bloqade.ir.location.base.ParallelRegister]. Otherwise it will be a [AtomArrangement][bloqade.ir.location.base.AtomArrangement].

show

show(**assignments)

Interactive visualization of the program

Parameters:

Name Type Description Default
**assignments

assigning the instance value (literal) to the existing variables in the program

{}
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/analog_circuit.py
122
123
124
125
126
127
128
129
130
def show(self, **assignments):
    """Interactive visualization of the program

    Args:
        **assignments: assigning the instance value (literal) to the
            existing variables in the program

    """
    display_ir(self, assignments)

AtomArrangement

AtomArrangement(parent: Optional[Builder] = None)

Bases: ProgramStart

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

n_atoms property

n_atoms: int

number of atoms (filled sites) in the register.

n_dims property

n_dims: int

number of dimensions in the register.

n_sites property

n_sites: int

number of sites in the register.

n_vacant property

n_vacant: int

number of vacant sites in the register.

add_position

add_position(
    position: Union[
        PositionArray,
        List[Tuple[ScalarType, ScalarType]],
        Tuple[ScalarType, ScalarType],
    ],
    filling: Optional[
        Union[BoolArray, List[bool], bool]
    ] = None,
) -> ListOfLocations

Add a position or multiple positions to a pre-existing geometry.

add_position is capable of accepting: - A single tuple for one atom coordinate: (1.0, 2.5) - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.] - A numpy array of shape (N, 2) where N is the number of atoms

You may also intersperse variables anywhere a value may be present.

You can also pass in an optional argument which determines the atom "filling" (whether or not at a specified coordinate an atom should be present).

Usage Example:
# single coordinate
>>> reg = start.add_position((0,0))
# you may chain add_position calls
>>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])
# you can add variables anywhere a value may be present
>>> reg_with_var = reg_plus_two.add_position(("x", "y"))
# and specify your atom fillings
>>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],
[True, False])
# alternatively you could use one boolean to specify
# all coordinates should be empty/filled
>>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),
(5.2, 2.2)], False)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
def add_position(
    self,
    position: Union[
        PositionArray,
        List[Tuple[ScalarType, ScalarType]],
        Tuple[ScalarType, ScalarType],
    ],
    filling: Optional[Union[BoolArray, List[bool], bool]] = None,
) -> "ListOfLocations":
    """
    Add a position or multiple positions to a pre-existing geometry.

    `add_position` is capable of accepting:
    - A single tuple for one atom coordinate: `(1.0, 2.5)`
    - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.]
    - A numpy array of shape (N, 2) where N is the number of atoms

    You may also intersperse variables anywhere a value may be present.

    You can also pass in an optional argument which determines the atom "filling"
    (whether or not at a specified coordinate an atom should be present).

    ### Usage Example:
    ```
    # single coordinate
    >>> reg = start.add_position((0,0))
    # you may chain add_position calls
    >>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])
    # you can add variables anywhere a value may be present
    >>> reg_with_var = reg_plus_two.add_position(("x", "y"))
    # and specify your atom fillings
    >>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],
    [True, False])
    # alternatively you could use one boolean to specify
    # all coordinates should be empty/filled
    >>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),
    (5.2, 2.2)], False)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...add_position(positions).add_position(positions)`:
            to add more positions
        - `...add_position(positions).apply_defect_count(n_defects)`:
        to randomly drop out n_atoms
        - `...add_position(positions).apply_defect_density(defect_probability)`:
        to drop out atoms with a certain probability
        - `...add_position(positions).scale(scale)`: to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...add_position(positions).rydberg`: to specify Rydberg coupling
        - `...add_position(positions).hyperfine`: to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...add_position(positions).show()`:
        shows your geometry in your web browser

    """

    if is_bearable(position, PositionArray) and is_bearable(
        filling, Optional[BoolArray]
    ):
        return self.add_position_ndarray(position, filling)
    elif is_bearable(position, List[Tuple[ScalarType, ScalarType]]) and is_bearable(
        filling, Optional[List[bool]]
    ):
        return self.add_position_list_tuples(position, filling)
    elif is_bearable(position, Tuple[ScalarType, ScalarType]) and is_bearable(
        filling, Optional[bool]
    ):
        return self.add_position_single_tupe(position, filling)
    else:
        raise TypeError("Invalid input types for add_position provided!")

apply_defect_count

apply_defect_count(
    n_defects: int, rng: Generator = np.random.default_rng()
)

Drop n_defects atoms from the geometry randomly. Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

Usage Example:
>>> from bloqade.analog.atom_arrangement import Chain
>>> import numpy as np
# set a custom seed for a numpy-based RNG
>>> custom_rng = np.random.default_rng(888)
# randomly remove two atoms from the geometry
>>> reg = Chain(11).apply_defect_count(2, custom_rng)
# you may also chain apply_defect_count calls
>>> reg.apply_defect_count(2, custom_rng)
# you can also use apply_defect_count on custom geometries
>>> from bloqade import start
>>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts) .apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
@beartype
def apply_defect_count(
    self, n_defects: int, rng: np.random.Generator = np.random.default_rng()
):
    """
    Drop `n_defects` atoms from the geometry randomly. Internally this occurs
    by setting certain sites to have a SiteFilling set to false indicating
    no atom is present at the coordinate.

    A default numpy-based Random Number Generator is used but you can
    explicitly override this by passing in your own.

    ### Usage Example:

    ```
    >>> from bloqade.analog.atom_arrangement import Chain
    >>> import numpy as np
    # set a custom seed for a numpy-based RNG
    >>> custom_rng = np.random.default_rng(888)
    # randomly remove two atoms from the geometry
    >>> reg = Chain(11).apply_defect_count(2, custom_rng)
    # you may also chain apply_defect_count calls
    >>> reg.apply_defect_count(2, custom_rng)
    # you can also use apply_defect_count on custom geometries
    >>> from bloqade import start
    >>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...apply_defect_count(defect_counts).add_position(positions)`:
            to add more positions
        - `...apply_defect_count(defect_counts)
            .apply_defect_count(n_defects)`: to randomly drop out n_atoms
        - `...apply_defect_count(defect_counts)
            .apply_defect_density(defect_probability)`:
            to drop out atoms with a certain probability
        - `...apply_defect_count(defect_counts).scale(scale)`:
            to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...apply_defect_count(defect_counts).rydberg`: to specify
            Rydberg coupling
        - `...apply_defect_count(defect_counts).hyperfine`:
            to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...apply_defect_count(defect_counts).show()`:
            shows your geometry in your web browser
    """

    location_list = []
    for location_info in self.enumerate():
        location_list.append(location_info)

    filled_sites = []

    for index, location_info in enumerate(location_list):
        if location_info.filling is SiteFilling.filled:
            filled_sites.append(index)

    if n_defects >= len(filled_sites):
        raise ValueError(
            f"n_defects {n_defects} must be less than the number of filled sites "
            f"({len(filled_sites)})"
        )

    for _ in range(n_defects):
        index = rng.choice(filled_sites)
        location_list[index] = LocationInfo.create(
            location_list[index].position,
            (False if location_list[index].filling is SiteFilling.filled else True),
        )
        filled_sites.remove(index)

    return ListOfLocations(location_list)

apply_defect_density

apply_defect_density(
    defect_probability: float,
    rng: Generator = np.random.default_rng(),
)

Drop atoms randomly with defect_probability probability (range of 0 to 1). Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

Usage Example:
>>> from bloqade.analog.atom_arrangement import Chain
>>> import numpy as np
# set a custom seed for a numpy-based RNG
>>> custom_rng = np.random.default_rng(888)
# randomly remove two atoms from the geometry
>>> reg = Chain(11).apply_defect_density(0.2, custom_rng)
# you may also chain apply_defect_density calls
>>> reg.apply_defect_count(0.1, custom_rng)
# you can also use apply_defect_density on custom geometries
>>> from bloqade import start
>>> start.add_position([(0,0), (1,1)])
.apply_defect_density(0.5, custom_rng)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
@beartype
def apply_defect_density(
    self,
    defect_probability: float,
    rng: np.random.Generator = np.random.default_rng(),
):
    """
    Drop atoms randomly with `defect_probability` probability (range of 0 to 1).
    Internally this occurs by setting certain sites to have a SiteFilling
    set to false indicating no atom is present at the coordinate.

    A default numpy-based Random Number Generator is used but you can
    explicitly override this by passing in your own.

    ### Usage Example:

    ```
    >>> from bloqade.analog.atom_arrangement import Chain
    >>> import numpy as np
    # set a custom seed for a numpy-based RNG
    >>> custom_rng = np.random.default_rng(888)
    # randomly remove two atoms from the geometry
    >>> reg = Chain(11).apply_defect_density(0.2, custom_rng)
    # you may also chain apply_defect_density calls
    >>> reg.apply_defect_count(0.1, custom_rng)
    # you can also use apply_defect_density on custom geometries
    >>> from bloqade import start
    >>> start.add_position([(0,0), (1,1)])
    .apply_defect_density(0.5, custom_rng)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...apply_defect_count(defect_counts).add_position(positions)`:
        to add more positions
        - `...apply_defect_count(defect_counts).apply_defect_count(n_defects)`:
        to randomly drop out n_atoms
        - `...apply_defect_count(defect_counts)
        .apply_defect_density(defect_probability)`:
        to drop out atoms with a certain probability
        - `...apply_defect_count(defect_counts).scale(scale)`:
        to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...apply_defect_count(defect_counts).rydberg`:
        to specify Rydberg coupling
        - `...apply_defect_count(defect_counts).hyperfine`:
        to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...apply_defect_count(defect_counts).show()`:
        shows your geometry in your web browser
    """

    p = min(1, max(0, defect_probability))
    location_list = []

    for location_info in self.enumerate():
        if rng.random() < p:
            location_list.append(
                LocationInfo.create(
                    location_info.position,
                    (
                        False
                        if location_info.filling is SiteFilling.filled
                        else True
                    ),
                )
            )
        else:
            location_list.append(location_info)

    return ListOfLocations(location_list=location_list)

enumerate

enumerate() -> Generator[LocationInfo, None, None]

enumerate all locations in the register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
128
129
130
def enumerate(self) -> Generator[LocationInfo, None, None]:
    """enumerate all locations in the register."""
    raise NotImplementedError

figure

figure(fig_kwargs=None, **assignments)

obtain a figure object from the atom arrangement.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
132
133
134
def figure(self, fig_kwargs=None, **assignments):
    """obtain a figure object from the atom arrangement."""
    return get_atom_arrangement_figure(self, fig_kwargs=fig_kwargs, **assignments)

rydberg_interaction

rydberg_interaction(**assignments) -> NDArray

calculate the Rydberg interaction matrix.

Parameters:

Name Type Description Default
**assignments

the values to assign to the variables in the register.

{}

Returns:

Name Type Description
NDArray NDArray

the Rydberg interaction matrix in the lower triangular form.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
def rydberg_interaction(self, **assignments) -> NDArray:
    """calculate the Rydberg interaction matrix.

    Args:
        **assignments: the values to assign to the variables in the register.

    Returns:
        NDArray: the Rydberg interaction matrix in the lower triangular form.

    """

    from bloqade.analog.constants import RB_C6

    # calculate the Interaction matrix
    V_ij = np.zeros((self.n_sites, self.n_sites))
    for i, site_i in enumerate(self.enumerate()):
        pos_i = np.array([float(ele(**assignments)) for ele in site_i.position])

        for j, site_j in enumerate(self.enumerate()):
            if j >= i:
                break  # enforce lower triangular form

            pos_j = np.array([float(ele(**assignments)) for ele in site_j.position])
            r_ij = np.linalg.norm(pos_i - pos_j)

            V_ij[i, j] = RB_C6 / r_ij**6

    return V_ij

scale

scale(scale: ScalarType)

Scale the geometry of your atoms.

Usage Example:
>>> reg = start.add_position([(0,0), (1,1)])
# atom positions are now (0,0), (2,2)
>>> new_reg = reg.scale(2)
# you may also use scale on pre-defined geometries
>>> from bloqade.analog.atom_arrangement import Chain
# atoms in the chain will now be 2 um apart versus
# the default 1 um
>>> Chain(11).scale(2)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
@beartype
def scale(self, scale: ScalarType):
    """
    Scale the geometry of your atoms.

    ### Usage Example:
    ```
    >>> reg = start.add_position([(0,0), (1,1)])
    # atom positions are now (0,0), (2,2)
    >>> new_reg = reg.scale(2)
    # you may also use scale on pre-defined geometries
    >>> from bloqade.analog.atom_arrangement import Chain
    # atoms in the chain will now be 2 um apart versus
    # the default 1 um
    >>> Chain(11).scale(2)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...add_position(positions).add_position(positions)`:
            to add more positions
        - `...add_position(positions).apply_defect_count(n_defects)`:
        to randomly drop out n_atoms
        - `...add_position(positions).apply_defect_density(defect_probability)`:
        to drop out atoms with a certain probability
        - `...add_position(positions).scale(scale)`: to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...add_position(positions).rydberg`:
        to specify Rydberg coupling
        - `...add_position(positions).hyperfine`:
        to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...add_position(positions).show()`:
        shows your geometry in your web browser

    """

    scale = cast(scale)
    location_list = []
    for location_info in self.enumerate():
        x, y = location_info.position
        new_position = (scale * x, scale * y)
        location_list.append(
            LocationInfo.create(new_position, bool(location_info.filling.value))
        )

    return ListOfLocations(location_list)

show

show(**assignments) -> None

Display the current program being defined with the given arguments and batch ID.

Parameters:

Name Type Description Default
*args

Additional arguments for display.

()
batch_id int

The batch ID to be displayed. Defaults to 0.

0
Note

This method uses the display_builder function to render the builder's state.

Example:

>>> class MyBuilder(Show):
...     pass
>>> builder = MyBuilder()
>>> builder.show()
>>> builder.show(batch_id=1)
>>> builder.show('arg1', 'arg2', batch_id=2)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
136
137
def show(self, **assignments) -> None:
    display_ir(self, assignments)

BoundedBravais

BoundedBravais(parent: Optional[Builder] = None)

Bases: AtomArrangement

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

__match_args__ class-attribute instance-attribute

__match_args__ = ('shape', 'lattice_spacing')

Base classe for Bravais lattices [AtomArrangement][bloqade.ir.location.base.AtomArrangement].

  • [Square][bloqade.ir.location.bravais.Square]
  • [Chain][bloqade.ir.location.bravais.Chain]
  • [Honeycomb][bloqade.ir.location.bravais.Honeycomb]
  • [Triangular][bloqade.ir.location.bravais.Triangular]
  • [Lieb][bloqade.ir.location.bravais.Lieb]
  • [Kagome][bloqade.ir.location.bravais.Kagome]
  • [Rectangular][bloqade.ir.location.bravais.Rectangular]

n_atoms cached property

n_atoms: int

number of atoms (filled sites) in the register.

n_dims property

n_dims

dimension of the lattice

Returns:

Name Type Description
int

dimension of the lattice

n_sites property

n_sites: int

number of sites in the register.

n_vacant property

n_vacant: int

number of vacant sites in the register.

coordinates

coordinates(index: List[int]) -> NDArray

calculate the coordinates of a cell in the lattice given the cell index.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
113
114
115
116
117
118
119
120
121
122
@beartype
def coordinates(self, index: List[int]) -> NDArray:
    """calculate the coordinates of a cell in the lattice
    given the cell index.
    """
    # damn! this is like stone age broadcasting
    vectors = np.array(self.cell_vectors())
    index = np.array(index)
    pos = np.sum(vectors.T * index, axis=1)
    return pos + np.array(self.cell_atoms())

enumerate

enumerate() -> Generator[LocationInfo, None, None]

enumerate all locations in the register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
124
125
126
127
128
def enumerate(self) -> Generator[LocationInfo, None, None]:
    for index in itertools.product(*[range(n) for n in self.shape]):
        for pos in self.coordinates(list(index)):
            position = tuple(self.lattice_spacing * pos)
            yield LocationInfo.create(position, True)

scale

scale(factor: ScalarType) -> BoundedBravais

Scale the current location with a factor.

(x,y) -> factor*(x,y)

Parameters:

Name Type Description Default
factor str | Real | Decimal | Scalar

scale factor

required

Returns:

Name Type Description
BoundedBravais BoundedBravais

The lattice with the scaled locations

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
@beartype
def scale(self, factor: ScalarType) -> "BoundedBravais":
    """Scale the current location with a factor.

    (x,y) -> factor*(x,y)

    Args:
        factor (str | Real | Decimal | Scalar): scale factor

    Returns:
        BoundedBravais: The lattice with the scaled locations
    """
    factor = cast(factor)
    obj = self.__new__(type(self))
    for f in fields(self):
        if f.name == "lattice_spacing":
            obj.lattice_spacing = factor * self.lattice_spacing
        else:
            setattr(obj, f.name, getattr(self, f.name))
    return obj

Chain

Chain(
    L: int,
    *,
    lattice_spacing: ScalarType = 1.0,
    vertical_chain: bool = False
)

Bases: BoundedBravais

Chain lattice.

  • 1D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0).
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default
L int

number of sites in the chain

required
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
183
184
185
186
187
188
189
190
@beartype
def __init__(
    self, L: int, *, lattice_spacing: ScalarType = 1.0, vertical_chain: bool = False
):
    self.L = L
    self.lattice_spacing = cast(lattice_spacing)
    self.vertical_chain = vertical_chain
    super().__init__()

Constant

Constant(value: ScalarType, duration: ScalarType)

Bases: Instruction

<constant> ::= 'constant' <scalar expr>

f(t=0:duration) = value

Parameters:

Name Type Description Default
value Scalar

the constant value

required
duration Scalar

the time span of the constant waveform.

required
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/waveform.py
323
324
325
326
@beartype
def __init__(self, value: ScalarType, duration: ScalarType):
    object.__setattr__(self, "value", cast(value))
    object.__setattr__(self, "duration", cast(duration))

Field

Bases: FieldExpr

Field node in the IR. Which contains collection(s) of [Waveform][bloqade.ir.control.waveform.Waveform]

<field> ::= ('field' <spatial modulation>  <padded waveform>)*

show

show(**assignments)

Interactive visualization of the Field

Parameters:

Name Type Description Default
**assignments

assigning the instance value (literal) to the existing variables in the Field

{}
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/field.py
262
263
264
265
266
267
268
269
270
271
def show(self, **assignments):
    """
    Interactive visualization of the Field

    Args:
        **assignments: assigning the instance value (literal) to the
            existing variables in the Field

    """
    display_ir(self, assignments)

Honeycomb

Honeycomb(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Honeycomb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (1/2, sqrt(3)/2)
  • unit cell (2 atom(s))
    • loc1 (0, 0)
    • loc2 (1/2, 1/(2*sqrt(3))

Parameters:

Name Type Description Default
L1 int

number of unit cells in linear direction. n_atoms = L1 * L1 * 2.

required
L2 Optional[int]

number of unit cells in direction a2. n_atoms = L1 * L2 * 2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
420
421
422
423
424
425
426
427
428
429
430
431
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1

    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)

    super().__init__()

Kagome

Kagome(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Kagome lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (1/2, sqrt(3)/2)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0.25 ,0.25sqrt(3))

Parameters:

Name Type Description Default
L1 int

number of sites in linear direction. n_atoms = 3 * L1 * L1.

required
L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
589
590
591
592
593
594
595
596
597
598
599
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1

    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)
    super().__init__()

Lieb

Lieb(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Lieb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (0, 1)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0 ,0.5)

Parameters:

Name Type Description Default
L1 int

number of unit cells in linear direction. n_atoms = 3* L1 * L1.

required
L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
534
535
536
537
538
539
540
541
542
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1
    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)

Linear

Linear(
    start: ScalarType,
    stop: ScalarType,
    duration: ScalarType,
)

Bases: Instruction

<linear> ::= 'linear' <scalar expr> <scalar expr>

f(t=0:duration) = start + (stop-start)/duration * t

Parameters:

Name Type Description Default
start Scalar

start value

required
stop Scalar

stop value

required
duration Scalar

the time span of the linear waveform.

required
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/waveform.py
274
275
276
277
278
@beartype
def __init__(self, start: ScalarType, stop: ScalarType, duration: ScalarType):
    object.__setattr__(self, "start", cast(start))
    object.__setattr__(self, "stop", cast(stop))
    object.__setattr__(self, "duration", cast(duration))

ListOfLocations

ListOfLocations(
    location_list: List[
        Union[LocationInfo, Tuple[ScalarType, ScalarType]]
    ] = [],
)

Bases: AtomArrangement

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
@beartype
def __init__(
    self,
    location_list: List[Union[LocationInfo, Tuple[ScalarType, ScalarType]]] = [],
):
    self.location_list = []
    for ele in location_list:
        if isinstance(ele, LocationInfo):
            self.location_list.append(ele)
        else:
            self.location_list.append(LocationInfo.create(ele, True))

    if self.location_list:
        self.__n_atoms = sum(
            1 for loc in self.location_list if loc.filling == SiteFilling.filled
        )
        self.__n_sites = len(self.location_list)
        self.__n_vacant = self.__n_sites - self.__n_atoms
        self.__n_dims = len(self.location_list[0].position)
    else:
        self.__n_sites = 0
        self.__n_atoms = 0
        self.__n_dims = None

    super().__init__()

n_atoms property

n_atoms

number of atoms (filled sites) in the register.

n_dims property

n_dims

number of dimensions in the register.

n_sites property

n_sites

number of sites in the register.

n_vacant property

n_vacant

number of vacant sites in the register.

enumerate

enumerate()

enumerate all locations in the register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
661
662
def enumerate(self):
    return iter(self.location_list)

Literal

Bases: Real

value instance-attribute

value: Decimal

Scalar Literal, which stores a decimaal value instance.

Parameters:

Name Type Description Default
value Decimal

decimal value instance

required

ParallelRegister

ParallelRegister(parent: Optional[Builder] = None)

Bases: ProgramStart

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

n_atoms property

n_atoms

Return the number of atoms in the program.

Returns:

Name Type Description
int int

The number of atoms in the parsed register.

Raises:

Type Description
ValueError

If the register type is unsupported.

Note

If the register is of type ParallelRegister, the number of atoms is extracted from its internal register.

Example:

>>> class MyBuilder(Parse):
...     pass
>>> builder = MyBuilder()
>>> n_atoms = builder.n_atoms

show

show(**assignments) -> None

Display the current program being defined with the given arguments and batch ID.

Parameters:

Name Type Description Default
*args

Additional arguments for display.

()
batch_id int

The batch ID to be displayed. Defaults to 0.

0
Note

This method uses the display_builder function to render the builder's state.

Example:

>>> class MyBuilder(Show):
...     pass
>>> builder = MyBuilder()
>>> builder.show()
>>> builder.show(batch_id=1)
>>> builder.show('arg1', 'arg2', batch_id=2)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
559
560
def show(self, **assignments) -> None:
    display_ir(self, assignments)

Poly

Poly(coeffs: Container[ScalarType], duration: ScalarType)

Bases: Instruction

<poly> ::= <scalar>+

f(t=0:duration) = c[0] + c[1]t + c[2]t^2 + ... + c[n-1]t^n-1 + c[n]t^n

Parameters:

Name Type Description Default
coeffs Tuple[Scalar]

the coefficients c[] of the polynomial.

required
duration Scalar

the time span of the waveform.

required
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/waveform.py
362
363
364
365
@beartype
def __init__(self, coeffs: Container[ScalarType], duration: ScalarType):
    object.__setattr__(self, "coeffs", tuple(map(cast, coeffs)))
    object.__setattr__(self, "duration", cast(duration))

Pulse

Bases: PulseExpr

<pulse> ::= (<field name> <field>)+

show

show(**assignments)

Interactive visualization of the Pulse

Parameters:

Name Type Description Default
**assignments

assigning the instance value (literal) to the existing variables in the Pulse

{}
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/pulse.py
194
195
196
197
198
199
200
201
202
203
def show(self, **assignments):
    """
    Interactive visualization of the Pulse

    Args:
        **assignments: assigning the instance value (literal) to the
            existing variables in the Pulse

    """
    display_ir(self, assignments)

PythonFn

Bases: Instruction

<python-fn> ::= 'python-fn' <python function def> <scalar expr>

Record

Bases: Waveform

<record> ::= 'record' <waveform> <var> <side>

Rectangular

Rectangular(
    width: int,
    height: int,
    *,
    lattice_spacing_x: ScalarType = 1.0,
    lattice_spacing_y: ScalarType = 1.0
)

Bases: BoundedBravais

Rectangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default
width int

number of sites in x direction.

required
height int

number of sites in y direction.

required
lattice_spacing_x (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
lattice_spacing_y (Scalar, Real)

lattice spacing in y direction. optional.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
@beartype
def __init__(
    self,
    width: int,
    height: int,
    *,
    lattice_spacing_x: ScalarType = 1.0,
    lattice_spacing_y: ScalarType = 1.0,
):
    self.width = width
    self.height = height
    self.lattice_spacing_x = cast(lattice_spacing_x)
    self.lattice_spacing_y = (
        cast(lattice_spacing_y)
        if lattice_spacing_y is not None
        else self.lattice_spacing_x
    )

    super().__init__()

Sample

Bases: Waveform

<sample> ::= 'sample' <waveform> <interpolation> <scalar>

Scalar

Base class for all scalar expressions.

<scalar> ::= <literal>
| <variable>
| <default>
| <negative>
| <add>
| <mul>
| <min>
| <max>
| <slice>
| <inverval>

<mul> ::= <scalar> '*' <scalar>
<add> ::= <scalar> '+' <scalar>
<min> ::= 'min' <scalar>+
<max> ::= 'max' <scalar>+
<slice> ::= <scalar expr> '[' <interval> ']'
<interval> ::= <scalar expr> '..' <scalar expr>
<real> ::= <literal> | <var>

Sequence

Bases: SequenceExpr

Sequence of a program, which includes pulses informations.

show

show(**assignments)

Interactive visualization of the Sequence

Parameters:

Name Type Description Default
**assignments

assigning the instance value (literal) to the existing variables in the Sequence

{}
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/sequence.py
166
167
168
169
170
171
172
173
174
175
def show(self, **assignments):
    """
    Interactive visualization of the Sequence

    Args:
        **assignments: assigning the instance value (literal) to the
            existing variables in the Sequence

    """
    display_ir(self, assignments)

Square

Square(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Square lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default
L1 int

number of sites in linear direction. n_atoms = L1 * L1.

required
L2 Optional[int]

number of sites in direction a2. n_atoms = L1 * L2, default is L1

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
240
241
242
243
244
245
246
247
248
249
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1
    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)
    super().__init__()

Triangular

Triangular(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Triangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (1/2, sqrt(3)/2)
  • unit cell (1 atom(s))
    • loc (0, 0)

Parameters:

Name Type Description Default
L int

number of sites in linear direction. n_atoms = L * L.

required
L2 Optional[int]

number of sites along a2 direction, n_atoms = L1 * L2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
477
478
479
480
481
482
483
484
485
486
487
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1
    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)

    super().__init__()

Variable

Bases: Real

Variable, which stores a variable name.

Parameters:

Name Type Description Default
name str

variable instance.

required

Waveform

Bases: HashTrait, CanonicalizeTrait

Waveform node in the IR.

  • [<instruction>][bloqade.ir.control.waveform.Instruction]
  • [<smooth>][bloqade.ir.control.waveform.Smooth]
  • [<slice>][bloqade.ir.control.waveform.Slice]
  • [<apppend>][bloqade.ir.control.waveform.Append]
  • [<negative>][bloqade.ir.control.waveform.Negative]
  • [<scale>][bloqade.ir.control.waveform.Scale]
  • [<add>][bloqade.ir.control.waveform.Add]
  • [<record>][bloqade.ir.control.waveform.Record]
  • [<sample>][bloqade.ir.control.waveform.Sample]
<waveform> ::= <instruction>
    | <smooth>
    | <slice>
    | <append>
    | <negative>
    | <scale>
    | <add>
    | <record>
    | <sample>

figure

figure(**assignments)

get figure of the plotting the waveform.

Returns:

Name Type Description
figure

a bokeh figure

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/waveform.py
 96
 97
 98
 99
100
101
102
def figure(self, **assignments):
    """get figure of the plotting the waveform.

    Returns:
        figure: a bokeh figure
    """
    return get_ir_figure(self, **assignments)

cast

cast(py) -> Scalar
  1. cast Real number (or list/tuple of Real numbers) to [Scalar Literal][bloqade.ir.scalar.Literal].

  2. cast str (or list/tuple of Real numbers) to [Scalar Variable][bloqade.ir.scalar.Variable].

Parameters:

Name Type Description Default
py Union[str, Real, Tuple[Real], List[Real]]

python object to cast

required

Returns:

Type Description
Scalar

Scalar

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/scalar.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
def cast(py) -> "Scalar":
    """
    1. cast Real number (or list/tuple of Real numbers)
    to [`Scalar Literal`][bloqade.ir.scalar.Literal].

    2. cast str (or list/tuple of Real numbers)
    to [`Scalar Variable`][bloqade.ir.scalar.Variable].

    Args:
        py (Union[str,Real,Tuple[Real],List[Real]]): python object to cast

    Returns:
        Scalar
    """
    ret = trycast(py)
    if ret is None:
        raise TypeError(f"Cannot cast {type(py)} to Scalar Literal")

    return ret

var

var(py: str) -> Variable

cast string (or list/tuple of strings) to [Variable][bloqade.ir.scalar.Variable].

Parameters:

Name Type Description Default
py Union[str, List[str]]

a string or list/tuple of strings

required

Returns:

Type Description
Variable

Union[Variable]

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/scalar.py
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
def var(py: str) -> "Variable":
    """cast string (or list/tuple of strings)
    to [`Variable`][bloqade.ir.scalar.Variable].

    Args:
        py (Union[str, List[str]]): a string or list/tuple of strings

    Returns:
       Union[Variable]
    """
    ret = tryvar(py)
    if ret is None:
        raise TypeError(f"Cannot cast {type(py)} to Variable")

    return ret

analog_circuit

AnalogCircuit

AnalogCircuit is a dummy type that bundle register and sequence together.

register property

register

Get the register of the program.

Returns:

Type Description

register (Union["AtomArrangement", "ParallelRegister"])

Note

If the program is built with [parallelize()][bloqade.builder.emit.Emit.parallelize], The the register will be a [ParallelRegister][bloqade.ir.location.base.ParallelRegister]. Otherwise it will be a [AtomArrangement][bloqade.ir.location.base.AtomArrangement].

show

show(**assignments)

Interactive visualization of the program

Parameters:

Name Type Description Default
**assignments

assigning the instance value (literal) to the existing variables in the program

{}
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/analog_circuit.py
122
123
124
125
126
127
128
129
130
def show(self, **assignments):
    """Interactive visualization of the program

    Args:
        **assignments: assigning the instance value (literal) to the
            existing variables in the program

    """
    display_ir(self, assignments)

control

field

Field

Bases: FieldExpr

Field node in the IR. Which contains collection(s) of [Waveform][bloqade.ir.control.waveform.Waveform]

<field> ::= ('field' <spatial modulation>  <padded waveform>)*
show
show(**assignments)

Interactive visualization of the Field

Parameters:

Name Type Description Default
**assignments

assigning the instance value (literal) to the existing variables in the Field

{}
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/field.py
262
263
264
265
266
267
268
269
270
271
def show(self, **assignments):
    """
    Interactive visualization of the Field

    Args:
        **assignments: assigning the instance value (literal) to the
            existing variables in the Field

    """
    display_ir(self, assignments)

pulse

Append

Bases: AppendTrait, PulseExpr

<append> ::= <expr>+

Pulse

Bases: PulseExpr

<pulse> ::= (<field name> <field>)+
show
show(**assignments)

Interactive visualization of the Pulse

Parameters:

Name Type Description Default
**assignments

assigning the instance value (literal) to the existing variables in the Pulse

{}
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/pulse.py
194
195
196
197
198
199
200
201
202
203
def show(self, **assignments):
    """
    Interactive visualization of the Pulse

    Args:
        **assignments: assigning the instance value (literal) to the
            existing variables in the Pulse

    """
    display_ir(self, assignments)

PulseExpr

Bases: HashTrait, CanonicalizeTrait

<expr> ::= <pulse>
  | <append>
  | <slice>
  | <named>

sequence

Sequence

Bases: SequenceExpr

Sequence of a program, which includes pulses informations.

show
show(**assignments)

Interactive visualization of the Sequence

Parameters:

Name Type Description Default
**assignments

assigning the instance value (literal) to the existing variables in the Sequence

{}
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/sequence.py
166
167
168
169
170
171
172
173
174
175
def show(self, **assignments):
    """
    Interactive visualization of the Sequence

    Args:
        **assignments: assigning the instance value (literal) to the
            existing variables in the Sequence

    """
    display_ir(self, assignments)

traits

SliceTrait

start cached property
start: Scalar

Start time of the sliced object

Returns:

Name Type Description
Scalar Scalar

The starting time of the sliced object as a

Scalar

Scalar Expression

stop cached property
stop: Scalar

Stop time of the sliced object

Returns:

Name Type Description
Scalar Scalar

The stopping time of the sliced object as a

Scalar

Scalar Expression

slice

SliceTrait
start cached property
start: Scalar

Start time of the sliced object

Returns:

Name Type Description
Scalar Scalar

The starting time of the sliced object as a

Scalar

Scalar Expression

stop cached property
stop: Scalar

Stop time of the sliced object

Returns:

Name Type Description
Scalar Scalar

The stopping time of the sliced object as a

Scalar

Scalar Expression

waveform

Add

Bases: Waveform

<add> ::= <waveform> '+' <waveform>

AlignedWaveform

Bases: Waveform

<padded waveform> ::= <waveform> | <waveform> <alignment> <value>

<alignment> ::= 'left aligned' | 'right aligned'
<value> ::= 'left value' | 'right value' | <scalar expr>

Append

Bases: AppendTrait, Waveform

<append> ::= <waveform>+

Constant

Constant(value: ScalarType, duration: ScalarType)

Bases: Instruction

<constant> ::= 'constant' <scalar expr>

f(t=0:duration) = value

Parameters:

Name Type Description Default
value Scalar

the constant value

required
duration Scalar

the time span of the constant waveform.

required
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/waveform.py
323
324
325
326
@beartype
def __init__(self, value: ScalarType, duration: ScalarType):
    object.__setattr__(self, "value", cast(value))
    object.__setattr__(self, "duration", cast(duration))

Instruction

Bases: Waveform

Instruction node in the IR.

  • [<linear>][bloqade.ir.control.waveform.Linear]
  • [<constant>][bloqade.ir.control.waveform.Constant]
  • [<poly>][bloqade.ir.control.waveform.Poly]
  • [<python-fn>][bloqade.ir.control.waveform.PythonFn]
<instruction> ::= <linear>
    | <constant>
    | <poly>
    | <python-fn>

Linear

Linear(
    start: ScalarType,
    stop: ScalarType,
    duration: ScalarType,
)

Bases: Instruction

<linear> ::= 'linear' <scalar expr> <scalar expr>

f(t=0:duration) = start + (stop-start)/duration * t

Parameters:

Name Type Description Default
start Scalar

start value

required
stop Scalar

stop value

required
duration Scalar

the time span of the linear waveform.

required
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/waveform.py
274
275
276
277
278
@beartype
def __init__(self, start: ScalarType, stop: ScalarType, duration: ScalarType):
    object.__setattr__(self, "start", cast(start))
    object.__setattr__(self, "stop", cast(stop))
    object.__setattr__(self, "duration", cast(duration))

Negative

Bases: Waveform

<negative> ::= '-' <waveform>

Poly

Poly(coeffs: Container[ScalarType], duration: ScalarType)

Bases: Instruction

<poly> ::= <scalar>+

f(t=0:duration) = c[0] + c[1]t + c[2]t^2 + ... + c[n-1]t^n-1 + c[n]t^n

Parameters:

Name Type Description Default
coeffs Tuple[Scalar]

the coefficients c[] of the polynomial.

required
duration Scalar

the time span of the waveform.

required
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/waveform.py
362
363
364
365
@beartype
def __init__(self, coeffs: Container[ScalarType], duration: ScalarType):
    object.__setattr__(self, "coeffs", tuple(map(cast, coeffs)))
    object.__setattr__(self, "duration", cast(duration))

PythonFn

Bases: Instruction

<python-fn> ::= 'python-fn' <python function def> <scalar expr>

Record

Bases: Waveform

<record> ::= 'record' <waveform> <var> <side>

Sample

Bases: Waveform

<sample> ::= 'sample' <waveform> <interpolation> <scalar>

Scale

Scale(scalar, waveform: Waveform)

Bases: Waveform

<scale> ::= <scalar expr> '*' <waveform>
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/waveform.py
754
755
756
def __init__(self, scalar, waveform: Waveform):
    object.__setattr__(self, "scalar", cast(scalar))
    object.__setattr__(self, "waveform", waveform)

Slice

Bases: SliceTrait, Waveform

<slice> ::= <waveform> <scalar.interval>

Smooth

Smooth(radius, kernel, waveform)

Bases: Waveform

<smooth> ::= 'smooth' <kernel> <waveform>
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/waveform.py
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
def __init__(self, radius, kernel, waveform):
    if isinstance(kernel, str):
        if kernel == "Gaussian":
            kernel = GaussianKernel
        elif kernel == "Logistic":
            kernel = LogisticKernel
        elif kernel == "Sigmoid":
            kernel = SigmoidKernel
        elif kernel == "Triangle":
            kernel = TriangleKernel
        elif kernel == "Uniform":
            kernel = UniformKernel
        elif kernel == "Parabolic":
            kernel = ParabolicKernel
        elif kernel == "Biweight":
            kernel = BiweightKernel
        elif kernel == "Triweight":
            kernel = TriweightKernel
        elif kernel == "Tricube":
            kernel = TricubeKernel
        elif kernel == "Cosine":
            kernel = CosineKernel
        else:
            raise ValueError(f"Invalid kernel: {kernel}")

    object.__setattr__(self, "radius", cast(radius))
    object.__setattr__(self, "kernel", kernel)
    object.__setattr__(self, "waveform", waveform)

Waveform

Bases: HashTrait, CanonicalizeTrait

Waveform node in the IR.

  • [<instruction>][bloqade.ir.control.waveform.Instruction]
  • [<smooth>][bloqade.ir.control.waveform.Smooth]
  • [<slice>][bloqade.ir.control.waveform.Slice]
  • [<apppend>][bloqade.ir.control.waveform.Append]
  • [<negative>][bloqade.ir.control.waveform.Negative]
  • [<scale>][bloqade.ir.control.waveform.Scale]
  • [<add>][bloqade.ir.control.waveform.Add]
  • [<record>][bloqade.ir.control.waveform.Record]
  • [<sample>][bloqade.ir.control.waveform.Sample]
<waveform> ::= <instruction>
    | <smooth>
    | <slice>
    | <append>
    | <negative>
    | <scale>
    | <add>
    | <record>
    | <sample>
figure
figure(**assignments)

get figure of the plotting the waveform.

Returns:

Name Type Description
figure

a bokeh figure

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/control/waveform.py
 96
 97
 98
 99
100
101
102
def figure(self, **assignments):
    """get figure of the plotting the waveform.

    Returns:
        figure: a bokeh figure
    """
    return get_ir_figure(self, **assignments)

location

start module-attribute

start = ListOfLocations()

A Program starting point, alias of empty [ListOfLocations][bloqade.ir.location.list.ListOfLocations].

  • Next possible steps to build your program are:
  • Specify which level coupling to address with:
    • start.rydberg: for [Rydberg][bloqade.builder.coupling.Rydberg] Level coupling
    • start.hyperfine: for [Hyperfine][bloqade.builder.coupling.Hyperfine] Level coupling
    • LOCKOUT: You cannot add atoms to your geometry after specifying level coupling.
  • continue/start building your geometry with:
    • start.add_position(): to add atom(s) to current register. It will accept:
      • A single coordinate, represented as a tuple (e.g. (5,6)) with a value that can either be:
        • integers: (5,6)
        • floats: (5.1, 2.5)
        • strings (for later variable assignment): ("x", "y")
        • [Scalar][bloqade.ir.scalar.Scalar] objects: (2*cast("x"), 5+cast("y"))
      • A list of coordinates, represented as a list of types mentioned previously.
      • A numpy array with shape (n, 2) where n is the total number of atoms

AtomArrangement

AtomArrangement(parent: Optional[Builder] = None)

Bases: ProgramStart

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

n_atoms property

n_atoms: int

number of atoms (filled sites) in the register.

n_dims property

n_dims: int

number of dimensions in the register.

n_sites property

n_sites: int

number of sites in the register.

n_vacant property

n_vacant: int

number of vacant sites in the register.

add_position

add_position(
    position: Union[
        PositionArray,
        List[Tuple[ScalarType, ScalarType]],
        Tuple[ScalarType, ScalarType],
    ],
    filling: Optional[
        Union[BoolArray, List[bool], bool]
    ] = None,
) -> ListOfLocations

Add a position or multiple positions to a pre-existing geometry.

add_position is capable of accepting: - A single tuple for one atom coordinate: (1.0, 2.5) - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.] - A numpy array of shape (N, 2) where N is the number of atoms

You may also intersperse variables anywhere a value may be present.

You can also pass in an optional argument which determines the atom "filling" (whether or not at a specified coordinate an atom should be present).

Usage Example:
# single coordinate
>>> reg = start.add_position((0,0))
# you may chain add_position calls
>>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])
# you can add variables anywhere a value may be present
>>> reg_with_var = reg_plus_two.add_position(("x", "y"))
# and specify your atom fillings
>>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],
[True, False])
# alternatively you could use one boolean to specify
# all coordinates should be empty/filled
>>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),
(5.2, 2.2)], False)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
def add_position(
    self,
    position: Union[
        PositionArray,
        List[Tuple[ScalarType, ScalarType]],
        Tuple[ScalarType, ScalarType],
    ],
    filling: Optional[Union[BoolArray, List[bool], bool]] = None,
) -> "ListOfLocations":
    """
    Add a position or multiple positions to a pre-existing geometry.

    `add_position` is capable of accepting:
    - A single tuple for one atom coordinate: `(1.0, 2.5)`
    - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.]
    - A numpy array of shape (N, 2) where N is the number of atoms

    You may also intersperse variables anywhere a value may be present.

    You can also pass in an optional argument which determines the atom "filling"
    (whether or not at a specified coordinate an atom should be present).

    ### Usage Example:
    ```
    # single coordinate
    >>> reg = start.add_position((0,0))
    # you may chain add_position calls
    >>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])
    # you can add variables anywhere a value may be present
    >>> reg_with_var = reg_plus_two.add_position(("x", "y"))
    # and specify your atom fillings
    >>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],
    [True, False])
    # alternatively you could use one boolean to specify
    # all coordinates should be empty/filled
    >>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),
    (5.2, 2.2)], False)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...add_position(positions).add_position(positions)`:
            to add more positions
        - `...add_position(positions).apply_defect_count(n_defects)`:
        to randomly drop out n_atoms
        - `...add_position(positions).apply_defect_density(defect_probability)`:
        to drop out atoms with a certain probability
        - `...add_position(positions).scale(scale)`: to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...add_position(positions).rydberg`: to specify Rydberg coupling
        - `...add_position(positions).hyperfine`: to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...add_position(positions).show()`:
        shows your geometry in your web browser

    """

    if is_bearable(position, PositionArray) and is_bearable(
        filling, Optional[BoolArray]
    ):
        return self.add_position_ndarray(position, filling)
    elif is_bearable(position, List[Tuple[ScalarType, ScalarType]]) and is_bearable(
        filling, Optional[List[bool]]
    ):
        return self.add_position_list_tuples(position, filling)
    elif is_bearable(position, Tuple[ScalarType, ScalarType]) and is_bearable(
        filling, Optional[bool]
    ):
        return self.add_position_single_tupe(position, filling)
    else:
        raise TypeError("Invalid input types for add_position provided!")

apply_defect_count

apply_defect_count(
    n_defects: int, rng: Generator = np.random.default_rng()
)

Drop n_defects atoms from the geometry randomly. Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

Usage Example:
>>> from bloqade.analog.atom_arrangement import Chain
>>> import numpy as np
# set a custom seed for a numpy-based RNG
>>> custom_rng = np.random.default_rng(888)
# randomly remove two atoms from the geometry
>>> reg = Chain(11).apply_defect_count(2, custom_rng)
# you may also chain apply_defect_count calls
>>> reg.apply_defect_count(2, custom_rng)
# you can also use apply_defect_count on custom geometries
>>> from bloqade import start
>>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts) .apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
@beartype
def apply_defect_count(
    self, n_defects: int, rng: np.random.Generator = np.random.default_rng()
):
    """
    Drop `n_defects` atoms from the geometry randomly. Internally this occurs
    by setting certain sites to have a SiteFilling set to false indicating
    no atom is present at the coordinate.

    A default numpy-based Random Number Generator is used but you can
    explicitly override this by passing in your own.

    ### Usage Example:

    ```
    >>> from bloqade.analog.atom_arrangement import Chain
    >>> import numpy as np
    # set a custom seed for a numpy-based RNG
    >>> custom_rng = np.random.default_rng(888)
    # randomly remove two atoms from the geometry
    >>> reg = Chain(11).apply_defect_count(2, custom_rng)
    # you may also chain apply_defect_count calls
    >>> reg.apply_defect_count(2, custom_rng)
    # you can also use apply_defect_count on custom geometries
    >>> from bloqade import start
    >>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...apply_defect_count(defect_counts).add_position(positions)`:
            to add more positions
        - `...apply_defect_count(defect_counts)
            .apply_defect_count(n_defects)`: to randomly drop out n_atoms
        - `...apply_defect_count(defect_counts)
            .apply_defect_density(defect_probability)`:
            to drop out atoms with a certain probability
        - `...apply_defect_count(defect_counts).scale(scale)`:
            to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...apply_defect_count(defect_counts).rydberg`: to specify
            Rydberg coupling
        - `...apply_defect_count(defect_counts).hyperfine`:
            to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...apply_defect_count(defect_counts).show()`:
            shows your geometry in your web browser
    """

    location_list = []
    for location_info in self.enumerate():
        location_list.append(location_info)

    filled_sites = []

    for index, location_info in enumerate(location_list):
        if location_info.filling is SiteFilling.filled:
            filled_sites.append(index)

    if n_defects >= len(filled_sites):
        raise ValueError(
            f"n_defects {n_defects} must be less than the number of filled sites "
            f"({len(filled_sites)})"
        )

    for _ in range(n_defects):
        index = rng.choice(filled_sites)
        location_list[index] = LocationInfo.create(
            location_list[index].position,
            (False if location_list[index].filling is SiteFilling.filled else True),
        )
        filled_sites.remove(index)

    return ListOfLocations(location_list)

apply_defect_density

apply_defect_density(
    defect_probability: float,
    rng: Generator = np.random.default_rng(),
)

Drop atoms randomly with defect_probability probability (range of 0 to 1). Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

Usage Example:
>>> from bloqade.analog.atom_arrangement import Chain
>>> import numpy as np
# set a custom seed for a numpy-based RNG
>>> custom_rng = np.random.default_rng(888)
# randomly remove two atoms from the geometry
>>> reg = Chain(11).apply_defect_density(0.2, custom_rng)
# you may also chain apply_defect_density calls
>>> reg.apply_defect_count(0.1, custom_rng)
# you can also use apply_defect_density on custom geometries
>>> from bloqade import start
>>> start.add_position([(0,0), (1,1)])
.apply_defect_density(0.5, custom_rng)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
@beartype
def apply_defect_density(
    self,
    defect_probability: float,
    rng: np.random.Generator = np.random.default_rng(),
):
    """
    Drop atoms randomly with `defect_probability` probability (range of 0 to 1).
    Internally this occurs by setting certain sites to have a SiteFilling
    set to false indicating no atom is present at the coordinate.

    A default numpy-based Random Number Generator is used but you can
    explicitly override this by passing in your own.

    ### Usage Example:

    ```
    >>> from bloqade.analog.atom_arrangement import Chain
    >>> import numpy as np
    # set a custom seed for a numpy-based RNG
    >>> custom_rng = np.random.default_rng(888)
    # randomly remove two atoms from the geometry
    >>> reg = Chain(11).apply_defect_density(0.2, custom_rng)
    # you may also chain apply_defect_density calls
    >>> reg.apply_defect_count(0.1, custom_rng)
    # you can also use apply_defect_density on custom geometries
    >>> from bloqade import start
    >>> start.add_position([(0,0), (1,1)])
    .apply_defect_density(0.5, custom_rng)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...apply_defect_count(defect_counts).add_position(positions)`:
        to add more positions
        - `...apply_defect_count(defect_counts).apply_defect_count(n_defects)`:
        to randomly drop out n_atoms
        - `...apply_defect_count(defect_counts)
        .apply_defect_density(defect_probability)`:
        to drop out atoms with a certain probability
        - `...apply_defect_count(defect_counts).scale(scale)`:
        to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...apply_defect_count(defect_counts).rydberg`:
        to specify Rydberg coupling
        - `...apply_defect_count(defect_counts).hyperfine`:
        to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...apply_defect_count(defect_counts).show()`:
        shows your geometry in your web browser
    """

    p = min(1, max(0, defect_probability))
    location_list = []

    for location_info in self.enumerate():
        if rng.random() < p:
            location_list.append(
                LocationInfo.create(
                    location_info.position,
                    (
                        False
                        if location_info.filling is SiteFilling.filled
                        else True
                    ),
                )
            )
        else:
            location_list.append(location_info)

    return ListOfLocations(location_list=location_list)

enumerate

enumerate() -> Generator[LocationInfo, None, None]

enumerate all locations in the register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
128
129
130
def enumerate(self) -> Generator[LocationInfo, None, None]:
    """enumerate all locations in the register."""
    raise NotImplementedError

figure

figure(fig_kwargs=None, **assignments)

obtain a figure object from the atom arrangement.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
132
133
134
def figure(self, fig_kwargs=None, **assignments):
    """obtain a figure object from the atom arrangement."""
    return get_atom_arrangement_figure(self, fig_kwargs=fig_kwargs, **assignments)

rydberg_interaction

rydberg_interaction(**assignments) -> NDArray

calculate the Rydberg interaction matrix.

Parameters:

Name Type Description Default
**assignments

the values to assign to the variables in the register.

{}

Returns:

Name Type Description
NDArray NDArray

the Rydberg interaction matrix in the lower triangular form.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
def rydberg_interaction(self, **assignments) -> NDArray:
    """calculate the Rydberg interaction matrix.

    Args:
        **assignments: the values to assign to the variables in the register.

    Returns:
        NDArray: the Rydberg interaction matrix in the lower triangular form.

    """

    from bloqade.analog.constants import RB_C6

    # calculate the Interaction matrix
    V_ij = np.zeros((self.n_sites, self.n_sites))
    for i, site_i in enumerate(self.enumerate()):
        pos_i = np.array([float(ele(**assignments)) for ele in site_i.position])

        for j, site_j in enumerate(self.enumerate()):
            if j >= i:
                break  # enforce lower triangular form

            pos_j = np.array([float(ele(**assignments)) for ele in site_j.position])
            r_ij = np.linalg.norm(pos_i - pos_j)

            V_ij[i, j] = RB_C6 / r_ij**6

    return V_ij

scale

scale(scale: ScalarType)

Scale the geometry of your atoms.

Usage Example:
>>> reg = start.add_position([(0,0), (1,1)])
# atom positions are now (0,0), (2,2)
>>> new_reg = reg.scale(2)
# you may also use scale on pre-defined geometries
>>> from bloqade.analog.atom_arrangement import Chain
# atoms in the chain will now be 2 um apart versus
# the default 1 um
>>> Chain(11).scale(2)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
@beartype
def scale(self, scale: ScalarType):
    """
    Scale the geometry of your atoms.

    ### Usage Example:
    ```
    >>> reg = start.add_position([(0,0), (1,1)])
    # atom positions are now (0,0), (2,2)
    >>> new_reg = reg.scale(2)
    # you may also use scale on pre-defined geometries
    >>> from bloqade.analog.atom_arrangement import Chain
    # atoms in the chain will now be 2 um apart versus
    # the default 1 um
    >>> Chain(11).scale(2)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...add_position(positions).add_position(positions)`:
            to add more positions
        - `...add_position(positions).apply_defect_count(n_defects)`:
        to randomly drop out n_atoms
        - `...add_position(positions).apply_defect_density(defect_probability)`:
        to drop out atoms with a certain probability
        - `...add_position(positions).scale(scale)`: to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...add_position(positions).rydberg`:
        to specify Rydberg coupling
        - `...add_position(positions).hyperfine`:
        to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...add_position(positions).show()`:
        shows your geometry in your web browser

    """

    scale = cast(scale)
    location_list = []
    for location_info in self.enumerate():
        x, y = location_info.position
        new_position = (scale * x, scale * y)
        location_list.append(
            LocationInfo.create(new_position, bool(location_info.filling.value))
        )

    return ListOfLocations(location_list)

show

show(**assignments) -> None

Display the current program being defined with the given arguments and batch ID.

Parameters:

Name Type Description Default
*args

Additional arguments for display.

()
batch_id int

The batch ID to be displayed. Defaults to 0.

0
Note

This method uses the display_builder function to render the builder's state.

Example:

>>> class MyBuilder(Show):
...     pass
>>> builder = MyBuilder()
>>> builder.show()
>>> builder.show(batch_id=1)
>>> builder.show('arg1', 'arg2', batch_id=2)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
136
137
def show(self, **assignments) -> None:
    display_ir(self, assignments)

BoundedBravais

BoundedBravais(parent: Optional[Builder] = None)

Bases: AtomArrangement

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

__match_args__ class-attribute instance-attribute

__match_args__ = ('shape', 'lattice_spacing')

Base classe for Bravais lattices [AtomArrangement][bloqade.ir.location.base.AtomArrangement].

  • [Square][bloqade.ir.location.bravais.Square]
  • [Chain][bloqade.ir.location.bravais.Chain]
  • [Honeycomb][bloqade.ir.location.bravais.Honeycomb]
  • [Triangular][bloqade.ir.location.bravais.Triangular]
  • [Lieb][bloqade.ir.location.bravais.Lieb]
  • [Kagome][bloqade.ir.location.bravais.Kagome]
  • [Rectangular][bloqade.ir.location.bravais.Rectangular]

n_atoms cached property

n_atoms: int

number of atoms (filled sites) in the register.

n_dims property

n_dims

dimension of the lattice

Returns:

Name Type Description
int

dimension of the lattice

n_sites property

n_sites: int

number of sites in the register.

n_vacant property

n_vacant: int

number of vacant sites in the register.

coordinates

coordinates(index: List[int]) -> NDArray

calculate the coordinates of a cell in the lattice given the cell index.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
113
114
115
116
117
118
119
120
121
122
@beartype
def coordinates(self, index: List[int]) -> NDArray:
    """calculate the coordinates of a cell in the lattice
    given the cell index.
    """
    # damn! this is like stone age broadcasting
    vectors = np.array(self.cell_vectors())
    index = np.array(index)
    pos = np.sum(vectors.T * index, axis=1)
    return pos + np.array(self.cell_atoms())

enumerate

enumerate() -> Generator[LocationInfo, None, None]

enumerate all locations in the register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
124
125
126
127
128
def enumerate(self) -> Generator[LocationInfo, None, None]:
    for index in itertools.product(*[range(n) for n in self.shape]):
        for pos in self.coordinates(list(index)):
            position = tuple(self.lattice_spacing * pos)
            yield LocationInfo.create(position, True)

scale

scale(factor: ScalarType) -> BoundedBravais

Scale the current location with a factor.

(x,y) -> factor*(x,y)

Parameters:

Name Type Description Default
factor str | Real | Decimal | Scalar

scale factor

required

Returns:

Name Type Description
BoundedBravais BoundedBravais

The lattice with the scaled locations

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
@beartype
def scale(self, factor: ScalarType) -> "BoundedBravais":
    """Scale the current location with a factor.

    (x,y) -> factor*(x,y)

    Args:
        factor (str | Real | Decimal | Scalar): scale factor

    Returns:
        BoundedBravais: The lattice with the scaled locations
    """
    factor = cast(factor)
    obj = self.__new__(type(self))
    for f in fields(self):
        if f.name == "lattice_spacing":
            obj.lattice_spacing = factor * self.lattice_spacing
        else:
            setattr(obj, f.name, getattr(self, f.name))
    return obj

Chain

Chain(
    L: int,
    *,
    lattice_spacing: ScalarType = 1.0,
    vertical_chain: bool = False
)

Bases: BoundedBravais

Chain lattice.

  • 1D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0).
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default
L int

number of sites in the chain

required
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
183
184
185
186
187
188
189
190
@beartype
def __init__(
    self, L: int, *, lattice_spacing: ScalarType = 1.0, vertical_chain: bool = False
):
    self.L = L
    self.lattice_spacing = cast(lattice_spacing)
    self.vertical_chain = vertical_chain
    super().__init__()

Honeycomb

Honeycomb(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Honeycomb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (1/2, sqrt(3)/2)
  • unit cell (2 atom(s))
    • loc1 (0, 0)
    • loc2 (1/2, 1/(2*sqrt(3))

Parameters:

Name Type Description Default
L1 int

number of unit cells in linear direction. n_atoms = L1 * L1 * 2.

required
L2 Optional[int]

number of unit cells in direction a2. n_atoms = L1 * L2 * 2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
420
421
422
423
424
425
426
427
428
429
430
431
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1

    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)

    super().__init__()

Kagome

Kagome(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Kagome lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (1/2, sqrt(3)/2)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0.25 ,0.25sqrt(3))

Parameters:

Name Type Description Default
L1 int

number of sites in linear direction. n_atoms = 3 * L1 * L1.

required
L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
589
590
591
592
593
594
595
596
597
598
599
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1

    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)
    super().__init__()

Lieb

Lieb(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Lieb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (0, 1)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0 ,0.5)

Parameters:

Name Type Description Default
L1 int

number of unit cells in linear direction. n_atoms = 3* L1 * L1.

required
L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
534
535
536
537
538
539
540
541
542
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1
    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)

ListOfLocations

ListOfLocations(
    location_list: List[
        Union[LocationInfo, Tuple[ScalarType, ScalarType]]
    ] = [],
)

Bases: AtomArrangement

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
@beartype
def __init__(
    self,
    location_list: List[Union[LocationInfo, Tuple[ScalarType, ScalarType]]] = [],
):
    self.location_list = []
    for ele in location_list:
        if isinstance(ele, LocationInfo):
            self.location_list.append(ele)
        else:
            self.location_list.append(LocationInfo.create(ele, True))

    if self.location_list:
        self.__n_atoms = sum(
            1 for loc in self.location_list if loc.filling == SiteFilling.filled
        )
        self.__n_sites = len(self.location_list)
        self.__n_vacant = self.__n_sites - self.__n_atoms
        self.__n_dims = len(self.location_list[0].position)
    else:
        self.__n_sites = 0
        self.__n_atoms = 0
        self.__n_dims = None

    super().__init__()

n_atoms property

n_atoms

number of atoms (filled sites) in the register.

n_dims property

n_dims

number of dimensions in the register.

n_sites property

n_sites

number of sites in the register.

n_vacant property

n_vacant

number of vacant sites in the register.

enumerate

enumerate()

enumerate all locations in the register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
661
662
def enumerate(self):
    return iter(self.location_list)

ParallelRegister

ParallelRegister(parent: Optional[Builder] = None)

Bases: ProgramStart

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent

n_atoms property

n_atoms

Return the number of atoms in the program.

Returns:

Name Type Description
int int

The number of atoms in the parsed register.

Raises:

Type Description
ValueError

If the register type is unsupported.

Note

If the register is of type ParallelRegister, the number of atoms is extracted from its internal register.

Example:

>>> class MyBuilder(Parse):
...     pass
>>> builder = MyBuilder()
>>> n_atoms = builder.n_atoms

show

show(**assignments) -> None

Display the current program being defined with the given arguments and batch ID.

Parameters:

Name Type Description Default
*args

Additional arguments for display.

()
batch_id int

The batch ID to be displayed. Defaults to 0.

0
Note

This method uses the display_builder function to render the builder's state.

Example:

>>> class MyBuilder(Show):
...     pass
>>> builder = MyBuilder()
>>> builder.show()
>>> builder.show(batch_id=1)
>>> builder.show('arg1', 'arg2', batch_id=2)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
559
560
def show(self, **assignments) -> None:
    display_ir(self, assignments)

Rectangular

Rectangular(
    width: int,
    height: int,
    *,
    lattice_spacing_x: ScalarType = 1.0,
    lattice_spacing_y: ScalarType = 1.0
)

Bases: BoundedBravais

Rectangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default
width int

number of sites in x direction.

required
height int

number of sites in y direction.

required
lattice_spacing_x (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
lattice_spacing_y (Scalar, Real)

lattice spacing in y direction. optional.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
@beartype
def __init__(
    self,
    width: int,
    height: int,
    *,
    lattice_spacing_x: ScalarType = 1.0,
    lattice_spacing_y: ScalarType = 1.0,
):
    self.width = width
    self.height = height
    self.lattice_spacing_x = cast(lattice_spacing_x)
    self.lattice_spacing_y = (
        cast(lattice_spacing_y)
        if lattice_spacing_y is not None
        else self.lattice_spacing_x
    )

    super().__init__()

Square

Square(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Square lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default
L1 int

number of sites in linear direction. n_atoms = L1 * L1.

required
L2 Optional[int]

number of sites in direction a2. n_atoms = L1 * L2, default is L1

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
240
241
242
243
244
245
246
247
248
249
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1
    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)
    super().__init__()

Triangular

Triangular(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Triangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (1/2, sqrt(3)/2)
  • unit cell (1 atom(s))
    • loc (0, 0)

Parameters:

Name Type Description Default
L int

number of sites in linear direction. n_atoms = L * L.

required
L2 Optional[int]

number of sites along a2 direction, n_atoms = L1 * L2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
477
478
479
480
481
482
483
484
485
486
487
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1
    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)

    super().__init__()

bravais

BoundedBravais

BoundedBravais(parent: Optional[Builder] = None)

Bases: AtomArrangement

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent
__match_args__ class-attribute instance-attribute
__match_args__ = ('shape', 'lattice_spacing')

Base classe for Bravais lattices [AtomArrangement][bloqade.ir.location.base.AtomArrangement].

  • [Square][bloqade.ir.location.bravais.Square]
  • [Chain][bloqade.ir.location.bravais.Chain]
  • [Honeycomb][bloqade.ir.location.bravais.Honeycomb]
  • [Triangular][bloqade.ir.location.bravais.Triangular]
  • [Lieb][bloqade.ir.location.bravais.Lieb]
  • [Kagome][bloqade.ir.location.bravais.Kagome]
  • [Rectangular][bloqade.ir.location.bravais.Rectangular]
n_atoms cached property
n_atoms: int

number of atoms (filled sites) in the register.

n_dims property
n_dims

dimension of the lattice

Returns:

Name Type Description
int

dimension of the lattice

n_sites property
n_sites: int

number of sites in the register.

n_vacant property
n_vacant: int

number of vacant sites in the register.

coordinates
coordinates(index: List[int]) -> NDArray

calculate the coordinates of a cell in the lattice given the cell index.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
113
114
115
116
117
118
119
120
121
122
@beartype
def coordinates(self, index: List[int]) -> NDArray:
    """calculate the coordinates of a cell in the lattice
    given the cell index.
    """
    # damn! this is like stone age broadcasting
    vectors = np.array(self.cell_vectors())
    index = np.array(index)
    pos = np.sum(vectors.T * index, axis=1)
    return pos + np.array(self.cell_atoms())
enumerate
enumerate() -> Generator[LocationInfo, None, None]

enumerate all locations in the register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
124
125
126
127
128
def enumerate(self) -> Generator[LocationInfo, None, None]:
    for index in itertools.product(*[range(n) for n in self.shape]):
        for pos in self.coordinates(list(index)):
            position = tuple(self.lattice_spacing * pos)
            yield LocationInfo.create(position, True)
scale
scale(factor: ScalarType) -> BoundedBravais

Scale the current location with a factor.

(x,y) -> factor*(x,y)

Parameters:

Name Type Description Default
factor str | Real | Decimal | Scalar

scale factor

required

Returns:

Name Type Description
BoundedBravais BoundedBravais

The lattice with the scaled locations

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
@beartype
def scale(self, factor: ScalarType) -> "BoundedBravais":
    """Scale the current location with a factor.

    (x,y) -> factor*(x,y)

    Args:
        factor (str | Real | Decimal | Scalar): scale factor

    Returns:
        BoundedBravais: The lattice with the scaled locations
    """
    factor = cast(factor)
    obj = self.__new__(type(self))
    for f in fields(self):
        if f.name == "lattice_spacing":
            obj.lattice_spacing = factor * self.lattice_spacing
        else:
            setattr(obj, f.name, getattr(self, f.name))
    return obj

Chain

Chain(
    L: int,
    *,
    lattice_spacing: ScalarType = 1.0,
    vertical_chain: bool = False
)

Bases: BoundedBravais

Chain lattice.

  • 1D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0).
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default
L int

number of sites in the chain

required
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
183
184
185
186
187
188
189
190
@beartype
def __init__(
    self, L: int, *, lattice_spacing: ScalarType = 1.0, vertical_chain: bool = False
):
    self.L = L
    self.lattice_spacing = cast(lattice_spacing)
    self.vertical_chain = vertical_chain
    super().__init__()

Honeycomb

Honeycomb(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Honeycomb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (1/2, sqrt(3)/2)
  • unit cell (2 atom(s))
    • loc1 (0, 0)
    • loc2 (1/2, 1/(2*sqrt(3))

Parameters:

Name Type Description Default
L1 int

number of unit cells in linear direction. n_atoms = L1 * L1 * 2.

required
L2 Optional[int]

number of unit cells in direction a2. n_atoms = L1 * L2 * 2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
420
421
422
423
424
425
426
427
428
429
430
431
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1

    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)

    super().__init__()

Kagome

Kagome(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Kagome lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (1/2, sqrt(3)/2)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0.25 ,0.25sqrt(3))

Parameters:

Name Type Description Default
L1 int

number of sites in linear direction. n_atoms = 3 * L1 * L1.

required
L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
589
590
591
592
593
594
595
596
597
598
599
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1

    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)
    super().__init__()

Lieb

Lieb(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Lieb lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (0, 1)
  • unit cell (3 atom(s))
    • loc1 (0, 0)
    • loc2 (0.5, 0)
    • loc3 (0 ,0.5)

Parameters:

Name Type Description Default
L1 int

number of unit cells in linear direction. n_atoms = 3* L1 * L1.

required
L2 Optional[int]

number of unit cells along a2 direction, n_atoms = 3 * L1 * L2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
534
535
536
537
538
539
540
541
542
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1
    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)

Rectangular

Rectangular(
    width: int,
    height: int,
    *,
    lattice_spacing_x: ScalarType = 1.0,
    lattice_spacing_y: ScalarType = 1.0
)

Bases: BoundedBravais

Rectangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default
width int

number of sites in x direction.

required
height int

number of sites in y direction.

required
lattice_spacing_x (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
lattice_spacing_y (Scalar, Real)

lattice spacing in y direction. optional.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
@beartype
def __init__(
    self,
    width: int,
    height: int,
    *,
    lattice_spacing_x: ScalarType = 1.0,
    lattice_spacing_y: ScalarType = 1.0,
):
    self.width = width
    self.height = height
    self.lattice_spacing_x = cast(lattice_spacing_x)
    self.lattice_spacing_y = (
        cast(lattice_spacing_y)
        if lattice_spacing_y is not None
        else self.lattice_spacing_x
    )

    super().__init__()

Square

Square(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Square lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1,0)
    • a2 = (0,1)
  • unit cell (1 atom(s))
    • loc (0,0)

Parameters:

Name Type Description Default
L1 int

number of sites in linear direction. n_atoms = L1 * L1.

required
L2 Optional[int]

number of sites in direction a2. n_atoms = L1 * L2, default is L1

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
240
241
242
243
244
245
246
247
248
249
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1
    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)
    super().__init__()

Triangular

Triangular(
    L1: int,
    L2: Optional[int] = None,
    *,
    lattice_spacing: ScalarType = 1.0
)

Bases: BoundedBravais

Triangular lattice.

  • 2D lattice
  • primitive (cell) vector(s)
    • a1 = (1, 0)
    • a2 = (1/2, sqrt(3)/2)
  • unit cell (1 atom(s))
    • loc (0, 0)

Parameters:

Name Type Description Default
L int

number of sites in linear direction. n_atoms = L * L.

required
L2 Optional[int]

number of sites along a2 direction, n_atoms = L1 * L2, default is L1.

None
lattice_spacing (Scalar, Real)

lattice spacing. Defaults to 1.0.

1.0
  • Possible Next: continue with . to see possible next step in auto-prompt supported setting (IPython, IDE ...)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/bravais.py
477
478
479
480
481
482
483
484
485
486
487
@beartype
def __init__(
    self, L1: int, L2: Optional[int] = None, *, lattice_spacing: ScalarType = 1.0
):
    if L2 is None:
        L2 = L1
    self.L1 = L1
    self.L2 = L2
    self.lattice_spacing = cast(lattice_spacing)

    super().__init__()

location

AtomArrangement

AtomArrangement(parent: Optional[Builder] = None)

Bases: ProgramStart

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent
n_atoms property
n_atoms: int

number of atoms (filled sites) in the register.

n_dims property
n_dims: int

number of dimensions in the register.

n_sites property
n_sites: int

number of sites in the register.

n_vacant property
n_vacant: int

number of vacant sites in the register.

add_position
add_position(
    position: Union[
        PositionArray,
        List[Tuple[ScalarType, ScalarType]],
        Tuple[ScalarType, ScalarType],
    ],
    filling: Optional[
        Union[BoolArray, List[bool], bool]
    ] = None,
) -> ListOfLocations

Add a position or multiple positions to a pre-existing geometry.

add_position is capable of accepting: - A single tuple for one atom coordinate: (1.0, 2.5) - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.] - A numpy array of shape (N, 2) where N is the number of atoms

You may also intersperse variables anywhere a value may be present.

You can also pass in an optional argument which determines the atom "filling" (whether or not at a specified coordinate an atom should be present).

Usage Example:
# single coordinate
>>> reg = start.add_position((0,0))
# you may chain add_position calls
>>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])
# you can add variables anywhere a value may be present
>>> reg_with_var = reg_plus_two.add_position(("x", "y"))
# and specify your atom fillings
>>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],
[True, False])
# alternatively you could use one boolean to specify
# all coordinates should be empty/filled
>>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),
(5.2, 2.2)], False)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
def add_position(
    self,
    position: Union[
        PositionArray,
        List[Tuple[ScalarType, ScalarType]],
        Tuple[ScalarType, ScalarType],
    ],
    filling: Optional[Union[BoolArray, List[bool], bool]] = None,
) -> "ListOfLocations":
    """
    Add a position or multiple positions to a pre-existing geometry.

    `add_position` is capable of accepting:
    - A single tuple for one atom coordinate: `(1.0, 2.5)`
    - A list of tuples: `[(0.0, 1.0), (2.0,1.5), etc.]
    - A numpy array of shape (N, 2) where N is the number of atoms

    You may also intersperse variables anywhere a value may be present.

    You can also pass in an optional argument which determines the atom "filling"
    (whether or not at a specified coordinate an atom should be present).

    ### Usage Example:
    ```
    # single coordinate
    >>> reg = start.add_position((0,0))
    # you may chain add_position calls
    >>> reg_plus_two = reg.add_position([(2,2),(5.0, 2.1)])
    # you can add variables anywhere a value may be present
    >>> reg_with_var = reg_plus_two.add_position(("x", "y"))
    # and specify your atom fillings
    >>> reg_with_filling = reg_with_var.add_position([(3.1, 0.0), (4.1, 2.2)],
    [True, False])
    # alternatively you could use one boolean to specify
    # all coordinates should be empty/filled
    >>> reg_with_more_filling = reg_with_filling.add_positions([(3.1, 2.9),
    (5.2, 2.2)], False)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...add_position(positions).add_position(positions)`:
            to add more positions
        - `...add_position(positions).apply_defect_count(n_defects)`:
        to randomly drop out n_atoms
        - `...add_position(positions).apply_defect_density(defect_probability)`:
        to drop out atoms with a certain probability
        - `...add_position(positions).scale(scale)`: to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...add_position(positions).rydberg`: to specify Rydberg coupling
        - `...add_position(positions).hyperfine`: to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...add_position(positions).show()`:
        shows your geometry in your web browser

    """

    if is_bearable(position, PositionArray) and is_bearable(
        filling, Optional[BoolArray]
    ):
        return self.add_position_ndarray(position, filling)
    elif is_bearable(position, List[Tuple[ScalarType, ScalarType]]) and is_bearable(
        filling, Optional[List[bool]]
    ):
        return self.add_position_list_tuples(position, filling)
    elif is_bearable(position, Tuple[ScalarType, ScalarType]) and is_bearable(
        filling, Optional[bool]
    ):
        return self.add_position_single_tupe(position, filling)
    else:
        raise TypeError("Invalid input types for add_position provided!")
apply_defect_count
apply_defect_count(
    n_defects: int, rng: Generator = np.random.default_rng()
)

Drop n_defects atoms from the geometry randomly. Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

Usage Example:
>>> from bloqade.analog.atom_arrangement import Chain
>>> import numpy as np
# set a custom seed for a numpy-based RNG
>>> custom_rng = np.random.default_rng(888)
# randomly remove two atoms from the geometry
>>> reg = Chain(11).apply_defect_count(2, custom_rng)
# you may also chain apply_defect_count calls
>>> reg.apply_defect_count(2, custom_rng)
# you can also use apply_defect_count on custom geometries
>>> from bloqade import start
>>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts) .apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
@beartype
def apply_defect_count(
    self, n_defects: int, rng: np.random.Generator = np.random.default_rng()
):
    """
    Drop `n_defects` atoms from the geometry randomly. Internally this occurs
    by setting certain sites to have a SiteFilling set to false indicating
    no atom is present at the coordinate.

    A default numpy-based Random Number Generator is used but you can
    explicitly override this by passing in your own.

    ### Usage Example:

    ```
    >>> from bloqade.analog.atom_arrangement import Chain
    >>> import numpy as np
    # set a custom seed for a numpy-based RNG
    >>> custom_rng = np.random.default_rng(888)
    # randomly remove two atoms from the geometry
    >>> reg = Chain(11).apply_defect_count(2, custom_rng)
    # you may also chain apply_defect_count calls
    >>> reg.apply_defect_count(2, custom_rng)
    # you can also use apply_defect_count on custom geometries
    >>> from bloqade import start
    >>> start.add_position([(0,0), (1,1)]).apply_defect_count(1, custom_rng)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...apply_defect_count(defect_counts).add_position(positions)`:
            to add more positions
        - `...apply_defect_count(defect_counts)
            .apply_defect_count(n_defects)`: to randomly drop out n_atoms
        - `...apply_defect_count(defect_counts)
            .apply_defect_density(defect_probability)`:
            to drop out atoms with a certain probability
        - `...apply_defect_count(defect_counts).scale(scale)`:
            to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...apply_defect_count(defect_counts).rydberg`: to specify
            Rydberg coupling
        - `...apply_defect_count(defect_counts).hyperfine`:
            to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...apply_defect_count(defect_counts).show()`:
            shows your geometry in your web browser
    """

    location_list = []
    for location_info in self.enumerate():
        location_list.append(location_info)

    filled_sites = []

    for index, location_info in enumerate(location_list):
        if location_info.filling is SiteFilling.filled:
            filled_sites.append(index)

    if n_defects >= len(filled_sites):
        raise ValueError(
            f"n_defects {n_defects} must be less than the number of filled sites "
            f"({len(filled_sites)})"
        )

    for _ in range(n_defects):
        index = rng.choice(filled_sites)
        location_list[index] = LocationInfo.create(
            location_list[index].position,
            (False if location_list[index].filling is SiteFilling.filled else True),
        )
        filled_sites.remove(index)

    return ListOfLocations(location_list)
apply_defect_density
apply_defect_density(
    defect_probability: float,
    rng: Generator = np.random.default_rng(),
)

Drop atoms randomly with defect_probability probability (range of 0 to 1). Internally this occurs by setting certain sites to have a SiteFilling set to false indicating no atom is present at the coordinate.

A default numpy-based Random Number Generator is used but you can explicitly override this by passing in your own.

Usage Example:
>>> from bloqade.analog.atom_arrangement import Chain
>>> import numpy as np
# set a custom seed for a numpy-based RNG
>>> custom_rng = np.random.default_rng(888)
# randomly remove two atoms from the geometry
>>> reg = Chain(11).apply_defect_density(0.2, custom_rng)
# you may also chain apply_defect_density calls
>>> reg.apply_defect_count(0.1, custom_rng)
# you can also use apply_defect_density on custom geometries
>>> from bloqade import start
>>> start.add_position([(0,0), (1,1)])
.apply_defect_density(0.5, custom_rng)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...apply_defect_count(defect_counts).add_position(positions): to add more positions
    • ...apply_defect_count(defect_counts).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...apply_defect_count(defect_counts) .apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...apply_defect_count(defect_counts).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...apply_defect_count(defect_counts).rydberg: to specify Rydberg coupling
    • ...apply_defect_count(defect_counts).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...apply_defect_count(defect_counts).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
@beartype
def apply_defect_density(
    self,
    defect_probability: float,
    rng: np.random.Generator = np.random.default_rng(),
):
    """
    Drop atoms randomly with `defect_probability` probability (range of 0 to 1).
    Internally this occurs by setting certain sites to have a SiteFilling
    set to false indicating no atom is present at the coordinate.

    A default numpy-based Random Number Generator is used but you can
    explicitly override this by passing in your own.

    ### Usage Example:

    ```
    >>> from bloqade.analog.atom_arrangement import Chain
    >>> import numpy as np
    # set a custom seed for a numpy-based RNG
    >>> custom_rng = np.random.default_rng(888)
    # randomly remove two atoms from the geometry
    >>> reg = Chain(11).apply_defect_density(0.2, custom_rng)
    # you may also chain apply_defect_density calls
    >>> reg.apply_defect_count(0.1, custom_rng)
    # you can also use apply_defect_density on custom geometries
    >>> from bloqade import start
    >>> start.add_position([(0,0), (1,1)])
    .apply_defect_density(0.5, custom_rng)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...apply_defect_count(defect_counts).add_position(positions)`:
        to add more positions
        - `...apply_defect_count(defect_counts).apply_defect_count(n_defects)`:
        to randomly drop out n_atoms
        - `...apply_defect_count(defect_counts)
        .apply_defect_density(defect_probability)`:
        to drop out atoms with a certain probability
        - `...apply_defect_count(defect_counts).scale(scale)`:
        to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...apply_defect_count(defect_counts).rydberg`:
        to specify Rydberg coupling
        - `...apply_defect_count(defect_counts).hyperfine`:
        to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...apply_defect_count(defect_counts).show()`:
        shows your geometry in your web browser
    """

    p = min(1, max(0, defect_probability))
    location_list = []

    for location_info in self.enumerate():
        if rng.random() < p:
            location_list.append(
                LocationInfo.create(
                    location_info.position,
                    (
                        False
                        if location_info.filling is SiteFilling.filled
                        else True
                    ),
                )
            )
        else:
            location_list.append(location_info)

    return ListOfLocations(location_list=location_list)
enumerate
enumerate() -> Generator[LocationInfo, None, None]

enumerate all locations in the register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
128
129
130
def enumerate(self) -> Generator[LocationInfo, None, None]:
    """enumerate all locations in the register."""
    raise NotImplementedError
figure
figure(fig_kwargs=None, **assignments)

obtain a figure object from the atom arrangement.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
132
133
134
def figure(self, fig_kwargs=None, **assignments):
    """obtain a figure object from the atom arrangement."""
    return get_atom_arrangement_figure(self, fig_kwargs=fig_kwargs, **assignments)
rydberg_interaction
rydberg_interaction(**assignments) -> NDArray

calculate the Rydberg interaction matrix.

Parameters:

Name Type Description Default
**assignments

the values to assign to the variables in the register.

{}

Returns:

Name Type Description
NDArray NDArray

the Rydberg interaction matrix in the lower triangular form.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
def rydberg_interaction(self, **assignments) -> NDArray:
    """calculate the Rydberg interaction matrix.

    Args:
        **assignments: the values to assign to the variables in the register.

    Returns:
        NDArray: the Rydberg interaction matrix in the lower triangular form.

    """

    from bloqade.analog.constants import RB_C6

    # calculate the Interaction matrix
    V_ij = np.zeros((self.n_sites, self.n_sites))
    for i, site_i in enumerate(self.enumerate()):
        pos_i = np.array([float(ele(**assignments)) for ele in site_i.position])

        for j, site_j in enumerate(self.enumerate()):
            if j >= i:
                break  # enforce lower triangular form

            pos_j = np.array([float(ele(**assignments)) for ele in site_j.position])
            r_ij = np.linalg.norm(pos_i - pos_j)

            V_ij[i, j] = RB_C6 / r_ij**6

    return V_ij
scale
scale(scale: ScalarType)

Scale the geometry of your atoms.

Usage Example:
>>> reg = start.add_position([(0,0), (1,1)])
# atom positions are now (0,0), (2,2)
>>> new_reg = reg.scale(2)
# you may also use scale on pre-defined geometries
>>> from bloqade.analog.atom_arrangement import Chain
# atoms in the chain will now be 2 um apart versus
# the default 1 um
>>> Chain(11).scale(2)
  • Next possible steps are:
  • Continuing to build your geometry via:
    • ...add_position(positions).add_position(positions): to add more positions
    • ...add_position(positions).apply_defect_count(n_defects): to randomly drop out n_atoms
    • ...add_position(positions).apply_defect_density(defect_probability): to drop out atoms with a certain probability
    • ...add_position(positions).scale(scale): to scale the geometry
  • Targeting a level coupling once you're done with the atom geometry:
    • ...add_position(positions).rydberg: to specify Rydberg coupling
    • ...add_position(positions).hyperfine: to specify Hyperfine coupling
  • Visualizing your atom geometry:
    • ...add_position(positions).show(): shows your geometry in your web browser
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
@beartype
def scale(self, scale: ScalarType):
    """
    Scale the geometry of your atoms.

    ### Usage Example:
    ```
    >>> reg = start.add_position([(0,0), (1,1)])
    # atom positions are now (0,0), (2,2)
    >>> new_reg = reg.scale(2)
    # you may also use scale on pre-defined geometries
    >>> from bloqade.analog.atom_arrangement import Chain
    # atoms in the chain will now be 2 um apart versus
    # the default 1 um
    >>> Chain(11).scale(2)
    ```

    - Next possible steps are:
    - Continuing to build your geometry via:
        - `...add_position(positions).add_position(positions)`:
            to add more positions
        - `...add_position(positions).apply_defect_count(n_defects)`:
        to randomly drop out n_atoms
        - `...add_position(positions).apply_defect_density(defect_probability)`:
        to drop out atoms with a certain probability
        - `...add_position(positions).scale(scale)`: to scale the geometry
    - Targeting a level coupling once you're done with the atom geometry:
        - `...add_position(positions).rydberg`:
        to specify Rydberg coupling
        - `...add_position(positions).hyperfine`:
        to specify Hyperfine coupling
    - Visualizing your atom geometry:
        - `...add_position(positions).show()`:
        shows your geometry in your web browser

    """

    scale = cast(scale)
    location_list = []
    for location_info in self.enumerate():
        x, y = location_info.position
        new_position = (scale * x, scale * y)
        location_list.append(
            LocationInfo.create(new_position, bool(location_info.filling.value))
        )

    return ListOfLocations(location_list)
show
show(**assignments) -> None

Display the current program being defined with the given arguments and batch ID.

Parameters:

Name Type Description Default
*args

Additional arguments for display.

()
batch_id int

The batch ID to be displayed. Defaults to 0.

0
Note

This method uses the display_builder function to render the builder's state.

Example:

>>> class MyBuilder(Show):
...     pass
>>> builder = MyBuilder()
>>> builder.show()
>>> builder.show(batch_id=1)
>>> builder.show('arg1', 'arg2', batch_id=2)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
136
137
def show(self, **assignments) -> None:
    display_ir(self, assignments)

ListOfLocations

ListOfLocations(
    location_list: List[
        Union[LocationInfo, Tuple[ScalarType, ScalarType]]
    ] = [],
)

Bases: AtomArrangement

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
@beartype
def __init__(
    self,
    location_list: List[Union[LocationInfo, Tuple[ScalarType, ScalarType]]] = [],
):
    self.location_list = []
    for ele in location_list:
        if isinstance(ele, LocationInfo):
            self.location_list.append(ele)
        else:
            self.location_list.append(LocationInfo.create(ele, True))

    if self.location_list:
        self.__n_atoms = sum(
            1 for loc in self.location_list if loc.filling == SiteFilling.filled
        )
        self.__n_sites = len(self.location_list)
        self.__n_vacant = self.__n_sites - self.__n_atoms
        self.__n_dims = len(self.location_list[0].position)
    else:
        self.__n_sites = 0
        self.__n_atoms = 0
        self.__n_dims = None

    super().__init__()
n_atoms property
n_atoms

number of atoms (filled sites) in the register.

n_dims property
n_dims

number of dimensions in the register.

n_sites property
n_sites

number of sites in the register.

n_vacant property
n_vacant

number of vacant sites in the register.

enumerate
enumerate()

enumerate all locations in the register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
661
662
def enumerate(self):
    return iter(self.location_list)

ParallelRegister

ParallelRegister(parent: Optional[Builder] = None)

Bases: ProgramStart

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/builder/base.py
10
11
12
13
14
def __init__(
    self,
    parent: Optional["Builder"] = None,
) -> None:
    self.__parent__ = parent
n_atoms property
n_atoms

Return the number of atoms in the program.

Returns:

Name Type Description
int int

The number of atoms in the parsed register.

Raises:

Type Description
ValueError

If the register type is unsupported.

Note

If the register is of type ParallelRegister, the number of atoms is extracted from its internal register.

Example:

>>> class MyBuilder(Parse):
...     pass
>>> builder = MyBuilder()
>>> n_atoms = builder.n_atoms
show
show(**assignments) -> None

Display the current program being defined with the given arguments and batch ID.

Parameters:

Name Type Description Default
*args

Additional arguments for display.

()
batch_id int

The batch ID to be displayed. Defaults to 0.

0
Note

This method uses the display_builder function to render the builder's state.

Example:

>>> class MyBuilder(Show):
...     pass
>>> builder = MyBuilder()
>>> builder.show()
>>> builder.show(batch_id=1)
>>> builder.show('arg1', 'arg2', batch_id=2)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
559
560
def show(self, **assignments) -> None:
    display_ir(self, assignments)

ParallelRegisterInfo

ParallelRegisterInfo(parallel_register: ParallelRegister)

ParallelRegisterInfo

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/location/location.py
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
def __init__(self, parallel_register: ParallelRegister):
    atom_arrangement = parallel_register.atom_arrangement
    cluster_spacing = parallel_register.cluster_spacing

    if atom_arrangement.n_atoms > 0:
        # calculate bounding box
        # of this register
        location_iter = atom_arrangement.enumerate()
        (x, y) = next(location_iter).position
        x_min = x
        x_max = x
        y_min = y
        y_max = y

        for location_info in location_iter:
            (x, y) = location_info.position
            x_min = x.min(x_min)
            x_max = x.max(x_max)
            y_min = y.min(y_min)
            y_max = y.max(y_max)

        shift_x = (x_max - x_min) + cluster_spacing
        shift_y = (y_max - y_min) + cluster_spacing

        register_locations = [
            list(location_info.position)
            for location_info in atom_arrangement.enumerate()
        ]
        register_filling = [
            location_info.filling.value
            for location_info in atom_arrangement.enumerate()
        ]
        shift_vectors = [[shift_x, cast(0)], [cast(0), shift_y]]
    else:
        raise ValueError("No locations to parallelize.")

    self.register_locations = register_locations
    self.register_filling = register_filling
    self.shift_vectors = shift_vectors

routine

base

Routine

Bases: RoutineBase

Result of parsing a completed Builder string.

RoutineParse

Bases: Parse

parse
parse() -> Routine

Parse the program to return a Routine object.

Returns:

Name Type Description
Routine Routine

The parsed routine object.

Raises:

Type Description
ValueError

If the routine cannot be parsed.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/base.py
29
30
31
32
def parse(self: "RoutineBase") -> "Routine":
    if self.source is None:
        raise ValueError("Cannot parse a routine without a source Builder.")
    return self
parse_circuit
parse_circuit() -> AnalogCircuit

Parse the analog circuit from the program.

Returns:

Name Type Description
AnalogCircuit AnalogCircuit

The parsed analog circuit.

Raises:

Type Description
ValueError

If the circuit cannot be parsed.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/base.py
26
27
def parse_circuit(self: "RoutineBase") -> AnalogCircuit:
    return self.circuit
parse_register
parse_register() -> (
    Union[AtomArrangement, ParallelRegister]
)

Parse the arrangement of atoms in the program.

Returns:

Type Description
Union[AtomArrangement, ParallelRegister]

Union[AtomArrangement, ParallelRegister]: The parsed atom arrangement or parallel register.

Raises:

Type Description
ValueError

If the register cannot be parsed.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/base.py
20
21
def parse_register(self: "RoutineBase") -> Union[AtomArrangement, ParallelRegister]:
    return self.circuit.register
parse_sequence
parse_sequence() -> Sequence

Parse the pulse sequence part of the program.

Returns:

Name Type Description
Sequence Sequence

The parsed pulse sequence.

Raises:

Type Description
ValueError

If the sequence cannot be parsed.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/base.py
23
24
def parse_sequence(self: "RoutineBase") -> Sequence:
    return self.circuit.sequence

RoutineShow

Bases: Show

show
show(*args, batch_index: int = 0)

Show an interactive plot of the routine.

int

which parameter set out of the batch to use. Default is 0. If there are no batch parameters, use 0.

*args: Any Specify the parameters that are defined in the .args([...]) build step.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/base.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def show(self: "RoutineBase", *args, batch_index: int = 0):
    """Show an interactive plot of the routine.

    batch_index: int
        which parameter set out of the batch to use. Default is 0.
        If there are no batch parameters, use 0.

    *args: Any
        Specify the parameters that are defined in the `.args([...])` build step.

    """
    if self.source is None:
        raise ValueError("Cannot show a routine without a source Builder.")

    return self.source.show(*args, batch_id=batch_index)

bloqade

BloqadeEmulation dataclass

BloqadeEmulation(
    task_data: TaskData,
    compile_cache: Optional[CompileCache] = None,
)

Data class to hold the Hamiltonian and metadata for a given set of parameters

hamiltonian property
hamiltonian: RydbergHamiltonian

Return the Hamiltonian object for the given task data.

metadata property
metadata: NamedTuple

The metadata for the given task data.

evolve
evolve(
    state: Optional[StateVector] = None,
    solver_name: str = "dop853",
    atol: float = 1e-07,
    rtol: float = 1e-14,
    nsteps: int = 2147483647,
    times: Sequence[float] = (),
    interaction_picture: bool = False,
) -> Iterator[StateVector]

Evolve an initial state vector using the Hamiltonian

Parameters:

Name Type Description Default
state Optional[StateVector]

The initial state vector to

None
solver_name str

Which SciPy Solver to use. Defaults to

'dop853'
atol float

Absolute tolerance for ODE solver. Defaults

1e-07
rtol float

Relative tolerance for adaptive step in

1e-14
nsteps int

Maximum number of steps allowed per integration

2147483647
times Sequence[float]

The times to evaluate the state vector

()
interaction_picture bool

Use the interaction picture when

False

Returns:

Type Description
Iterator[StateVector]

Iterator[StateVector]: An iterator of the state vectors at each time step.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/bloqade.py
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def evolve(
    self,
    state: Optional[StateVector] = None,
    solver_name: str = "dop853",
    atol: float = 1e-7,
    rtol: float = 1e-14,
    nsteps: int = 2147483647,
    times: Sequence[float] = (),
    interaction_picture: bool = False,
) -> Iterator[StateVector]:
    """Evolve an initial state vector using the Hamiltonian

    Args:
        state (Optional[StateVector], optional): The initial state vector to
        evolve. if not provided, the zero state will be used. Defaults to None.
        solver_name (str, optional): Which SciPy Solver to use. Defaults to
        "dop853".
        atol (float, optional): Absolute tolerance for ODE solver. Defaults
        to 1e-14.
        rtol (float, optional): Relative tolerance for adaptive step in
        ODE solver. Defaults to 1e-7.
        nsteps (int, optional): Maximum number of steps allowed per integration
        step. Defaults to 2147483647.
        times (Sequence[float], optional): The times to evaluate the state vector
        at. Defaults to (). If not provided the state will be evaluated at
        the end of the bloqade program.
        interaction_picture (bool, optional): Use the interaction picture when
        solving schrodinger equation. Defaults to False.

    Returns:
        Iterator[StateVector]: An iterator of the state vectors at each time step.

    """
    state = self.zero_state(np.complex128) if state is None else state

    U = AnalogGate(self.hamiltonian)

    return U.apply(
        state,
        times=times,
        solver_name=solver_name,
        atol=atol,
        rtol=rtol,
        nsteps=nsteps,
        interaction_picture=interaction_picture,
    )
fock_state
fock_state(
    fock_state_str: str, dtype: dtype = np.float64
) -> StateVector

Return the fock state for the given Hamiltonian.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/bloqade.py
91
92
93
94
95
96
97
98
def fock_state(
    self, fock_state_str: str, dtype: np.dtype = np.float64
) -> StateVector:
    """Return the fock state for the given Hamiltonian."""
    index = self.hamiltonian.space.fock_state_to_index(fock_state_str)
    data = np.zeros(self.hamiltonian.space.size, dtype=dtype)
    data[index] = 1
    return StateVector(data, self.hamiltonian.space)
zero_state
zero_state(dtype: dtype = np.float64) -> StateVector

Return the zero state for the given Hamiltonian.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/bloqade.py
87
88
89
def zero_state(self, dtype: np.dtype = np.float64) -> StateVector:
    """Return the zero state for the given Hamiltonian."""
    return self.hamiltonian.space.zero_state(dtype)

BloqadePythonRoutine

Bases: RoutineBase

hamiltonian
hamiltonian(
    *args: LiteralType,
    blockade_radius: float = 0.0,
    use_hyperfine: bool = False,
    waveform_runtime: str = "interpret",
    cache_matrices: bool = False
) -> List[BloqadeEmulation]

Generates a list of BloqadeEmulation objects which contain the Hamiltonian of your program.

If you have a variable(s) in your program you have assigned multiple values via batch_assign() there will be multiple BloqadeEmulation objects, one for each value. On the other hand if the program only assumes a singular value per each variable, there will only be one BloqadeEmulation object but it will still be encapsulated in a list.

Parameters:

Name Type Description Default
*args LiteralType

If your program has a variable that was declared as run-time assignable via .args you may pass a value to it here. If there are multiple variables declared via .args the order in which you assign values to those variables through this argument should follow the order in which the declaration occurred.

()
blockade_radius float

The radius in which atoms blockade eachother. Default value is 0.0 micrometers.

0.0
use_hyperfine bool

Should the Hamiltonian account for hyperfine levels. Default value is False.

False
waveform_runtime str

Specify which runtime to use for waveforms. If "numba" is specify the waveform is compiled, otherwise it is interpreted via the "interpret" argument. Defaults to "interpret".

'interpret'
cache_matrices bool

Speed up Hamiltonian generation by reusing data (when possible) from previously generated Hamiltonians. Default value is False.

False

Returns:

Type Description
List[BloqadeEmulation]

List[BloqadeEmulation]

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/bloqade.py
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
def hamiltonian(
    self,
    *args: LiteralType,
    blockade_radius: float = 0.0,
    use_hyperfine: bool = False,
    waveform_runtime: str = "interpret",
    cache_matrices: bool = False,
) -> List[BloqadeEmulation]:
    """
    Generates a list of BloqadeEmulation objects which contain the Hamiltonian of your program.

    If you have a variable(s) in your program you have assigned multiple values via `batch_assign()`
    there will be multiple `BloqadeEmulation` objects, one for each value. On the other hand
    if the program only assumes a singular value per each variable, there will only be
    one `BloqadeEmulation` object but it will still be encapsulated in a list.


    Args:
        *args (LiteralType): If your program has a variable that was declared as run-time assignable
            via `.args` you may pass a value to it here. If there are multiple
            variables declared via `.args` the order in which you assign values to those variables
            through this argument should follow the order in which the declaration occurred.
        blockade_radius (float): The radius in which atoms blockade eachother. Default value is 0.0 micrometers.
        use_hyperfine (bool): Should the Hamiltonian account for hyperfine levels. Default value is False.
        waveform_runtime (str): Specify which runtime to use for waveforms. If "numba" is specify the waveform
            is compiled, otherwise it is interpreted via the "interpret" argument. Defaults to "interpret".
        cache_matrices (bool): Speed up Hamiltonian generation by reusing data (when possible) from previously generated Hamiltonians.
            Default value is False.

    Returns:
        List[BloqadeEmulation]

    """
    ir_iter = self._generate_ir(
        args, blockade_radius, waveform_runtime, use_hyperfine
    )

    if cache_matrices:
        compile_cache = CompileCache()
    else:
        compile_cache = None

    return [
        BloqadeEmulation(task_data, compile_cache=compile_cache)
        for task_data in ir_iter
    ]
run
run(
    shots: int,
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    blockade_radius: float = 0.0,
    waveform_runtime: str = "interpret",
    interaction_picture: bool = False,
    cache_matrices: bool = False,
    multiprocessing: bool = False,
    num_workers: Optional[int] = None,
    solver_name: str = "dop853",
    atol: float = 1e-07,
    rtol: float = 1e-14,
    nsteps: int = 2147483647,
) -> LocalBatch

Run the current program using bloqade python backend

Parameters:

Name Type Description Default
shots int

number of shots after running state vector simulation

required
args Tuple[LiteralType, ...]

The values for parameters defined

()
name Optional[str]

Name to give this run. Defaults to None.

None
blockade_radius float

Use the Blockade subspace given a

0.0
waveform_runtime str

(bool, optional): Use Numba to compile the waveforms,

'interpret'
interaction_picture bool

Use the interaction picture when

False
cache_matrices bool

Reuse previously evaluated matrcies when

False
multiprocessing bool

Use multiple processes to process the

False
num_workers Optional[int]

Number of processes to run with

None
solver_name str

Which SciPy Solver to use. Defaults to

'dop853'
atol float

Absolute tolerance for ODE solver. Defaults to

1e-07
rtol float

Relative tolerance for adaptive step in ODE solver.

1e-14
nsteps int

Maximum number of steps allowed per integration

2147483647

Raises:

Type Description
ValueError

Cannot use multiprocessing and cache_matrices at the same time.

Returns:

Name Type Description
LocalBatch LocalBatch

Batch of local tasks that have been executed.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/bloqade.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
@beartype
def run(
    self,
    shots: int,
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    blockade_radius: float = 0.0,
    waveform_runtime: str = "interpret",
    interaction_picture: bool = False,
    cache_matrices: bool = False,
    multiprocessing: bool = False,
    num_workers: Optional[int] = None,
    solver_name: str = "dop853",
    atol: float = 1e-7,
    rtol: float = 1e-14,
    nsteps: int = 2_147_483_647,
) -> LocalBatch:
    """Run the current program using bloqade python backend

    Args:
        shots (int): number of shots after running state vector simulation
        args (Tuple[LiteralType, ...], optional): The values for parameters defined
        in `args`. Defaults to ().
        name (Optional[str], optional): Name to give this run. Defaults to None.
        blockade_radius (float, optional): Use the Blockade subspace given a
        particular radius. Defaults to 0.0.
        waveform_runtime: (bool, optional): Use Numba to compile the waveforms,
        Defaults to False.
        interaction_picture (bool, optional): Use the interaction picture when
        solving schrodinger equation. Defaults to False.
        cache_matrices (bool, optional): Reuse previously evaluated matrcies when
        possible. Defaults to False.
        multiprocessing (bool, optional): Use multiple processes to process the
        batches. Defaults to False.
        num_workers (Optional[int], optional): Number of processes to run with
        multiprocessing. Defaults to None.
        solver_name (str, optional): Which SciPy Solver to use. Defaults to
        "dop853".
        atol (float, optional): Absolute tolerance for ODE solver. Defaults to
        1e-14.
        rtol (float, optional): Relative tolerance for adaptive step in ODE solver.
        Defaults to 1e-7.
        nsteps (int, optional): Maximum number of steps allowed per integration
        step. Defaults to 2_147_483_647, the maximum value.

    Raises:
        ValueError: Cannot use multiprocessing and cache_matrices at the same time.

    Returns:
        LocalBatch: Batch of local tasks that have been executed.
    """
    if multiprocessing and cache_matrices:
        raise ValueError(
            "Cannot use multiprocessing and cache_matrices at the same time."
        )

    compile_options = dict(
        shots=shots,
        args=args,
        name=name,
        blockade_radius=blockade_radius,
        cache_matrices=cache_matrices,
        waveform_runtime=waveform_runtime,
    )

    solver_options = dict(
        multiprocessing=multiprocessing,
        num_workers=num_workers,
        solver_name=solver_name,
        atol=atol,
        rtol=rtol,
        nsteps=nsteps,
        interaction_picture=interaction_picture,
    )

    batch = self._compile(**compile_options)
    batch._run(**solver_options)

    return batch
run_callback
run_callback(
    callback: Callable[
        [StateVector, NamedTuple, RydbergHamiltonian, Any],
        Any,
    ],
    program_args: Tuple[LiteralType, ...] = (),
    callback_args: Tuple = (),
    ignore_exceptions: bool = False,
    blockade_radius: float = 0.0,
    waveform_runtime: str = "interpret",
    interaction_picture: bool = False,
    cache_matrices: bool = False,
    multiprocessing: bool = False,
    num_workers: Optional[int] = None,
    solver_name: str = "dop853",
    atol: float = 1e-07,
    rtol: float = 1e-14,
    nsteps: int = 2147483647,
    use_hyperfine: bool = False,
) -> List

Run state-vector simulation with a callback to access full state-vector from emulator

Parameters:

Name Type Description Default
callback Callable[[StateVector, Metadata, RydbergHamiltonian, Any], Any]
required
program_args Tuple[LiteralType, ...]

The values for parameters

()
callback_args Tuple[Any, ...]

Extra arguments to pass into

()
ignore_exceptions bool

(bool, optional) If True any exception raised during

False
blockade_radius float

Use the Blockade subspace given a

0.0
waveform_runtime str

(str, optional): Specify which runtime to use for

'interpret'
interaction_picture bool

Use the interaction picture when

False
cache_matrices bool

Reuse previously evaluated matrcies when

False
multiprocessing bool

Use multiple processes to process the

False
num_workers Optional[int]

Number of processes to run with

None
solver_name str

Which SciPy Solver to use. Defaults to

'dop853'
atol float

Absolute tolerance for ODE solver. Defaults to

1e-07
rtol float

Relative tolerance for adaptive step in ODE solver.

1e-14
nsteps int

Maximum number of steps allowed per integration

2147483647

Returns:

Name Type Description
List List

List of resulting outputs from the callbacks

Raises:

Type Description
RuntimeError

Raises the first error that occurs, only if

Note

For the callback function, first argument is the many-body wavefunction as a 1D complex numpy array, the second argument is of type Metadata which is a Named Tuple where the fields correspond to the parameters of that given task, RydbergHamiltonian is the object that contains the Hamiltonian used to generate the evolution for that task, Finally any optional positional arguments are allowed after that. The return value can be anything, the results will be collected in a list for each task in the batch.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/bloqade.py
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
@beartype
def run_callback(
    self,
    callback: Callable[[StateVector, NamedTuple, RydbergHamiltonian, Any], Any],
    program_args: Tuple[LiteralType, ...] = (),
    callback_args: Tuple = (),
    ignore_exceptions: bool = False,
    blockade_radius: float = 0.0,
    waveform_runtime: str = "interpret",
    interaction_picture: bool = False,
    cache_matrices: bool = False,
    multiprocessing: bool = False,
    num_workers: Optional[int] = None,
    solver_name: str = "dop853",
    atol: float = 1e-7,
    rtol: float = 1e-14,
    nsteps: int = 2_147_483_647,
    use_hyperfine: bool = False,
) -> List:
    """Run state-vector simulation with a callback to access full state-vector from
    emulator

    Args:
        callback (Callable[[StateVector, Metadata, RydbergHamiltonian, Any], Any]):
        The callback function to run for each task in batch. See note below for more
        details about the signature of the function.
        program_args (Tuple[LiteralType, ...], optional): The values for parameters
        defined in `args`. Defaults to ().
        callback_args (Tuple[Any,...], optional): Extra arguments to pass into
        ignore_exceptions: (bool, optional) If `True` any exception raised during
        a task will be saved instead of the resulting output of the callback,
        otherwise the first exception by task number will be raised after *all*
        tasks have executed. Defaults to False.
        blockade_radius (float, optional): Use the Blockade subspace given a
        particular radius. Defaults to 0.0.
        waveform_runtime: (str, optional): Specify which runtime to use for
        waveforms. Defaults to "interpret".
        interaction_picture (bool, optional): Use the interaction picture when
        solving schrodinger equation. Defaults to False.
        cache_matrices (bool, optional): Reuse previously evaluated matrcies when
        possible. Defaults to False.
        multiprocessing (bool, optional): Use multiple processes to process the
        batches. Defaults to False.
        num_workers (Optional[int], optional): Number of processes to run with
        multiprocessing. Defaults to None.
        solver_name (str, optional): Which SciPy Solver to use. Defaults to
        "dop853".
        atol (float, optional): Absolute tolerance for ODE solver. Defaults to
        1e-14.
        rtol (float, optional): Relative tolerance for adaptive step in ODE solver.
        Defaults to 1e-7.
        nsteps (int, optional): Maximum number of steps allowed per integration
        step. Defaults to 2_147_483_647, the maximum value.

    Returns:
        List: List of resulting outputs from the callbacks

    Raises:
        RuntimeError: Raises the first error that occurs, only if
        `ignore_exceptions=False`.

    Note:
        For the `callback` function, first argument is the many-body wavefunction
        as a 1D complex numpy array, the second argument is of type `Metadata` which
        is a Named Tuple where the fields correspond to the parameters of that given
        task, RydbergHamiltonian is the object that contains the Hamiltonian used to
        generate the evolution for that task, Finally any optional positional
        arguments are allowed after that. The return value can be anything, the
        results will be collected in a list for each task in the batch.


    """
    if multiprocessing:
        from multiprocessing import Queue, Process, cpu_count
    else:
        from queue import Queue

    if cache_matrices:
        compile_cache = CompileCache()
    else:
        compile_cache = None

    solver_args = dict(
        solver_name=solver_name,
        atol=atol,
        rtol=rtol,
        nsteps=nsteps,
        interaction_picture=interaction_picture,
    )

    runner = self.EmuRunner(
        compile_cache=compile_cache,
        solver_args=solver_args,
        callback=callback,
        callback_args=callback_args,
    )

    tasks = Queue()
    results = Queue()

    total_tasks = 0
    ir_iter = self._generate_ir(
        program_args, blockade_radius, waveform_runtime, use_hyperfine
    )
    for task_data in ir_iter:
        task_number = task_data.task_id
        emulator_ir = task_data.emulator_ir
        metadata = task_data.metadata_dict
        total_tasks += 1
        tasks.put((task_number, (emulator_ir, metadata)))

    workers = []
    if multiprocessing:
        num_workers = max(int(num_workers or cpu_count()), 1)
        num_workers = min(total_tasks, num_workers)

        for _ in range(num_workers):
            worker = Process(
                target=BloqadePythonRoutine.process_tasks,
                args=(runner, tasks, results),
            )
            worker.start()

            workers.append(worker)
    else:
        self.process_tasks(runner, tasks, results)

    # blocks until all
    # results have been fetched
    # from the id_results Queue
    id_results = []
    for i in range(total_tasks):
        id_results.append(results.get())

    if workers:
        for worker in workers:
            worker.join()

        tasks.close()
        results.close()

    id_results.sort(key=lambda x: x[0])
    results = []

    for task_id, result in id_results:
        if not ignore_exceptions and isinstance(result, BaseException):
            try:
                raise result
            except BaseException:
                raise RuntimeError(
                    f"{result.__class__.__name__} occured during child process "
                    f"running for task number {task_id}:\n{traceback.format_exc()}"
                )

        results.append(result)

    return results

TaskData dataclass

TaskData(
    task_id: int,
    emulator_ir: EmulatorProgram,
    metadata_dict: Dict[str, LiteralType],
)

Data class to hold the program ir and metadata for a given set of parameters

braket

BraketHardwareRoutine

Bases: RoutineBase

__call__
__call__(
    *args: LiteralType,
    shots: int = 1,
    name: Optional[str] = None,
    use_experimental: bool = False,
    shuffle: bool = False,
    **kwargs
)

Compile to a RemoteBatch, which contain Braket backend specific tasks, run_async to Braket, and wait until the results are coming back.

Note

This is sync, and will wait until remote results finished.

Parameters:

Name Type Description Default
shots int

number of shots

1
args LiteralType

additional arguments for args variables.

()
name str

custom name of the batch

None
shuffle bool

shuffle the order of jobs

False
Return

RemoteBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/braket.py
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
@beartype
def __call__(
    self,
    *args: LiteralType,
    shots: int = 1,
    name: Optional[str] = None,
    use_experimental: bool = False,
    shuffle: bool = False,
    **kwargs,
):
    """
    Compile to a RemoteBatch, which contain
    Braket backend specific tasks, run_async to Braket,
    and wait until the results are coming back.

    Note:
        This is sync, and will wait until remote results
        finished.

    Args:
        shots (int): number of shots
        args: additional arguments for args variables.
        name (str): custom name of the batch
        shuffle (bool): shuffle the order of jobs

    Return:
        RemoteBatch

    """
    return self.run(shots, args, name, use_experimental, shuffle, **kwargs)
run
run(
    shots: int,
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    use_experimental: bool = False,
    shuffle: bool = False,
    **kwargs
) -> RemoteBatch

Compile to a RemoteBatch, which contain Braket backend specific tasks, run_async to Braket, and wait until the results are coming back.

Note

This is sync, and will wait until remote results finished.

Parameters:

Name Type Description Default
shots int

number of shots

required
args Tuple

additional arguments

()
name str

custom name of the batch

None
shuffle bool

shuffle the order of jobs

False
Return

RemoteBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/braket.py
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
@beartype
def run(
    self,
    shots: int,
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    use_experimental: bool = False,
    shuffle: bool = False,
    **kwargs,
) -> RemoteBatch:
    """
    Compile to a RemoteBatch, which contain
    Braket backend specific tasks, run_async to Braket,
    and wait until the results are coming back.

    Note:
        This is sync, and will wait until remote results
        finished.

    Args:
        shots (int): number of shots
        args (Tuple): additional arguments
        name (str): custom name of the batch
        shuffle (bool): shuffle the order of jobs

    Return:
        RemoteBatch

    """

    batch = self.run_async(shots, args, name, use_experimental, shuffle, **kwargs)
    batch.pull()
    return batch
run_async
run_async(
    shots: int,
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    use_experimental: bool = False,
    shuffle: bool = False,
    **kwargs
) -> RemoteBatch

Compile to a RemoteBatch, which contain Braket backend specific tasks, and run_async to Braket.

Note

This is async.

Parameters:

Name Type Description Default
shots int

number of shots

required
args Tuple

Values of the parameter defined in args, defaults to ()

()
name str | None

custom name of the batch, defaults to None

None
use_experimental bool

Use experimental hardware capabilities

False
shuffle bool

shuffle the order of jobs

False
Return

RemoteBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/braket.py
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
@beartype
def run_async(
    self,
    shots: int,
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    use_experimental: bool = False,
    shuffle: bool = False,
    **kwargs,
) -> RemoteBatch:
    """
    Compile to a RemoteBatch, which contain
    Braket backend specific tasks, and run_async to Braket.

    Note:
        This is async.

    Args:
        shots (int): number of shots
        args (Tuple): Values of the parameter defined in `args`, defaults to ()
        name (str | None): custom name of the batch, defaults to None
        use_experimental (bool): Use experimental hardware capabilities
        shuffle (bool): shuffle the order of jobs

    Return:
        RemoteBatch

    """

    batch = self._compile(shots, use_experimental, args, name)
    batch._submit(shuffle, **kwargs)
    return batch

BraketLocalEmulatorRoutine

Bases: RoutineBase

__call__
__call__(
    *args: LiteralType,
    shots: int = 1,
    name: Optional[str] = None,
    multiprocessing: bool = False,
    num_workers: Optional[int] = None,
    **kwargs
)

Compile to a LocalBatch, and run. The LocalBatch contain tasks to run on local emulator.

Note

This is sync, and will wait until remote results finished.

Parameters:

Name Type Description Default
shots int

number of shots

1
args LiteralType

additional arguments for args variables.

()
multiprocessing bool

enable multi-process

False
num_workers int

number of workers to run the emulator

None
Return

LocalBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/braket.py
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
@beartype
def __call__(
    self,
    *args: LiteralType,
    shots: int = 1,
    name: Optional[str] = None,
    multiprocessing: bool = False,
    num_workers: Optional[int] = None,
    **kwargs,
):
    """
    Compile to a LocalBatch, and run.
    The LocalBatch contain tasks to run on local emulator.

    Note:
        This is sync, and will wait until remote results
        finished.

    Args:
        shots (int): number of shots
        args: additional arguments for args variables.
        multiprocessing (bool): enable multi-process
        num_workers (int): number of workers to run the emulator

    Return:
        LocalBatch

    """
    return self.run(
        shots,
        args,
        name,
        multiprocessing=multiprocessing,
        num_workers=num_workers,
        **kwargs,
    )
run
run(
    shots: int,
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    multiprocessing: bool = False,
    num_workers: Optional[int] = None,
    **kwargs
) -> LocalBatch

Compile to a LocalBatch, and run. The LocalBatch contain tasks to run on local emulator.

Note

This is sync, and will wait until remote results finished.

Parameters:

Name Type Description Default
shots int

number of shots

required
args Tuple[LiteralType, ...]

additional arguments for args variables.

()
multiprocessing bool

enable multi-process

False
num_workers int

number of workers to run the emulator

None
Return

LocalBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/braket.py
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
@beartype
def run(
    self,
    shots: int,
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    multiprocessing: bool = False,
    num_workers: Optional[int] = None,
    **kwargs,
) -> LocalBatch:
    """
    Compile to a LocalBatch, and run.
    The LocalBatch contain tasks to run on local emulator.

    Note:
        This is sync, and will wait until remote results
        finished.

    Args:
        shots (int): number of shots
        args: additional arguments for args variables.
        multiprocessing (bool): enable multi-process
        num_workers (int): number of workers to run the emulator

    Return:
        LocalBatch

    """

    batch = self._compile(shots, args, name)
    batch._run(multiprocessing=multiprocessing, num_workers=num_workers, **kwargs)
    return batch

quera

CustomSubmissionRoutine

Bases: RoutineBase

run
run(
    shots: int,
    RemoteTask: type[RemoteTaskType],
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    use_experimental: bool = False,
    shuffle: bool = False,
    **kwargs
) -> RemoteBatch

Run the custom task and return the result.

Parameters:

Name Type Description Default
shots int

number of shots

required
RemoteTask type

type of the remote task, must subclass of CustomRemoteTaskABC

required
args Tuple

additional arguments for remaining un

()
name str

name of the batch object

None
shuffle bool

shuffle the order of jobs

False
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/quera.py
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
@beartype
def run(
    self,
    shots: int,
    RemoteTask: type[RemoteTaskType],
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    use_experimental: bool = False,
    shuffle: bool = False,
    **kwargs,
) -> RemoteBatch:
    """Run the custom task and return the result.

    Args:
        shots (int): number of shots
        RemoteTask (type): type of the remote task, must subclass of CustomRemoteTaskABC
        args (Tuple): additional arguments for remaining un
        name (str): name of the batch object
        shuffle (bool): shuffle the order of jobs
    """
    if not callable(getattr(RemoteTask, "pull", None)):
        raise TypeError(
            f"{RemoteTask} must have a `pull` method for executing `run`."
        )

    batch = self.run_async(
        shots, RemoteTask, args, name, use_experimental, shuffle, **kwargs
    )
    batch.pull()
    return batch
run_async
run_async(
    shots: int,
    RemoteTask: type[RemoteTaskType],
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    use_experimental: bool = False,
    shuffle: bool = False,
    **kwargs
) -> RemoteBatch

Compile to a RemoteBatch, which contain QuEra backend specific tasks, and run_async through QuEra service.

Parameters:

Name Type Description Default
shots int

number of shots

required
args Tuple

additional arguments

()
name str

custom name of the batch

None
shuffle bool

shuffle the order of jobs

False
Return

RemoteBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/quera.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
@beartype
def run_async(
    self,
    shots: int,
    RemoteTask: type[RemoteTaskType],
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    use_experimental: bool = False,
    shuffle: bool = False,
    **kwargs,
) -> RemoteBatch:
    """
    Compile to a RemoteBatch, which contain
        QuEra backend specific tasks,
        and run_async through QuEra service.

    Args:
        shots (int): number of shots
        args (Tuple): additional arguments
        name (str): custom name of the batch
        shuffle (bool): shuffle the order of jobs

    Return:
        RemoteBatch

    """
    batch = self._compile_custom_batch(
        shots, RemoteTask, use_experimental, args, name
    )
    batch._submit(shuffle, **kwargs)
    return batch
submit
submit(
    shots: int,
    url: str,
    json_body_template: str,
    method: str = "POST",
    args: Tuple[LiteralType] = (),
    request_options: Dict[str, Any] = {},
    use_experimental: bool = False,
    sleep_time: float = 0.1,
) -> List[Tuple[NamedTuple, Response]]

Compile to QuEraTaskSpecification and submit to a custom service.

Parameters:

Name Type Description Default
shots int

number of shots

required
url str

url of the custom service

required
json_body_template str

json body template, must contain '{task_ir}'

required
method str

http method to be used. Defaults to "POST".

'POST'
args Tuple[LiteralType]

additional arguments to be passed into the

()
request_options Dict[str, Any]

additional options to be passed into the request method,

{}
use_experimental bool

Enable experimental hardware capabilities

False
sleep_time float

time to sleep between each request. Defaults to 0.1.

0.1

Returns:

Type Description
List[Tuple[NamedTuple, Response]]

List[Tuple[NamedTuple, Response]]: List of parameters for each batch in

List[Tuple[NamedTuple, Response]]

the task and the response from the post request.

Examples:

Here is a simple example of how to use this method. Note the body_template has double curly braces on the outside to escape the string formatting.

>>> body_template = "{{"token": "my_token", "task": {task_ir}}}"
>>> responses = (
    program.quera.custom.submit(
        100,
        "http://my_custom_service.com",
        body_template
    )
)
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/quera.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
def submit(
    self,
    shots: int,
    url: str,
    json_body_template: str,
    method: str = "POST",
    args: Tuple[LiteralType] = (),
    request_options: Dict[str, Any] = {},
    use_experimental: bool = False,
    sleep_time: float = 0.1,
) -> List[Tuple[NamedTuple, Response]]:
    """Compile to QuEraTaskSpecification and submit to a custom service.

    Args:
        shots (int): number of shots
        url (str): url of the custom service
        json_body_template (str): json body template, must contain '{task_ir}'
        which is a placeholder for a string representation of the task ir.
        The task ir string will be inserted into the template with
        `json_body_template.format(task_ir=task_ir_string)`.
        to be replaced by QuEraTaskSpecification
        method (str): http method to be used. Defaults to "POST".
        args (Tuple[LiteralType]): additional arguments to be passed into the
        compiler coming from `args` option of the build. Defaults to ().
        request_options: additional options to be passed into the request method,
        Note the `data` option will be overwritten by the
        `json_body_template.format(task_ir=task_ir_string)`.
        use_experimental (bool): Enable experimental hardware capabilities
        sleep_time (float): time to sleep between each request. Defaults to 0.1.

    Returns:
        List[Tuple[NamedTuple, Response]]: List of parameters for each batch in
        the task and the response from the post request.

    Examples:
        Here is a simple example of how to use this method. Note the body_template
        has double curly braces on the outside to escape the string formatting.

    ```python
    >>> body_template = "{{"token": "my_token", "task": {task_ir}}}"
    >>> responses = (
        program.quera.custom.submit(
            100,
            "http://my_custom_service.com",
            body_template
        )
    )
    ```
    """

    if r"{task_ir}" not in json_body_template:
        raise ValueError(r"body_template must contain '{task_ir}'")

    partial_eval = json_body_template.format(task_ir='"task_ir"')
    try:
        _ = json.loads(partial_eval)
    except json.JSONDecodeError as e:
        raise ValueError(
            "body_template must be a valid json template. "
            'When evaluating template with task_ir="task_ir", '
            f"the template evaluated to: {partial_eval!r}.\n"
            f"JSONDecodeError: {e}"
        )

    out = []
    for metadata, task_ir in self._compile_single(shots, use_experimental, args):
        json_request_body = json_body_template.format(
            task_ir=task_ir.json(exclude_none=True, exclude_unset=True)
        )
        request_options.update(data=json_request_body)
        response = request(method, url, **request_options)
        out.append((metadata, response))
        time.sleep(sleep_time)

    return out

QuEraHardwareRoutine

Bases: RoutineBase

run_async
run_async(
    shots: int,
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    use_experimental: bool = False,
    shuffle: bool = False,
    **kwargs
) -> RemoteBatch

Compile to a RemoteBatch, which contain QuEra backend specific tasks, and run_async through QuEra service.

Parameters:

Name Type Description Default
shots int

number of shots

required
args Tuple

additional arguments

()
name str

custom name of the batch

None
shuffle bool

shuffle the order of jobs

False
Return

RemoteBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/routine/quera.py
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
@beartype
def run_async(
    self,
    shots: int,
    args: Tuple[LiteralType, ...] = (),
    name: Optional[str] = None,
    use_experimental: bool = False,
    shuffle: bool = False,
    **kwargs,
) -> RemoteBatch:
    """
    Compile to a RemoteBatch, which contain
        QuEra backend specific tasks,
        and run_async through QuEra service.

    Args:
        shots (int): number of shots
        args (Tuple): additional arguments
        name (str): custom name of the batch
        shuffle (bool): shuffle the order of jobs

    Return:
        RemoteBatch

    """
    batch = self._compile(shots, use_experimental, args, name)
    batch._submit(shuffle, **kwargs)
    return batch

scalar

Literal

Bases: Real

value instance-attribute

value: Decimal

Scalar Literal, which stores a decimaal value instance.

Parameters:

Name Type Description Default
value Decimal

decimal value instance

required

Scalar

Base class for all scalar expressions.

<scalar> ::= <literal>
| <variable>
| <default>
| <negative>
| <add>
| <mul>
| <min>
| <max>
| <slice>
| <inverval>

<mul> ::= <scalar> '*' <scalar>
<add> ::= <scalar> '+' <scalar>
<min> ::= 'min' <scalar>+
<max> ::= 'max' <scalar>+
<slice> ::= <scalar expr> '[' <interval> ']'
<interval> ::= <scalar expr> '..' <scalar expr>
<real> ::= <literal> | <var>

Variable

Bases: Real

Variable, which stores a variable name.

Parameters:

Name Type Description Default
name str

variable instance.

required

cast

cast(py) -> Scalar
  1. cast Real number (or list/tuple of Real numbers) to [Scalar Literal][bloqade.ir.scalar.Literal].

  2. cast str (or list/tuple of Real numbers) to [Scalar Variable][bloqade.ir.scalar.Variable].

Parameters:

Name Type Description Default
py Union[str, Real, Tuple[Real], List[Real]]

python object to cast

required

Returns:

Type Description
Scalar

Scalar

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/scalar.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
def cast(py) -> "Scalar":
    """
    1. cast Real number (or list/tuple of Real numbers)
    to [`Scalar Literal`][bloqade.ir.scalar.Literal].

    2. cast str (or list/tuple of Real numbers)
    to [`Scalar Variable`][bloqade.ir.scalar.Variable].

    Args:
        py (Union[str,Real,Tuple[Real],List[Real]]): python object to cast

    Returns:
        Scalar
    """
    ret = trycast(py)
    if ret is None:
        raise TypeError(f"Cannot cast {type(py)} to Scalar Literal")

    return ret

var

var(py: str) -> Variable

cast string (or list/tuple of strings) to [Variable][bloqade.ir.scalar.Variable].

Parameters:

Name Type Description Default
py Union[str, List[str]]

a string or list/tuple of strings

required

Returns:

Type Description
Variable

Union[Variable]

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/ir/scalar.py
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
def var(py: str) -> "Variable":
    """cast string (or list/tuple of strings)
    to [`Variable`][bloqade.ir.scalar.Variable].

    Args:
        py (Union[str, List[str]]): a string or list/tuple of strings

    Returns:
       Union[Variable]
    """
    ret = tryvar(py)
    if ret is None:
        raise TypeError(f"Cannot cast {type(py)} to Variable")

    return ret

serialize

dumps

dumps(
    o: Any, use_decimal: bool = True, **json_kwargs
) -> str

Serialize object to string

Parameters:

Name Type Description Default
o Any

the object to serialize

required
use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True
**json_kwargs

other arguments passed to json.dumps

{}

Returns:

Name Type Description
str str

the serialized object as a string

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/serialize.py
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
@beartype
def dumps(
    o: Any,
    use_decimal: bool = True,
    **json_kwargs,
) -> str:
    """Serialize object to string

    Args:
        o (Any): the object to serialize
        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.
        **json_kwargs: other arguments passed to json.dumps

    Returns:
        str: the serialized object as a string
    """
    if not isinstance(o, Serializer.types):
        raise TypeError(
            f"Object of type {type(o)} is not JSON serializable. "
            f"Only {Serializer.types} are supported."
        )
    return json.dumps(o, cls=Serializer, use_decimal=use_decimal, **json_kwargs)

load

load(
    fp: Union[TextIO, str],
    use_decimal: bool = True,
    **json_kwargs
)

Load object from file

Parameters:

Name Type Description Default
fp Union[TextIO, str]

the file path or file object

required
use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True
**json_kwargs

other arguments passed to json.load

{}

Returns:

Name Type Description
Any

the deserialized object

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/serialize.py
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
@beartype
def load(fp: Union[TextIO, str], use_decimal: bool = True, **json_kwargs):
    """Load object from file

    Args:
        fp (Union[TextIO, str]): the file path or file object
        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.
        **json_kwargs: other arguments passed to json.load

    Returns:
        Any: the deserialized object
    """
    load_bloqade()
    if isinstance(fp, str):
        with open(fp, "r") as f:
            return json.load(
                f,
                object_hook=Serializer.object_hook,
                use_decimal=use_decimal,
                **json_kwargs,
            )
    else:
        return json.load(
            fp,
            object_hook=Serializer.object_hook,
            use_decimal=use_decimal,
            **json_kwargs,
        )

loads

loads(s: str, use_decimal: bool = True, **json_kwargs)

Load object from string

Parameters:

Name Type Description Default
s str

the string to load

required
use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True
**json_kwargs

other arguments passed to json.loads

{}

Returns:

Name Type Description
Any

the deserialized object

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/serialize.py
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
@beartype
def loads(s: str, use_decimal: bool = True, **json_kwargs):
    """Load object from string

    Args:
        s (str): the string to load
        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.
        **json_kwargs: other arguments passed to json.loads

    Returns:
        Any: the deserialized object
    """
    load_bloqade()
    return json.loads(
        s, object_hook=Serializer.object_hook, use_decimal=use_decimal, **json_kwargs
    )

save

save(
    o: Any,
    fp: Union[TextIO, str],
    use_decimal=True,
    **json_kwargs
) -> None

Serialize object to file

Parameters:

Name Type Description Default
o Any

the object to serialize

required
fp Union[TextIO, str]

the file path or file object

required
use_decimal bool

use decimal.Decimal for numbers. Defaults to True.

True
**json_kwargs

other arguments passed to json.dump

{}

Returns:

Type Description
None

None

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/serialize.py
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
@beartype
def save(
    o: Any,
    fp: Union[TextIO, str],
    use_decimal=True,
    **json_kwargs,
) -> None:
    """Serialize object to file

    Args:
        o (Any): the object to serialize
        fp (Union[TextIO, str]): the file path or file object
        use_decimal (bool, optional): use decimal.Decimal for numbers. Defaults to True.
        **json_kwargs: other arguments passed to json.dump

    Returns:
        None
    """
    if not isinstance(o, Serializer.types):
        raise TypeError(
            f"Object of type {type(o)} is not JSON serializable. "
            f"Only {Serializer.types} are supported."
        )
    if isinstance(fp, str):
        with open(fp, "w") as f:
            json.dump(o, f, cls=Serializer, use_decimal=use_decimal, **json_kwargs)
    else:
        json.dump(o, fp, cls=Serializer, use_decimal=use_decimal, **json_kwargs)

submission

ir

braket

Helper functions related to IR submission co-ordinations between Bloqade and Braket

BraketTaskSpecification

Bases: BaseModel

Class representing geometry of an atom arrangement.

Attributes:

Name Type Description
nshots int

Number of shots

program Program

IR(Intermediate Representation) program suitable for braket

extract_braket_program

extract_braket_program(
    quera_task_ir: QuEraTaskSpecification,
)

Extracts the Braket program.

Parameters:

Name Type Description Default
quera_task_ir QuEraTaskSpecification

Quera IR(Intermediate representation) of the task.

required
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/submission/ir/braket.py
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
def extract_braket_program(quera_task_ir: QuEraTaskSpecification):
    """Extracts the Braket program.

    Args:
        quera_task_ir (QuEraTaskSpecification):
            Quera IR(Intermediate representation) of the task.
    """
    lattice = quera_task_ir.lattice

    rabi_amplitude = (
        quera_task_ir.effective_hamiltonian.rydberg.rabi_frequency_amplitude.global_
    )
    rabi_phase = (
        quera_task_ir.effective_hamiltonian.rydberg.rabi_frequency_phase.global_
    )
    global_detuning = quera_task_ir.effective_hamiltonian.rydberg.detuning.global_
    local_detuning = quera_task_ir.effective_hamiltonian.rydberg.detuning.local

    register = AtomArrangement()
    for site, filled in zip(lattice.sites, lattice.filling):
        site_type = SiteType.FILLED if filled == 1 else SiteType.VACANT
        register.add(site, site_type)

    hamiltonian = DrivingField(
        amplitude=to_braket_field(rabi_amplitude),
        phase=to_braket_field(rabi_phase),
        detuning=to_braket_field(global_detuning),
    )

    if local_detuning:
        hamiltonian = hamiltonian + ShiftingField(to_braket_field(local_detuning))

    return AnalogHamiltonianSimulation(
        register=register,
        hamiltonian=hamiltonian,
    )

from_braket_status_codes

from_braket_status_codes(
    braket_status: str,
) -> QuEraTaskStatusCode

Gets the QuEraTaskStatusCode object for working with Bloqade SDK.

Parameters:

Name Type Description Default
braket_status str

str The value of status in metadata() in the Amazon Braket. GetQuantumTask operation. If use_cached_value is True, the value most recently returned from GetQuantumTask operation is used

required

Returns:

Type Description
QuEraTaskStatusCode

An object of the type Field in Braket SDK

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/submission/ir/braket.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
def from_braket_status_codes(braket_status: str) -> QuEraTaskStatusCode:
    """Gets the `QuEraTaskStatusCode` object for working with Bloqade SDK.

    Args:
        braket_status: str
            The value of status in metadata() in the Amazon Braket.
            `GetQuantumTask` operation. If `use_cached_value` is `True`,
            the value most recently returned from
            `GetQuantumTask` operation is used

    Returns:
        An object of the type `Field` in Braket SDK
    """
    if braket_status == str("QUEUED"):
        return QuEraTaskStatusCode.Enqueued
    else:
        return QuEraTaskStatusCode(braket_status.lower().capitalize())

from_braket_task_results

from_braket_task_results(
    braket_task_results: AnalogHamiltonianSimulationTaskResult,
) -> QuEraTaskResults

Get the QuEraTaskResults object for working with Bloqade SDK.

Parameters:

Name Type Description Default
braket_task_results AnalogHamiltonianSimulationTaskResult

AnalogHamiltonianSimulationTaskResult Quantum task result of braket system

required

Returns:

Type Description
QuEraTaskResults

An object of the type Field in Braket SDK.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/submission/ir/braket.py
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
def from_braket_task_results(
    braket_task_results: AnalogHamiltonianSimulationTaskResult,
) -> QuEraTaskResults:
    """Get the `QuEraTaskResults` object for working with Bloqade SDK.

    Args:
        braket_task_results: AnalogHamiltonianSimulationTaskResult
            Quantum task result of braket system

    Returns:
        An object of the type `Field` in Braket SDK.
    """
    shot_outputs = []
    for measurement in braket_task_results.measurements:
        shot_outputs.append(
            QuEraShotResult(
                shot_status=QuEraShotStatusCode.Completed,
                pre_sequence=list(measurement.pre_sequence),
                post_sequence=list(measurement.post_sequence),
            )
        )

    return QuEraTaskResults(
        task_status=QuEraTaskStatusCode.Completed, shot_outputs=shot_outputs
    )

to_braket_field

to_braket_field(
    quera_field: Union[GlobalField, LocalField],
) -> Field

Converts to TimeSeries object supported by Braket.

Parameters:

Name Type Description Default
quera_field Union[GlobalField, LocalField)]

Field supported by Quera

required

Returns:

Type Description
Field

An object of the type braket.ahs.field.Field

Raises:

Type Description
TypeError

If field is not of the type GlobalField or LocalField.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/submission/ir/braket.py
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
def to_braket_field(quera_field: Union[GlobalField, LocalField]) -> Field:
    """Converts to `TimeSeries` object supported by Braket.

    Args:
        quera_field (Union[GlobalField, LocalField)]:
            Field supported by Quera

    Returns:
        An object of the type `braket.ahs.field.Field`

    Raises:
        TypeError: If field is not of the type `GlobalField` or `LocalField`.
    """
    if isinstance(quera_field, GlobalField):
        times = quera_field.times
        values = quera_field.values
        time_series = to_braket_time_series(times, values)
        return Field(pattern="uniform", time_series=time_series)
    elif isinstance(quera_field, LocalField):
        times = quera_field.times
        values = quera_field.values
        pattern = quera_field.lattice_site_coefficients
        time_series = to_braket_time_series(times, values)
        pattern = Pattern(pattern)
        return Field(pattern=pattern, time_series=time_series)
    else:
        raise TypeError

to_braket_task

to_braket_task(
    quera_task_ir: QuEraTaskSpecification,
) -> Tuple[int, AnalogHamiltonianSimulation]

Converts to Tuple[int, AnalogHamiltonianSimulation] object supported by Braket.

Parameters:

Name Type Description Default
quera_task_ir QuEraTaskSpecification

Quera IR(Intermediate representation) of the task.

required

Returns:

Type Description
Tuple[int, AnalogHamiltonianSimulation]

An tuple of the type Tuple[int, AnalogHamiltonianSimulation].

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/submission/ir/braket.py
129
130
131
132
133
134
135
136
137
138
139
140
141
142
def to_braket_task(
    quera_task_ir: QuEraTaskSpecification,
) -> Tuple[int, AnalogHamiltonianSimulation]:
    """Converts to `Tuple[int, AnalogHamiltonianSimulation]` object supported by Braket.

    Args:
        quera_task_ir (QuEraTaskSpecification):
            Quera IR(Intermediate representation) of the task.

    Returns:
        An tuple  of the type `Tuple[int, AnalogHamiltonianSimulation]`.
    """
    braket_ahs_program = extract_braket_program(quera_task_ir)
    return quera_task_ir.nshots, braket_ahs_program

to_braket_task_ir

to_braket_task_ir(
    quera_task_ir: QuEraTaskSpecification,
) -> BraketTaskSpecification

Converts quera IR(Intermendiate Representation) to to BraketTaskSpecification object.

Parameters:

Name Type Description Default
quera_task_ir QuEraTaskSpecification

Quera IR(Intermediate representation) of the task.

required

Returns:

Type Description
BraketTaskSpecification

An object of the type BraketTaskSpecification in Braket SDK

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/submission/ir/braket.py
145
146
147
148
149
150
151
152
153
154
155
156
157
158
def to_braket_task_ir(quera_task_ir: QuEraTaskSpecification) -> BraketTaskSpecification:
    """Converts quera IR(Intermendiate Representation) to
    to `BraketTaskSpecification` object.

    Args:
        quera_task_ir (QuEraTaskSpecification):
            Quera IR(Intermediate representation) of the task.

    Returns:
        An object of the type `BraketTaskSpecification` in Braket SDK

    """
    nshots, braket_ahs_program = to_braket_task(quera_task_ir)
    return BraketTaskSpecification(nshots=nshots, program=braket_ahs_program.to_ir())

to_braket_time_series

to_braket_time_series(
    times: List[Decimal], values: List[Decimal]
) -> TimeSeries

Converts to TimeSeries object supported by Braket.

Parameters:

Name Type Description Default
times List[Decimal]

Times of the value.

required
values List[Decimal]

Corresponding values to add to the time series

required

Returns:

Type Description
TimeSeries

An object of the type braket.timings.TimeSeries

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/submission/ir/braket.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
def to_braket_time_series(times: List[Decimal], values: List[Decimal]) -> TimeSeries:
    """Converts to `TimeSeries` object supported by Braket.

    Args:
        times (List[Decimal]): Times of the value.
        values (List[Decimal]): Corresponding values to add to the time series

    Returns:
        An object of the type `braket.timings.TimeSeries`
    """
    time_series = TimeSeries()
    for time, value in zip(times, values):
        time_series.put(time, value)

    return time_series

to_quera_capabilities

to_quera_capabilities(paradigm) -> cp.QuEraCapabilities

Converts to QuEraCapabilities object supported by Braket.

Parameters:

Name Type Description Default
paradigm

The paradigm property of the AwsDevice object for Aquila

required

Returns:

Type Description
QuEraCapabilities

An object of the type QuEraCapabilities in Bloqade SDK.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/submission/ir/braket.py
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
def to_quera_capabilities(paradigm) -> cp.QuEraCapabilities:
    """Converts to `QuEraCapabilities` object supported by Braket.

    Args:
        paradigm: The `paradigm` property of the `AwsDevice` object for Aquila

    Returns:
        An object of the type `QuEraCapabilities` in Bloqade SDK.
    """
    rydberg_global = paradigm.rydberg.rydbergGlobal

    return cp.QuEraCapabilities(
        version=paradigm.braketSchemaHeader.version,
        capabilities=cp.DeviceCapabilities(
            task=cp.TaskCapabilities(
                number_shots_min=1,
                number_shots_max=1000,
            ),
            lattice=cp.LatticeCapabilities(
                number_qubits_max=paradigm.qubitCount,
                geometry=cp.LatticeGeometryCapabilities(
                    spacing_radial_min=paradigm.lattice.geometry.spacingRadialMin,
                    spacing_vertical_min=paradigm.lattice.geometry.spacingVerticalMin,
                    position_resolution=paradigm.lattice.geometry.positionResolution,
                    number_sites_max=paradigm.lattice.geometry.numberSitesMax,
                ),
                area=cp.LatticeAreaCapabilities(
                    width=paradigm.lattice.area.width,
                    height=paradigm.lattice.area.height,
                ),
            ),
            rydberg=cp.RydbergCapabilities(
                c6_coefficient=paradigm.rydberg.c6Coefficient,
                global_=cp.RydbergGlobalCapabilities(
                    rabi_frequency_max=rydberg_global.rabiFrequencyRange[0],
                    rabi_frequency_min=rydberg_global.rabiFrequencyRange[1],
                    rabi_frequency_resolution=rydberg_global.rabiFrequencyResolution,
                    rabi_frequency_slew_rate_max=rydberg_global.rabiFrequencySlewRateMax,
                    detuning_max=rydberg_global.detuningRange[0],
                    detuning_min=rydberg_global.detuningRange[1],
                    detuning_resolution=rydberg_global.detuningResolution,
                    detuning_slew_rate_max=rydberg_global.detuningSlewRateMax,
                    phase_min=rydberg_global.phaseRange[0],
                    phase_max=rydberg_global.phaseRange[1],
                    phase_resolution=rydberg_global.phaseResolution,
                    time_min=rydberg_global.timeMin,
                    time_max=rydberg_global.timeMax,
                    time_resolution=rydberg_global.timeResolution,
                    time_delta_min=rydberg_global.timeDeltaMin,
                ),
                local=None,
            ),
        ),
    )

parallel

ClusterLocationInfo

Bases: BaseModel

Class that stores the mapping of batched jobs.

Parameters:

Name Type Description Default
cluster_index int

the index of the cluster a site belongs to

required
global_location_index int

the index of the site in the multplexed system

required
cluster_location_index int

the index of the site in the original system

required

task_results

QuEraTaskResults

Bases: BaseModel

export_as_probabilities
export_as_probabilities() -> TaskProbabilities

converts from shot results to probabilities

Returns:

Name Type Description
TaskProbabilities TaskProbabilities

The task results as probabilties

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/submission/ir/task_results.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def export_as_probabilities(self) -> TaskProbabilities:
    """converts from shot results to probabilities

    Returns:
        TaskProbabilities: The task results as probabilties
    """
    counts = dict()
    nshots = len(self.shot_outputs)
    for shot_result in self.shot_outputs:
        pre_sequence_str = "".join(str(bit) for bit in shot_result.pre_sequence)

        post_sequence_str = "".join(str(bit) for bit in shot_result.post_sequence)

        configuration = (pre_sequence_str, post_sequence_str)
        # iterative average
        current_count = counts.get(configuration, 0)
        counts[configuration] = current_count + 1

    probabilities = [(config, count / nshots) for config, count in counts.items()]
    return TaskProbabilities(probabilities=probabilities)

QuEraTaskStatusCode

Bases: str, Enum

An Enum representing the various states a task can be in within the QuEra system.

Attributes:

Name Type Description
Created

The task has been created but not yet started.

Running

The task is currently running.

Completed

The task has completed successfully.

Failed

The task has failed.

Cancelled

The task has been cancelled.

Executing

The task is currently being executed.

Enqueued

The task is in the queue waiting to be executed.

Accepted

The task has been accepted for execution.

Unaccepted

The task has not been accepted for execution.

Partial

The task has partially completed.

Unsubmitted

The task has not been submitted for execution.

task

base

Geometry

Class representing geometry of an atom arrangement.

Attributes:

Name Type Description
sites List[Tuple[float, float]]

Atom site arrangement

filling List[int]

Which sites are filled

parallel_decoder Optional[ParallelDecoder]

Decoder object for decoding Geometry object

LocalTask

Bases: Task

Task to use for local executions for simulation purposes..

RemoteTask

Bases: Task

Task to use for remote executions to run the program on Quera Quantum Computers.

Report

Report(data, metas, geos, name='')

Report is a helper class for organizing and analysing data

Analyzing Results

When you've retrieved your results from either emulation or hardware you can generate a .report():

report = results.report()

For the examples below we analyze the results of a two atom program.

The report contains useful information such as:

The raw bitstrings measured per each execution of the program

>>> report.bitstrings()
[array([[1, 1],
        [1, 1],
        [1, 1],
        ...,
        [1, 1],
        [1, 1],

The number of times each unique bitstring occurred:

>>> report.counts()

[OrderedDict([('11', 892), ('10', 59), ('01', 49)])]

The Rydberg Density for each atom

>>> report.rydberg_densities()

                0      1
task_number
0            0.053  0.054

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/base.py
234
235
236
237
238
239
240
def __init__(self, data, metas, geos, name="") -> None:
    self.dataframe = data  # df
    self._bitstrings = None  # bitstring cache
    self._counts = None  # counts cache
    self.metas = metas
    self.geos = geos
    self.name = name + " " + str(datetime.datetime.now())

markdown property

markdown: str

Get the markdown representation of the dataframe

bitstrings

bitstrings(
    filter_perfect_filling: bool = True,
    clusters: Union[
        tuple[int, int], List[tuple[int, int]]
    ] = [],
) -> List[NDArray]

Get the bitstrings from the data.

Parameters:

Name Type Description Default
filter_perfect_filling bool

whether return will only contain perfect filling shots. Defaults to True.

True
clusters Union[tuple[int, int], List[tuple[int, int]]]

(tuple[int, int], Sequence[Tuple[int, int]]): cluster index to filter shots from. If none are provided all clusters are used, defaults to [].

[]

Returns:

Name Type Description
bitstrings list of ndarray

list corresponding to each task in the report. Each element is an ndarray of shape (nshots, nsites) where nshots is the number of shots for the task and nsites is the number of sites in the task. For example:

[array([[1, 1],
        [1, 1],
        [1, 1],
        ...,
        [1, 1],
        [1, 1],
        [1, 0]], dtype=int8)]

Note

Note that nshots may vary between tasks if filter_perfect_filling is set to True.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/base.py
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
@beartype
def bitstrings(
    self,
    filter_perfect_filling: bool = True,
    clusters: Union[tuple[int, int], List[tuple[int, int]]] = [],
) -> List[NDArray]:
    """Get the bitstrings from the data.

    Args:
        filter_perfect_filling (bool): whether return will
            only contain perfect filling shots. Defaults to True.
        clusters: (tuple[int, int], Sequence[Tuple[int, int]]):
            cluster index to filter shots from. If none are provided
            all clusters are used, defaults to [].

    Returns:
        bitstrings (list of ndarray): list corresponding to each
            task in the report. Each element is an ndarray of shape
            (nshots, nsites) where nshots is the number of shots for
            the task and nsites is the number of sites in the task.
            For example:
            ```python3
            [array([[1, 1],
                    [1, 1],
                    [1, 1],
                    ...,
                    [1, 1],
                    [1, 1],
                    [1, 0]], dtype=int8)]
            ```

    Note:
        Note that nshots may vary between tasks if filter_perfect_filling
        is set to True.

    """

    task_numbers = self.dataframe.index.get_level_values("task_number").unique()

    bitstrings = []
    for task_number in task_numbers:
        mask = self._filter(
            task_number=task_number,
            filter_perfect_filling=filter_perfect_filling,
            clusters=clusters,
        )
        if np.any(mask):
            bitstrings.append(self.dataframe.loc[mask].to_numpy())
        else:
            bitstrings.append(
                np.zeros((0, self.dataframe.shape[1]), dtype=np.uint8)
            )

    return bitstrings

counts

counts(
    filter_perfect_filling: bool = True,
    clusters: Union[
        tuple[int, int], List[tuple[int, int]]
    ] = [],
) -> List[OrderedDict[str, int]]

Get the counts of unique bit strings.

Parameters:

Name Type Description Default
filter_perfect_filling bool

whether return will only contain perfect filling shots. Defaults to True.

True
clusters Union[tuple[int, int], List[tuple[int, int]]]

(tuple[int, int], Sequence[Tuple[int, int]]): cluster index to filter shots from. If none are provided all clusters are used, defaults to [].

[]

Returns:

Name Type Description
counts list of OrderedDict[str, int]

list corresponding to each task in the report. Each element is an ndarray of shape (nshots, nsites) where nshots is the number of shots for the task and nsites is the number of sites in the task. For example:

    [OrderedDict([('11', 892), ('10', 59), ('01', 49)])]

Note

Note that nshots may vary between tasks if filter_perfect_filling is set to True.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/base.py
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
def counts(
    self,
    filter_perfect_filling: bool = True,
    clusters: Union[tuple[int, int], List[tuple[int, int]]] = [],
) -> List[OrderedDict[str, int]]:
    """Get the counts of unique bit strings.

    Args:
        filter_perfect_filling (bool): whether return will
            only contain perfect filling shots. Defaults to True.
        clusters: (tuple[int, int], Sequence[Tuple[int, int]]):
            cluster index to filter shots from. If none are provided
            all clusters are used, defaults to [].

    Returns:
        counts (list of OrderedDict[str, int]): list corresponding to each
            task in the report. Each element is an ndarray of shape
            (nshots, nsites) where nshots is the number of shots for
            the task and nsites is the number of sites in the task.
            For example:
            ```python
                [OrderedDict([('11', 892), ('10', 59), ('01', 49)])]
            ```

    Note:
        Note that nshots may vary between tasks if filter_perfect_filling
        is set to True.

    """

    def _generate_counts(bitstring):
        output = np.unique(bitstring, axis=0, return_counts=True)

        count_list = [
            ("".join(map(str, bitstring)), int(count))
            for bitstring, count in zip(*output)
        ]
        count_list.sort(key=lambda x: x[1], reverse=True)
        count = OrderedDict(count_list)

        return count

    return list(
        map(_generate_counts, self.bitstrings(filter_perfect_filling, clusters))
    )

list_param

list_param(field_name: str) -> List[Union[Number, None]]

List the parameters associate with the given variable field_name for each tasks.

Parameters:

Name Type Description Default
field_name str

variable name

required
Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/base.py
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
def list_param(self, field_name: str) -> List[Union[Number, None]]:
    """
    List the parameters associate with the given variable field_name
    for each tasks.

    Args:
        field_name (str): variable name

    """

    def cast(x):
        if x is None:
            return None
        elif isinstance(x, (list, tuple, np.ndarray)):
            return list(map(cast, x))
        else:
            return float(x)

    return list(map(cast, (meta.get(field_name) for meta in self.metas)))

rydberg_densities

rydberg_densities(
    filter_perfect_filling: bool = True,
    clusters: Union[
        tuple[int, int], List[tuple[int, int]]
    ] = [],
) -> Union[pd.Series, pd.DataFrame]

Get rydberg density for each task.

Parameters:

Name Type Description Default
filter_perfect_filling bool

whether return will only contain perfect filling shots. Defaults to True.

True
clusters Union[tuple[int, int], List[tuple[int, int]]]

(tuple[int, int], Sequence[Tuple[int, int]]): cluster index to filter shots from. If none are provided all clusters are used, defaults to [].

[]

Returns:

Name Type Description
rydberg_densities Union[Series, DataFrame]

per-site rydberg density for each task as a pandas DataFrame or Series. For example:

0      1
task_number
0            0.053  0.054

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/base.py
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
@beartype
def rydberg_densities(
    self,
    filter_perfect_filling: bool = True,
    clusters: Union[tuple[int, int], List[tuple[int, int]]] = [],
) -> Union[pd.Series, pd.DataFrame]:
    """Get rydberg density for each task.

    Args:
        filter_perfect_filling (bool, optional): whether return will
            only contain perfect filling shots. Defaults to True.
        clusters: (tuple[int, int], Sequence[Tuple[int, int]]):
            cluster index to filter shots from. If none are provided
            all clusters are used, defaults to [].

    Returns:
        rydberg_densities (Union[pd.Series, pd.DataFrame]):
            per-site rydberg density for each task as a pandas DataFrame or Series.
            For example:
            ```python
            0      1
            task_number
            0            0.053  0.054
            ```
    """
    mask = self._filter(
        filter_perfect_filling=filter_perfect_filling, clusters=clusters
    )
    df = self.dataframe[mask]
    return 1 - (df.groupby("task_number").mean())

show

show()

Interactive Visualization of the Report

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/base.py
426
427
428
429
430
431
def show(self):
    """
    Interactive Visualization of the Report

    """
    display_report(self)

batch

Filter

filter_metadata

filter_metadata(
    __match_any__: bool = False,
    **metadata: MetadataFilterType
) -> Union[LocalBatch, RemoteBatch]

Create a Batch object that has tasks filtered based on the values of metadata.

Parameters:

Name Type Description Default
__match_any__ bool

if True, then a task will be included if it matches any of the metadata filters. If False, then a task will be included only if it matches all of the metadata filters. Defaults to False.

False
**metadata MetadataFilterType

the metadata to filter on. The keys are the metadata names and the values (as a set) are the values to filter on. The elements in the set can be Real, Decimal, Tuple[Real], or Tuple[Decimal].

{}
Return

type(self): a Batch object with the filtered tasks, either LocalBatch or RemoteBatch depending on the type of self

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
@beartype
def filter_metadata(
    self, __match_any__: bool = False, **metadata: MetadataFilterType
) -> Union["LocalBatch", "RemoteBatch"]:
    """Create a Batch object that has tasks filtered based on the
    values of metadata.

    Args:
        __match_any__: if True, then a task will be included if it
            matches any of the metadata filters. If False, then a
            task will be included only if it matches all of the
            metadata filters. Defaults to False.

        **metadata: the metadata to filter on. The keys are the metadata
            names and the values (as a set) are the values to filter on.
            The elements in the set can be Real, Decimal, Tuple[Real], or
            Tuple[Decimal].

    Return:
        type(self): a Batch object with the filtered tasks, either
            LocalBatch or RemoteBatch depending on the type of self

    """

    def convert_to_decimal(element):
        if isinstance(element, list):
            return list(map(convert_to_decimal, element))
        elif isinstance(element, (Real, Decimal)):
            return Decimal(str(element))
        else:
            raise ValueError(
                f"Invalid value {element} for metadata filter. "
                "Only Real, Decimal, List[Real], and List[Decimal] "
                "are supported."
            )

    def metadata_match_all(task):
        return all(
            task.metadata.get(key) in value for key, value in metadata.items()
        )

    def metadata_match_any(task):
        return any(
            task.metadata.get(key) in value for key, value in metadata.items()
        )

    metadata = {k: list(map(convert_to_decimal, v)) for k, v in metadata.items()}

    metadata_filter = metadata_match_any if __match_any__ else metadata_match_all

    new_tasks = OrderedDict(
        [(k, v) for k, v in self.tasks.items() if metadata_filter(v)]
    )

    kw = dict(self.__dict__)
    kw["tasks"] = new_tasks

    return self.__class__(**kw)

LocalBatch dataclass

LocalBatch(
    source: Optional[Builder],
    tasks: OrderedDict[
        int, Union[BraketEmulatorTask, BloqadeTask]
    ],
    name: Optional[str] = None,
)

Bases: Serializable, Filter

report

report() -> Report

Generate analysis report base on currently completed tasks in the LocalBatch.

Return

Report

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
def report(self) -> Report:
    """
    Generate analysis report base on currently
    completed tasks in the LocalBatch.

    Return:
        Report

    """

    ## this potentially can be specialize/disatch
    ## offline
    index = []
    data = []
    metas = []
    geos = []

    for task_number, task in self.tasks.items():
        geometry = task.geometry
        perfect_sorting = "".join(map(str, geometry.filling))
        parallel_decoder = geometry.parallel_decoder

        if parallel_decoder:
            cluster_indices = parallel_decoder.get_cluster_indices()
        else:
            cluster_indices = {(0, 0): list(range(len(perfect_sorting)))}

        shot_iter = filter(
            lambda shot: shot.shot_status == QuEraShotStatusCode.Completed,
            task.result().shot_outputs,
        )

        for shot, (cluster_coordinate, cluster_index) in product(
            shot_iter, cluster_indices.items()
        ):
            pre_sequence = "".join(
                map(
                    str,
                    (shot.pre_sequence[index] for index in cluster_index),
                )
            )

            post_sequence = np.asarray(
                [shot.post_sequence[index] for index in cluster_index],
                dtype=np.int8,
            )

            pfc_sorting = "".join(
                [perfect_sorting[index] for index in cluster_index]
            )

            key = (
                task_number,
                cluster_coordinate,
                pfc_sorting,
                pre_sequence,
            )

            index.append(key)
            data.append(post_sequence)

        metas.append(task.metadata)
        geos.append(task.geometry)

    index = pd.MultiIndex.from_tuples(
        index, names=["task_number", "cluster", "perfect_sorting", "pre_sequence"]
    )

    df = pd.DataFrame(data, index=index)
    df.sort_index(axis="index")

    rept = None
    if self.name is None:
        rept = Report(df, metas, geos, "Local")
    else:
        rept = Report(df, metas, geos, self.name)

    return rept

rerun

rerun(
    multiprocessing: bool = False,
    num_workers: Optional[int] = None,
    **kwargs
)

Rerun all the tasks in the LocalBatch.

Return

Report

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
@beartype
def rerun(
    self, multiprocessing: bool = False, num_workers: Optional[int] = None, **kwargs
):
    """
    Rerun all the tasks in the LocalBatch.

    Return:
        Report

    """

    return self._run(
        multiprocessing=multiprocessing, num_workers=num_workers, **kwargs
    )

RemoteBatch dataclass

RemoteBatch(
    source: Builder,
    tasks: Union[
        OrderedDict[int, QuEraTask],
        OrderedDict[int, BraketTask],
        OrderedDict[int, CustomRemoteTaskABC],
    ],
    name: Optional[str] = None,
)

Bases: Serializable, Filter

total_nshots property

total_nshots

Total number of shots of all tasks in the RemoteBatch

Return

number of shots

cancel

cancel() -> RemoteBatch

Cancel all the tasks in the Batch.

Return

self

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
360
361
362
363
364
365
366
367
368
369
370
371
372
def cancel(self) -> "RemoteBatch":
    """
    Cancel all the tasks in the Batch.

    Return:
        self

    """
    # cancel all jobs
    for task in self.tasks.values():
        task.cancel()

    return self

fetch

fetch() -> RemoteBatch

Fetch the tasks in the Batch.

Note

Fetching will update the status of tasks, and only pull the results for those tasks that have completed.

Return

self

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
def fetch(self) -> "RemoteBatch":
    """
    Fetch the tasks in the Batch.

    Note:
        Fetching will update the status of tasks,
        and only pull the results for those tasks
        that have completed.

    Return:
        self

    """
    # online, non-blocking
    # pull the results only when its ready
    for task in self.tasks.values():
        task.fetch()

    return self

get_completed_tasks

get_completed_tasks() -> RemoteBatch

Create a RemoteBatch object that contain completed tasks from current Batch.

Tasks consider completed with following status codes:

  1. Completed
  2. Partial
Return

RemoteBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
def get_completed_tasks(self) -> "RemoteBatch":
    """
    Create a RemoteBatch object that
    contain completed tasks from current Batch.

    Tasks consider completed with following status codes:

    1. Completed
    2. Partial

    Return:
        RemoteBatch

    """
    statuses = [
        "Completed",
        "Partial",
    ]
    return self.get_tasks(*statuses)

get_failed_tasks

get_failed_tasks() -> RemoteBatch

Create a RemoteBatch object that contain failed tasks from current Batch.

failed tasks with following status codes:

  1. Failed
  2. Unaccepted
Return

RemoteBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
def get_failed_tasks(self) -> "RemoteBatch":
    """
    Create a RemoteBatch object that
    contain failed tasks from current Batch.

    failed tasks with following status codes:

    1. Failed
    2. Unaccepted

    Return:
        RemoteBatch

    """
    # statuses that are in a state that are
    # completed because of an error
    statuses = ["Failed", "Unaccepted"]
    return self.get_tasks(*statuses)

get_finished_tasks

get_finished_tasks() -> RemoteBatch

Create a RemoteBatch object that contain finished tasks from current Batch.

Tasks consider finished with following status codes:

  1. Failed
  2. Unaccepted
  3. Completed
  4. Partial
  5. Cancelled
Return

RemoteBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
def get_finished_tasks(self) -> "RemoteBatch":
    """
    Create a RemoteBatch object that
    contain finished tasks from current Batch.

    Tasks consider finished with following status codes:

    1. Failed
    2. Unaccepted
    3. Completed
    4. Partial
    5. Cancelled

    Return:
        RemoteBatch

    """
    # statuses that are in a state that will
    # not run going forward for any reason
    statuses = ["Completed", "Failed", "Unaccepted", "Partial", "Cancelled"]
    return self.get_tasks(*statuses)

get_tasks

get_tasks(*status_codes: str) -> RemoteBatch

Get Tasks with specify status_codes.

Return

RemoteBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
@beartype
def get_tasks(self, *status_codes: str) -> "RemoteBatch":
    """
    Get Tasks with specify status_codes.

    Return:
        RemoteBatch

    """
    # offline:
    st_codes = [QuEraTaskStatusCode(x) for x in status_codes]

    new_task_results = OrderedDict()
    for task_number, task in self.tasks.items():
        if task.task_result_ir.task_status in st_codes:
            new_task_results[task_number] = task

    return RemoteBatch(self.source, new_task_results, name=self.name)

pull

pull() -> RemoteBatch

Pull results of the tasks in the Batch.

Note

Pulling will pull the results for the tasks. If a given task(s) has not been completed, wait until it finished.

Return

self

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
def pull(self) -> "RemoteBatch":
    """
    Pull results of the tasks in the Batch.

    Note:
        Pulling will pull the results for the tasks.
        If a given task(s) has not been completed, wait
        until it finished.

    Return:
        self
    """
    # online, blocking
    # pull the results. if its not ready, hanging
    for task in self.tasks.values():
        task.pull()

    return self

remove_failed_tasks

remove_failed_tasks() -> RemoteBatch

Create a RemoteBatch object that contain tasks from current Batch, with failed tasks removed.

failed tasks with following status codes:

  1. Failed
  2. Unaccepted
Return

RemoteBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
def remove_failed_tasks(self) -> "RemoteBatch":
    """
    Create a RemoteBatch object that
    contain tasks from current Batch,
    with failed tasks removed.

    failed tasks with following status codes:

    1. Failed
    2. Unaccepted

    Return:
        RemoteBatch

    """
    # statuses that are in a state that will
    # not run going forward because of an error
    statuses = ["Failed", "Unaccepted"]
    return self.remove_tasks(*statuses)

remove_invalid_tasks

remove_invalid_tasks() -> RemoteBatch

Create a RemoteBatch object that contain tasks from current Batch, with all Unaccepted tasks removed.

Return

RemoteBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
461
462
463
464
465
466
467
468
469
470
471
def remove_invalid_tasks(self) -> "RemoteBatch":
    """
    Create a RemoteBatch object that
    contain tasks from current Batch,
    with all Unaccepted tasks removed.

    Return:
        RemoteBatch

    """
    return self.remove_tasks("Unaccepted")

remove_tasks

remove_tasks(
    *status_codes: Literal[
        "Created",
        "Running",
        "Completed",
        "Failed",
        "Cancelled",
        "Executing",
        "Enqueued",
        "Accepted",
        "Unaccepted",
        "Partial",
        "Unsubmitted",
    ]
) -> RemoteBatch

Remove Tasks with specify status_codes.

Return

RemoteBatch

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
@beartype
def remove_tasks(
    self,
    *status_codes: Literal[
        "Created",
        "Running",
        "Completed",
        "Failed",
        "Cancelled",
        "Executing",
        "Enqueued",
        "Accepted",
        "Unaccepted",
        "Partial",
        "Unsubmitted",
    ],
) -> "RemoteBatch":
    """
    Remove Tasks with specify status_codes.

    Return:
        RemoteBatch

    """
    # offline:

    st_codes = [QuEraTaskStatusCode(x) for x in status_codes]

    new_results = OrderedDict()
    for task_number, task in self.tasks.items():
        if task.task_result_ir.task_status in st_codes:
            continue

        new_results[task_number] = task

    return RemoteBatch(self.source, new_results, self.name)

report

report() -> Report

Generate analysis report base on currently completed tasks in the RemoteBatch.

Return

Report

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
def report(self) -> "Report":
    """
    Generate analysis report base on currently
    completed tasks in the RemoteBatch.

    Return:
        Report

    """
    ## this potentially can be specialize/disatch
    ## offline
    index = []
    data = []
    metas = []
    geos = []

    for task_number, task in self.tasks.items():
        ## fliter not existing results tasks:
        if (task.task_id is None) or (not task._result_exists()):
            continue

        ## filter has result but is not correctly completed.
        if task.task_result_ir.task_status not in [
            QuEraTaskStatusCode.Completed,
            QuEraTaskStatusCode.Partial,
        ]:
            continue

        geometry = task.geometry
        perfect_sorting = "".join(map(str, geometry.filling))
        parallel_decoder = geometry.parallel_decoder

        if parallel_decoder:
            cluster_indices = parallel_decoder.get_cluster_indices()
        else:
            cluster_indices = {(0, 0): list(range(len(perfect_sorting)))}

        shot_iter = filter(
            lambda shot: shot.shot_status == QuEraShotStatusCode.Completed,
            task.result().shot_outputs,
        )

        for shot, (cluster_coordinate, cluster_index) in product(
            shot_iter, cluster_indices.items()
        ):
            pre_sequence = "".join(
                map(
                    str,
                    (shot.pre_sequence[index] for index in cluster_index),
                )
            )

            post_sequence = np.asarray(
                [shot.post_sequence[index] for index in cluster_index],
                dtype=np.int8,
            )

            pfc_sorting = "".join(
                [perfect_sorting[index] for index in cluster_index]
            )

            key = (
                task_number,
                cluster_coordinate,
                pfc_sorting,
                pre_sequence,
            )

            index.append(key)
            data.append(post_sequence)

        metas.append(task.metadata)
        geos.append(task.geometry)

    index = pd.MultiIndex.from_tuples(
        index, names=["task_number", "cluster", "perfect_sorting", "pre_sequence"]
    )

    df = pd.DataFrame(data, index=index)
    df.sort_index(axis="index")

    rept = None
    if self.name is None:
        rept = Report(df, metas, geos, "Remote")
    else:
        rept = Report(df, metas, geos, self.name)

    return rept

resubmit

resubmit(shuffle_submit_order: bool = True) -> RemoteBatch

Resubmit all the tasks in the RemoteBatch

Return

self

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
475
476
477
478
479
480
481
482
483
484
485
486
@beartype
def resubmit(self, shuffle_submit_order: bool = True) -> "RemoteBatch":
    """
    Resubmit all the tasks in the RemoteBatch

    Return:
        self

    """
    # online, non-blocking
    self._submit(shuffle_submit_order, force=True)
    return self

retrieve

retrieve() -> RemoteBatch

Retrieve missing task results.

Note

Retrieve will update the status of tasks, and only pull the results for those tasks that have completed.

Return

self

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
def retrieve(self) -> "RemoteBatch":
    """Retrieve missing task results.

    Note:
        Retrieve will update the status of tasks,
        and only pull the results for those tasks
        that have completed.

    Return:
        self

    """
    # partially online, sometimes blocking
    # pull the results for tasks that have
    # not been pulled already.
    for task in self.tasks.values():
        if not task._result_exists():
            task.pull()

    return self

tasks_metric

tasks_metric() -> pd.DataFrame

Get current tasks status metric

Return

dataframe with ["task id", "status", "shots"]

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
def tasks_metric(self) -> pd.DataFrame:
    """
    Get current tasks status metric

    Return:
        dataframe with ["task id", "status", "shots"]

    """
    # [TODO] more info on current status
    # offline, non-blocking
    tid = []
    data = []
    for task_num, task in self.tasks.items():
        tid.append(task_num)

        dat: list[int | str | None] = [None, None, None]
        dat[0] = task.task_id
        if task.task_result_ir is not None:
            dat[1] = task.task_result_ir.task_status.name
        dat[2] = task.task_ir.nshots
        data.append(dat)

    return pd.DataFrame(data, index=tid, columns=["task ID", "status", "shots"])

Serializable

json

json(**options) -> str

Serialize the object to JSON string.

Return

JSON string

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/batch.py
37
38
39
40
41
42
43
44
45
46
47
def json(self, **options) -> str:
    """
    Serialize the object to JSON string.

    Return:
        JSON string

    """
    from bloqade.analog import dumps

    return dumps(self, **options)

exclusive

ExclusiveRemoteTask dataclass

ExclusiveRemoteTask(
    _task_ir: QuEraTaskSpecification | None,
    _metadata: Dict[str, ParamType],
    _parallel_decoder: ParallelDecoder | None,
    _http_handler: HTTPHandlerABC = HTTPHandler(),
    _task_id: str | None = None,
    _task_result_ir: QuEraTaskResults | None = None,
)

Bases: CustomRemoteTaskABC

pull

pull(poll_interval: float = 20)

Blocking pull to get the task result. poll_interval is the time interval to poll the task status. Please ensure that it is relatively large, otherwise the server could get overloaded with queries.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/exclusive.py
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
def pull(self, poll_interval: float = 20):
    """
    Blocking pull to get the task result.
    poll_interval is the time interval to poll the task status.
    Please ensure that it is relatively large, otherwise
    the server could get overloaded with queries.
    """

    while True:
        if self._task_result_ir.task_status is QuEraTaskStatusCode.Unsubmitted:
            raise ValueError("Task ID not found.")

        if self._task_result_ir.task_status in [
            QuEraTaskStatusCode.Completed,
            QuEraTaskStatusCode.Partial,
            QuEraTaskStatusCode.Failed,
            QuEraTaskStatusCode.Unaccepted,
            QuEraTaskStatusCode.Cancelled,
        ]:
            return self

        status = self.status()
        if status in [QuEraTaskStatusCode.Completed, QuEraTaskStatusCode.Partial]:
            self._task_result_ir = self._http_handler.fetch_results(self._task_id)
            return self

        time.sleep(poll_interval)

HTTPHandler

HTTPHandler(
    zapier_webhook_url: str = None,
    zapier_webhook_key: str = None,
    vercel_api_url: str = None,
)

Bases: HTTPHandlerABC

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/exclusive.py
76
77
78
79
80
81
82
83
84
def __init__(
    self,
    zapier_webhook_url: str = None,
    zapier_webhook_key: str = None,
    vercel_api_url: str = None,
):
    self.zapier_webhook_url = zapier_webhook_url or os.environ["ZAPIER_WEBHOOK_URL"]
    self.zapier_webhook_key = zapier_webhook_key or os.environ["ZAPIER_WEBHOOK_KEY"]
    self.verrcel_api_url = vercel_api_url or os.environ["VERCEL_API_URL"]

fetch_results

fetch_results(task_id: str)

Fetch the task results from the AirTable.

Parameters:

Name Type Description Default
task_id str

The task id to be queried.

required

returns response: The response from the AirTable. used for error handling

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/exclusive.py
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
def fetch_results(self, task_id: str):
    response = request(
        "GET",
        self.verrcel_api_url,
        params={
            "searchPattern": task_id,
            "magicToken": self.zapier_webhook_key,
            "useRegex": False,
        },
    )
    if response.status_code != 200:
        print(f"HTTP request failed with status code: {response.status_code}")
        print("HTTP responce: ", response.text)
        return None

    response_data = response.json()
    # Get "matched" from the response
    matches = response_data.get("matches", None)
    # The return is a list of dictionaries
    # Verify if the list contains only one element
    if matches is None:
        print("No task found with the given ID.")
        return None
    elif len(matches) > 1:
        print("Multiple tasks found with the given ID.")
        return None
    record = matches[0]
    if record.get("status") == "Completed":
        googledoc = record.get("resultsFileUrl")

        # convert the preview URL to download URL
        googledoc = convert_preview_to_download(googledoc)
        res = get(googledoc)
        res.raise_for_status()
        data = res.json()

        task_results = QuEraTaskResults(**data)
    return task_results

query_task_status

query_task_status(task_id: str)

Query the task status from the AirTable.

Parameters:

Name Type Description Default
task_id str

The task id to be queried.

required

returns response: The response from the AirTable. used for error handling

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/exclusive.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
def query_task_status(self, task_id: str):
    response = request(
        "GET",
        self.verrcel_api_url,
        params={
            "searchPattern": task_id,
            "magicToken": self.zapier_webhook_key,
            "useRegex": False,
        },
    )
    if response.status_code != 200:
        return "Not Found"
    response_data = response.json()
    # Get "matched" from the response
    matches = response_data.get("matches", None)
    # The return is a list of dictionaries
    # Verify if the list contains only one element
    if matches is None:
        print("No task found with the given ID.")
        return "Failed"
    elif len(matches) > 1:
        print("Multiple tasks found with the given ID.")
        return "Failed"

    # Extract the status from the first dictionary
    status = matches[0].get("status")
    return status

submit_task_via_zapier

submit_task_via_zapier(
    task_ir: QuEraTaskSpecification,
    task_id: str,
    task_note: str,
)

Submit a task and add task_id to the task fields for querying later.

Parameters:

Name Type Description Default
task_ir QuEraTaskSpecification

The task to be submitted.

required
task_id str

The task id to be added to the task fields.

required

returns response: The response from the Zapier webhook. used for error handling

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/exclusive.py
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
def submit_task_via_zapier(
    self, task_ir: QuEraTaskSpecification, task_id: str, task_note: str
):
    # implement http request logic to submit task via Zapier
    request_options = dict(params={"key": self.zapier_webhook_key, "note": task_id})

    # for metadata, task_ir in self._compile_single(shots, use_experimental, args):
    json_request_body = task_ir.json(exclude_none=True, exclude_unset=True)

    request_options.update(data=json_request_body)
    response = request("POST", self.zapier_webhook_url, **request_options)

    if response.status_code == 200:
        response_data = response.json()
        submit_status = response_data.get("status", None)
        return submit_status
    else:
        print(f"HTTP request failed with status code: {response.status_code}")
        print("HTTP responce: ", response.text)
        return "Failed"

HTTPHandlerABC

fetch_results abstractmethod

fetch_results(task_id: str)

Fetch the task results from the AirTable.

Parameters:

Name Type Description Default
task_id str

The task id to be queried.

required

returns response: The response from the AirTable. used for error handling

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/exclusive.py
50
51
52
53
54
55
56
57
58
59
60
61
62
@abc.abstractmethod
def fetch_results(task_id: str):
    """Fetch the task results from the AirTable.

    args:
        task_id: The task id to be queried.

    returns
        response: The response from the AirTable. used for error handling

    """

    ...

query_task_status abstractmethod

query_task_status(task_id: str)

Query the task status from the AirTable.

Parameters:

Name Type Description Default
task_id str

The task id to be queried.

required

returns response: The response from the AirTable. used for error handling

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/exclusive.py
37
38
39
40
41
42
43
44
45
46
47
48
@abc.abstractmethod
def query_task_status(task_id: str):
    """Query the task status from the AirTable.

    args:
        task_id: The task id to be queried.

    returns
        response: The response from the AirTable. used for error handling

    """
    ...

submit_task_via_zapier abstractmethod

submit_task_via_zapier(
    task_ir: QuEraTaskSpecification, task_id: str
)

Submit a task and add task_id to the task fields for querying later.

Parameters:

Name Type Description Default
task_ir QuEraTaskSpecification

The task to be submitted.

required
task_id str

The task id to be added to the task fields.

required

returns response: The response from the Zapier webhook. used for error handling

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/task/exclusive.py
23
24
25
26
27
28
29
30
31
32
33
34
35
@abc.abstractmethod
def submit_task_via_zapier(task_ir: QuEraTaskSpecification, task_id: str):
    """Submit a task and add task_id to the task fields for querying later.

    args:
        task_ir: The task to be submitted.
        task_id: The task id to be added to the task fields.

    returns
        response: The response from the Zapier webhook. used for error handling

    """
    ...

visualization

display

atom_arrangement_figure

atom_arrangement_figure(atom_arrangement, assignments)

show the register.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/visualization/display.py
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
def atom_arrangement_figure(atom_arrangement, assignments):
    """show the register."""
    p = atom_arrangement.figure(None, **assignments)

    # get the Blocade rad object
    cr = None
    for rd in p.renderers:
        if rd.name == "Brad":
            cr = rd

    # adding rydberg radis input
    Brad_input = NumericInput(
        value=0, low=0, title="Blockade radius (um):", mode="float"
    )

    # js link toggle btn
    toggle_button = Button(label="Toggle")
    toggle_button.js_on_event(
        "button_click",
        CustomJS(args=dict(cr=cr), code="""cr.visible = !cr.visible;"""),
    )

    # js link radius
    Brad_input.js_link("value", cr.glyph, "radius")

    full = column(p, row(Brad_input, toggle_button))
    # full.sizing_mode="scale_both"
    return full

report_visualize

plot_register_bits

plot_register_bits(geo)

obtain a figure object from the atom arrangement.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/visualization/report_visualize.py
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
def plot_register_bits(geo):
    """obtain a figure object from the atom arrangement."""
    # xs_filled, ys_filled, labels_filled, density_filled = [], [], [], []
    # xs_vacant, ys_vacant, labels_vacant, density_vacant = [], [], [], []
    xs = []
    ys = []
    bits = []
    labels = []

    x_min = np.inf
    x_max = -np.inf
    y_min = np.inf
    y_max = -np.inf
    for idx, location_info in enumerate(zip(geo.sites, geo.filling)):
        (x, y), filling = location_info
        x = float(Decimal(str(x)) * Decimal("1e6"))  # convert to um
        y = float(Decimal(str(y)) * Decimal("1e6"))  # convert to um
        x_min = min(x, x_min)
        y_min = min(y, y_min)
        x_max = max(x, x_max)
        y_max = max(y, y_max)

        ys.append(y)
        xs.append(x)
        bits.append(0)
        labels.append(idx)
    print(x_min, x_max, y_min, y_max)

    if len(geo.sites) > 1:
        length_scale = np.inf
        for i, site_i in enumerate(geo.sites):
            for site_j in geo.sites[i + 1 :]:
                dist = np.linalg.norm(np.array(site_i) - np.array(site_j)) / 1e-6
                length_scale = min(length_scale, dist)
    else:
        length_scale = 1

    source = ColumnDataSource(data=dict(_x=xs, _y=ys, _bits=bits, _labels=labels))

    hover = HoverTool()
    hover.tooltips = [
        ("(x,y)", "(@_x, @_y)"),
        ("index: ", "@_labels"),
        ("state: ", "@_bits"),
    ]

    color_mapper = LinearColorMapper(palette="Magma256", low=0, high=1)

    # specify that we want to map the colors to the y values,
    # this could be replaced with a list of colors
    ##p.scatter(x,y,color={'field': 'y', 'transform': color_mapper})

    ## remove box_zoom since we don't want to change the scale

    p = figure(
        width=400,
        height=400,
        tools="wheel_zoom,reset, undo, redo, pan",
        toolbar_location="above",
        title="reg state",
    )
    # interpolate between a scale for small lattices
    # and a scale for larger lattices
    radius = get_radius(length_scale, x_min, x_max, y_min, y_max)
    window_size = max(x_max - x_min, y_max - y_min, 1)

    p.x_range = Range1d(x_min - length_scale, x_min + window_size + length_scale)
    p.y_range = Range1d(y_min - length_scale, y_min + window_size + length_scale)

    p.circle(
        "_x",
        "_y",
        source=source,
        radius=radius,
        fill_alpha=1,
        line_color="black",
        color={"field": "_bits", "transform": color_mapper},
        name="reg",
    )

    p.xaxis.axis_label = "(um)"
    p.add_tools(hover)

    return p

plot_register_ryd_dense

plot_register_ryd_dense(geo, ryds)

obtain a figure object from the atom arrangement.

Source code in .venv/lib/python3.12/site-packages/bloqade/analog/visualization/report_visualize.py
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
def plot_register_ryd_dense(geo, ryds):
    """obtain a figure object from the atom arrangement."""
    xs_filled, ys_filled, labels_filled, density_filled = [], [], [], []
    xs_vacant, ys_vacant, labels_vacant, density_vacant = [], [], [], []
    x_min = np.inf
    x_max = -np.inf
    y_min = np.inf
    y_max = -np.inf
    for idx, location_info in enumerate(zip(geo.sites, geo.filling, ryds)):
        (x, y), filling, density = location_info
        x = float(Decimal(str(x)) * Decimal("1e6"))  # convert to um
        y = float(Decimal(str(y)) * Decimal("1e6"))  # convert to um
        x_min = min(x, x_min)
        y_min = min(y, y_min)
        x_max = max(x, x_max)
        y_max = max(y, y_max)
        if filling:
            xs_filled.append(x)
            ys_filled.append(y)
            labels_filled.append(idx)
            density_filled.append(density)
        else:
            xs_vacant.append(x)
            ys_vacant.append(y)
            labels_vacant.append(idx)
            density_vacant.append(density)

    if len(geo.sites) > 1:
        length_scale = np.inf
        for i, site_i in enumerate(geo.sites):
            for site_j in geo.sites[i + 1 :]:
                dist = np.linalg.norm(np.array(site_i) - np.array(site_j)) / 1e-6
                length_scale = min(length_scale, dist)
    else:
        length_scale = 1

    source_filled = ColumnDataSource(
        data=dict(
            _x=xs_filled, _y=ys_filled, _labels=labels_filled, _ryd=density_filled
        )
    )
    source_vacant = ColumnDataSource(
        data=dict(
            _x=xs_vacant, _y=ys_vacant, _labels=labels_vacant, _ryd=density_vacant
        )
    )

    hover = HoverTool()
    hover.tooltips = [
        ("(x,y)", "(@_x, @_y)"),
        ("index: ", "@_labels"),
        ("ryd density: ", "@_ryd"),
    ]
    color_mapper = LinearColorMapper(palette="Magma256", low=min(ryds), high=max(ryds))

    # specify that we want to map the colors to the y values,
    # this could be replaced with a list of colors
    ##p.scatter(x,y,color={'field': 'y', 'transform': color_mapper})

    ## remove box_zoom since we don't want to change the scale

    p = figure(
        width=500,
        height=400,
        tools="wheel_zoom,reset, undo, redo, pan",
        toolbar_location="above",
        title="rydberg density",
    )
    radius = get_radius(length_scale, x_min, x_max, y_min, y_max)
    window_size = max(x_max - x_min, y_max - y_min, 1)

    p.x_range = Range1d(x_min - length_scale, x_min + window_size + length_scale)
    p.y_range = Range1d(y_min - length_scale, y_min + window_size + length_scale)

    # interpolate between a scale for small lattices
    # and a scale for larger lattices

    p.circle(
        "_x",
        "_y",
        source=source_filled,
        radius=radius,
        fill_alpha=1,
        line_color="black",
        color={"field": "_ryd", "transform": color_mapper},
    )

    p.circle(
        "_x",
        "_y",
        source=source_vacant,
        radius=radius,
        fill_alpha=1,
        # color="grey",
        line_color="black",
        color={"field": "_ryd", "transform": color_mapper},
        line_width=0.01 * length_scale,
    )

    color_bar = ColorBar(
        color_mapper=color_mapper,
        label_standoff=12,
        border_line_color=None,
        location=(0, 0),
    )

    p.xaxis.axis_label = "(um)"
    p.add_layout(color_bar, "right")
    p.add_tools(hover)

    return p