Batches and selections¶
Construct a batch¶
Use Batch[T] to work with many integers, rationals,
floats, complex numbers, or balls at once. Start with a vector of integers:
Batch[Integer]() is empty, Batch[Integer](items) accepts a List[Integer], and
Batch[Integer].from_native([1, 2, 3]) converts native integers.
from_iterable(range(4)) consumes a finite iterable.
Integer batches print as lists of decimal values, with [] for an empty
batch. Arithmetic operators work element by element. A scalar such as 3
broadcasts in values + 3; two one-dimensional batches must have equal
lengths, even if one has just one element.
Snapshots and slicing¶
Indexing returns an independent value. Slices such as values[:],
values[::2], and values[::-1] share the stored elements but keep their
values when the original changes. A slice is itself a batch that you can
update; updating it does not change the original.
To change the original, assign through its slice: values[::2] = 0.
"""Batch snapshots, selections and updates."""
from apn_mojo import Batch, Integer, sum
def main() raises:
var values = Batch[Integer].from_native([4, -1, 0, -3, 8])
var original = values[:]
var negative = values < 0
var selected = values[negative]
values[negative] = 0
print("negative count:", negative.count())
print("selected values:", selected)
print("totals:", sum(original), sum(values))
values[1:] = values[:-1]
print("overlap:", values)
values[0] = 18446744073709551616
print("wide assignment:", values)
print("original snapshot:", original)
var sequence = Batch[Integer].from_iterable(range(4))
for item in sequence[::-1]:
print("reverse:", item)
print("all nonnegative:", (values >= 0).all())
Run from the repository root pixi run mojo run -I src docs/examples/batches.mojo
Output
negative count: 2
selected values: [-1, -3]
totals: 8 12
overlap: [4, 4, 0, 0, 0]
wide assignment: [18446744073709551616, 4, 0, 0, 0]
original snapshot: [4, -1, 0, -3, 8]
reverse: 3
reverse: 2
reverse: 1
reverse: 0
all nonnegative: True
Conditional selection and masks¶
values < 0 produces a Mask. Combine conditions with parentheses and
bitwise operators, such as (values >= 0) & (values < 10). Use .any(),
.all(), or .count() to summarize a mask. Converting a mask to Bool
raises because it does not specify which of those decisions you want.
values[mask] collects the selected elements in logical order.
values[mask] = 0 updates those elements. A batch used as the replacement
must have as many elements as the mask selects. Masks keep their bits when
the original numbers change; they do not rerun the comparison.
Assignment and iteration¶
Index, slice, and mask assignments read the source before changing the
destination. This lets overlapping assignments such as
values[1:] = values[:-1] use the original values throughout. Assignments
keep the batch's shape.
You can update a whole batch with values += 1, or a selection with
values[mask] += 1 or values[1:3] *= 2. A failed update leaves the
destination unchanged.
Iteration visits values in logical order using a snapshot of the sequence.
You can update the batch variable during iteration without changing that
snapshot. This guarantee does not cover concurrent mutation from multiple
threads. Use to_list() to collect APN elements in a Mojo list. For a list
of native numbers, use to_native[dtype](); see native output.
More dimensions and other number types¶
Rank and shape are runtime properties of Batch[T]. Higher-rank arithmetic
aligns trailing dimensions, which must match or have size one. Masks must
match the selected shape and do not broadcast. The
batch reference covers reshaping, axes, and mapping
rows or columns with vmap.
vmap[f] applies scalar functions to every batch element family, and lift[f]
adds folds and outer products to binary functions. The
vectorization tutorial walks through custom functions,
axes, shapes, and rounding choices.
NumPy-style functions¶
If you know NumPy, from apn_mojo import batch gives you familiar calls such
as batch.exp(xs), batch.atan2(ys, xs), batch.where(xs > 0, xs, zero),
batch.zeros[Integer]([2, 3]), and batch.cumsum(xs, axis=1). The functions
reuse scalar arithmetic: Integer results stay exact, Float results round
once, and balls enclose their results. Elementwise arguments broadcast over
matching or size-one trailing dimensions, including one-element vectors.
A scalar is shared by every element. Unlike these functions, operators on
two vectors require equal lengths. Scalar functions remain available at the
package root.
"""NumPy-style functions of batches: apn_mojo.batch."""
from apn_mojo import ArithmeticContext, Batch, Float, FloatFormat, Integer, batch
def main() raises:
var grid = batch.arange[Integer](6).reshape([2, 3])
print("grid + row:", batch.add(grid, Batch[Integer]([10, 20, 30])))
print("halves:", batch.divide(grid, 2))
print("large kept:", batch.where(grid > 2, grid, 0))
print("running sums:", batch.cumsum(grid, axis=1))
print("stacked shape:", batch.stack([grid, grid]).shape())
var c = ArithmeticContext(format=FloatFormat.binary64())
var xs = batch.linspace[Float](0, 1, 5, context=c)
print("xs:", xs)
print("exp:", batch.exp(xs))
print("atan2(1, xs):", batch.atan2(1, xs))
print("exp(5), rounded once:", batch.exp(grid, context=c)[1, 2])
Run from the repository root pixi run mojo run -I src docs/examples/batch_functions.mojo
Output
grid + row: [[10, 21, 32], [13, 24, 35]]
halves: [[0, 1/2, 1], [3/2, 2, 5/2]]
large kept: [[0, 0, 0], [3, 4, 5]]
running sums: [[0, 1, 3], [3, 7, 12]]
stacked shape: [2, 2, 3]
xs: [0.0, 0.25, 0.5, 0.75, 1.0]
exp: [1.0, 1.2840254166877414, 1.6487212707001282, 2.117000016612675, 2.718281828459045]
atan2(1, xs): [1.5707963267948966, 1.3258176636680326, 1.1071487177940904, 0.9272952180016122, 0.7853981633974483]
exp(5), rounded once: 148.4131591025766
The batch reference lists the
functions and their rules. Integer, rational, float, and complex batches also
provide arithmetic operators, built-in reductions, and JSON. For ball and
complex ball batches, use the NumPy-style functions or map functions from
apn_mojo.ball and apn_mojo.complex_ball with vmap or lift. See
Ball and ComplexBall.
Native output¶
values.to_native[DType.float64]() returns a flat List[Float64] in
row-major order. Keep values.shape() alongside it to retain dimensions.
Floating-point output rounds each element once; integer output requires
exact whole numbers in range. to_list() instead keeps the APN types and
their precision. The native conversion example
also covers fixed-size Mojo arrays, complex components, and Ball midpoints.
Choose the thread count¶
Import set_num_threads and get_num_threads from apn_mojo, or set
APN_MOJO_NUM_THREADS before starting the program. The count includes the
calling thread; use 1 to keep all work on it. Short operations may run on
the caller even with a larger configured count. See the
thread example and defaults.