Skip to content

Numeric Arrays ​

Array[T] is Aura's owned, contiguous numeric container for the CPU. Use it for local preprocessing, postprocessing, evaluation, and batch-shaped numeric work. It is smaller than a general tensor framework:

  • Shape is runtime metadata.
  • Storage is row-major and lives on the host.
  • Every result owns its buffer.

The only dtypes, or element types, are int32, int64, float32, and float64.

Aura
def main() -> int32:
    left = Array[float64].from_list([1.0, 2.0, 3.0, 4.0], [2, 2])
    right = Array[float64].full([2, 2], 0.5)
    combined = left + right
    first_row = combined[0:1]
    print(combined.shape())
    print(combined[1, 0])
    print(first_row.sum())
    print(combined.mean())
    return 0

Grammar ​

Array is a global builtin generic type, not a module. It uses the ordinary grammar for specialization, calls, member calls, indexing, indexed assignment, and one-colon slices:

text
Array [ dtype ]
Array [ dtype ] . constructor ( arguments )
array [ expression { , expression } ]
array [ [ expression ] : [ expression ] ]

The supported dtype names are exactly int32, int64, float32, and float64. Comma-separated Array indexing is different from list indexing. A one-colon slice selects a range along the first axis. Grammar defines the complete syntax.

Aura has no Array literal, dtype value, rank annotation, array-shape broadcast syntax, view syntax, step slice, or multidimensional slice tuple.

Typing Rules ​

Constructors ​

These are all the constructors:

ConstructorResult
Array[T].zeros(shape: list[int64])Array[T]
Array[T].full(shape: list[int64], value: T)Array[T]
Array[T].from_list(values: list[T], shape: list[int64])Array[T]

T must be one of the four dtypes. Shape is a runtime list[int64], so rank and dimensions are not part of the static type.

from_list requires exactly list[T]. It copies the scalar elements, and the shared source list stays usable. Constructors never infer a different dtype from a mixed numeric source.

Members ​

MemberResult and contract
shape()list[int64]; owned shape snapshot
len()int64; total element count
clone()fresh Array[T]
get(index: list[int64])T | None; None for an out-of-bounds coordinate or rank mismatch
set(index: list[int64], value: T)mutable receiver; T | None holding the replaced value, or a coordinate/rank trap rather than None
fill(value: T)mutable receiver; returns None
map[U](f: def(T) -> U)Array[U]; U is one of the four dtypes
sum()T
min()T
max()T
mean()float64 for every input dtype

map requires a repeatable callable whose bare parameter type and return type match exactly. The checker rejects a consuming closure, a mut or own parameter, and an unsupported result dtype. set and fill require a mutable Array place.

Indexing And Slicing ​

Direct indexing takes exactly one int64 coordinate per runtime axis. array[i, j] has type T. Indexed assignment requires a mutable Array place and a value of exactly T. array[start:end] returns Array[T].

Operators ​

+, -, and * accept either of these operand pairs and return a fresh Array[T]:

  • two Arrays with the same dtype and exactly the same shape
  • one Array and one scalar of exactly T, in either order

/ has the same forms for floating Arrays only. Integer Array / is rejected with AU2003.

There is no array-shape broadcasting and no mixed promotion. An Array[int32] and an Array[int64] do not combine. A bound scalar is never implicitly widened or narrowed for an Array operation.

Wrapping And Saturating Arithmetic ​

Every scalar integer type, plus Array[int32] and Array[int64], provides wrapping_add, wrapping_sub, wrapping_mul, saturating_add, saturating_sub, and saturating_mul. The Array methods accept one scalar of the same dtype or one Array of the same shape. Ordinary arithmetic stays checked.

Runtime Semantics ​

Shape And Layout ​

An Array has rank one or more and owns one contiguous row-major buffer. Dimensions are int64. They can be zero but not negative. len() is the checked product of all dimensions, so a zero dimension makes the Array empty while keeping its full shape.

zeros, full, and from_list lay out elements in row-major order, and direct coordinates map to storage in the same order.

Coordinates ​

A negative coordinate is normalized once against its own axis. For an invalid coordinate:

  • get returns None.
  • Method set, direct indexed reads, and direct indexed assignment trap.

A valid set returns the previous scalar as its T | None result.

First-Axis Slices ​

array[start:end] selects complete rows along axis zero. Written endpoints have exact type int32. Omitted bounds and one-time negative normalization follow the owned-slice rules for lists. Endpoints never clamp. The result has shape [end - start] followed by the source's remaining dimensions. It is fresh storage that never aliases the source.

Arithmetic ​

  • Array/Array operations require exactly equal shapes.
  • Scalar forms apply the scalar to every element in row-major order.
  • Results own fresh contiguous storage.
  • Floating / follows the ordinary floating operator rules.
  • Integer +, -, *, and sum keep checked overflow.
  • Wrapping operations use fixed-width two's-complement modular arithmetic.
  • Saturating operations clamp at the declared integer width.

Reductions ​

map, reductions, fill, and elementwise kernels walk storage in row-major order.

  • sum() of an empty Array returns the dtype's zero.
  • min(), max(), and mean() require at least one element.
  • Floating reductions visit elements from left to right with deterministic dtype rounding, and they propagate NaN.
  • mean() accumulates and returns a float64 result for every source dtype.

The contract promises no reassociation and no vectorized reduction order.

Ownership And Evaluation Order ​

Array[T] is non-Copy and cloneable. Assignment and owned argument passing move the buffer. .clone() is the explicit way to duplicate the whole buffer.

An Array is always structurally Transfer, because every dtype is a Transfer scalar. A Task result that holds an Array still has the ordinary single-consumer observation right. Bare parameters and receivers give shared access. set and fill require exclusive mutable access.

Evaluation order:

  • Constructors evaluate their arguments once, from left to right.
  • A binary operation evaluates the left operand, then the right. It keeps both Arrays for the kernel and consumes neither shared operand.
  • Coordinates evaluate from left to right.
  • A direct indexed assignment captures its coordinate before it evaluates the new value.

Elementwise operations, map, and first-axis slices allocate a fresh result. map calls its repeatable callback once per element in row-major order, and moves or copies each scalar result into the output. If a trap occurs, any partial output is cleaned up. Shape snapshots and first-axis slices are owned copies, not views.

Diagnostics ​

Compile-time codes:

CodeCause
AU2001Unknown Array member or constructor.
AU2002Unsupported dtype, mismatched argument, callback, or result type, or mixed dtypes.
AU2003Unsupported operator, including integer Array /. The message keeps the ordinary checked-integer guidance.
AU2004Invalid argument binding.
AU2005Slice steps and slice assignment, which are reserved. The message gives the same owned-copy guidance as for list and str slices.
AU3002Mutation while shared access is active.
AU3003set, fill, or indexed assignment through an immutable place.

Runtime codes:

CodeCause
AU4002Checked integer Array arithmetic overflows.
AU4003A direct coordinate is out of range, or a first-axis slice is invalid or reversed. get returns None instead, but method set traps.
AU4004Floating Array division with any zero divisor.
AU4005Shape product or element count overflows, or allocation fails.
AU4007Numeric array shape or reduction violation. See below.

AU4007 (numeric array shape or reduction violation) covers:

  • construction with rank zero or a negative dimension
  • a from_list element count that does not match the shape
  • an Array/Array operation whose shapes differ
  • a direct coordinate count that does not match the runtime rank
  • min, max, or mean on an empty Array

These traps are defined language behavior. A backend must not panic in their place.

Backend Support ​

MIR and direct execution both implement:

  • constructors, indexing, and mutation
  • first-axis copies
  • map and reductions
  • checked, wrapping, and saturating arithmetic
  • scalar forms and exact-shape elementwise operations

Direct native execution uses contiguous kernels specialized per dtype. Both backends share checked types, evaluation order, row-major results, cleanup, and exact AU4003 and AU4007 behavior.

The compiler's analysis and the language server expose the same constructors, member signatures, result types, hover, definitions, completions, and diagnostics. The bundled VS Code extension uses that compiler-owned surface.

Limits And Implementation-Defined Behavior ​

Aura 0.6 Arrays are CPU-only, contiguous, row-major, and have rank one or more. They do not have:

  • array-shape broadcasting or mixed promotion
  • views, reshape, or transpose
  • matrix multiplication
  • equality or ordering
  • multidimensional slicing, step slices, or slice assignment
  • autograd, device placement, or distributed storage
  • aliasing of foreign buffers

Shape metadata is dynamic, and the checker does not prove that shapes are compatible. Allocation is limited by host memory and the element-count checks. Floating arithmetic follows the host IEEE-754 rules. This API is narrower than NumPy's.

Measured Performance ​

These measurements were taken on a quiet host, not after a reboot. They have not yet been repeated under the full post-reboot protocol.

Both runs are contractual under the benchmark runner's qualification rules:

  • clean detached sources
  • 11 rotating pairs, with warmups excluded
  • exact protocol and checksum validation
  • three empty host inventories
  • successful input and hash rechecks

No override was used.

SettingValue
HostMac14,9 / Apple M2 Pro / 16 GiB
PythonXcode CPython 3.9.6
NumPy2.0.2
Rust1.95.0
Threadssingle-threaded
Unittime per operation; ratios are ratios of medians

"Before" is merge base a368dce7e7b335c1c0cb800ba5240501a21f02bc. "After" is implementation head d9fc79921ba9fed1116634ec9994bcb599aa9f90. Each clean checkout ran its own unchanged runner in the same session, after the full local gates. Later documentation edits do not change compiler or runtime sources.

Workload (one million float64 elements)Aura beforeAura afterChange in timeBefore Aura / RustAfter Aura / Rust
Fresh owned addition1.244818 ms0.249473 ms-79.9591%5.0393591.006524
Existing-array sum1.149271 ms1.149844 ms+0.0498%1.3389901.339087
ControlBefore medianAfter medianDrift
NumPy add0.253398 ms0.247745 ms-2.2310%
Rust add0.247019 ms0.247856 ms+0.3387%
NumPy sum0.173397 ms0.172073 ms-0.7639%
Rust sum0.858312 ms0.858677 ms+0.0425%

Addition meets the 1.5x Rust target at 1.006524x and takes 79.9591% less Aura time. Its after Aura/NumPy ratio is 1.006974.

The sum ratio against NumPy is 6.682308. Most of that gap comes from the deterministic reduction-order policy. Under the same left-to-right order, Aura stays within 1.34x of Rust.

Addition allocates a fresh owned result, and sum reuses its input. These workloads do not rank Aura or the Array API in general.

The before raw data, after raw data, comparison and controls, and SHA256SUMS keep every observation and the identities of every source and binary.

Kernel Implementation ​

The shared runtime that MIR and direct execution use picks each floating-point operation before entering its slice loop. Release arm64 disassembly contains fadd.4s and fadd.2d, plus the matching fsub, fmul, and fdiv vector forms, including scalar broadcasts. Division checks for the first zero divisor after the fallible output allocation. Integer arithmetic and sequential reductions keep their existing implementations.

Both runtime paths match 1,008 frozen output-bit and diagnostic cases, recorded before the kernels were written, in debug and optimized release builds. The cases cover:

  • all four element types
  • empty and vector-boundary lengths, and 1,000,001 elements
  • NaN payloads, infinities, and signed zero
  • arithmetic modes and first-trap indices

The kernels do not reassociate, do not approximate division, and add no new numeric policy.

Status ​

Contiguous numeric Arrays and explicit scalar and Array integer arithmetic modes are part of Aura 0.3. The contract is exactly the surface on this page. It does not include tensor placement, views, shape transformations, or distributed execution.

Design records: ADR-0041: Contiguous numeric arrays and explicit integer arithmetic modes and ADR-0002: Integer division and modulo.

Aura 0.6.0 technical preview. Implementation baseline: 4d7e076de9ff6e2e056cdec10881d4bf421358de.