# Operator contracts

Most operators need no more than their [catalog](https://ambiqai.github.io/helia-aot/reference/operators/)
row. The ones below have a contract worth reading before you rely on them:
which inputs they take, what the generated code calls, and where the result
can differ from LiteRT. [Boolean operators](https://ambiqai.github.io/helia-aot/guide/boolean-operators/)
have their own page.

The INT32 and floating-point routes call heliaCORE directly, with the
dependency described in the
[installation guide](https://ambiqai.github.io/helia-aot/getting-started/install/). The Ethos-U fence is
an identity copy and calls no kernel.

## INT32 comparisons and selection

EQUAL, NOT_EQUAL, GREATER, GREATER_EQUAL, LESS and LESS_EQUAL take unquantized
INT32 inputs and produce Boolean outputs. Scalar through rank-four operands use
singleton broadcasting, and the broadcast output count must fit INT32_MAX / 4.

SELECT_V2 takes a Boolean condition and INT32 x and y tensors with singleton
broadcasting through rank eight. All dimensions must be positive and each
tensor's byte count must fit INT32_MAX. Comparisons preserve full-width signed
values; selection copies values exactly. Neither path needs float support or
scratch. The INT8 and INT16 routes keep their quantized behavior.

## Floating-point division

`DIV` lowers FP32 and FP16 operands to heliaCORE's direct division kernels.
Equal layouts use a flat call; scalar through rank-four broadcasting uses
compact, left-padded NHWC operands without scratch or expanded tensors. Operand
order is preserved, including a scalar numerator. Integer DIV is not supported.

- Both operands and the output share a dtype and have positive static extents;
  each tensor's byte count fits `INT32_MAX`. FP16 needs an M55-class target.
- NONE, RELU, RELU_N1_TO_1 and RELU6 use the same dtype-specific activation
  bounds as float ADD and MUL; unbounded ends are finite dtype limits. NaNs
  propagate through the clamp. FP16 rounds the quotient to half storage before
  the activation. No reciprocal-multiply approximation is emitted.
- The heliaCORE FP16 clamp turns negative zero into positive zero under RELU
  and RELU6, where LiteRT's FP32 reference keeps the sign, so fused FP16
  division does not promise bitwise parity for signed zero. LiteRT does not run
  native FP16 DIV; finite FP16 reference checks widen the inputs to FP32 and
  round the reference output to half precision.
- heliaCORE's CMake integration passes `-fno-fast-math` for the DIV sources
  under Clang and Armclang; a manual heliaCORE build must keep it.

## Floating-point NEG

NEG takes FP32 and FP16 tensors of any rank, scalars included, with identical
input and output shapes and positive static dimensions. Tensor byte counts must
fit INT32_MAX; FP16 needs a Cortex-M55-class target. The generated layer calls
heliaCORE directly, without scratch or arithmetic loops.

Negation toggles the IEEE sign bit: subnormal values and NaN payloads are
preserved, and positive and negative zero exchange. Integer NEG is not
supported.

## Floating-point squared difference

SQUARED_DIFFERENCE takes FP32 and FP16 operands with equal layouts and positive
static dimensions, scalars included. Leading singleton axes can differ through
rank four; higher ranks need identical shapes. Float broadcasting is rejected;
integer broadcasting is supported.

- heliaCORE computes a rounded subtraction followed by a rounded multiplication
  at the selected precision. NaN payloads are unspecified.
- Explicit MVE uses its architectural flushing of subnormals, which can differ
  from scalar gradual underflow. From heliaCORE 7.47.0 the FP32 entry redoes on
  the scalar FPU each lane whose square MVE flushed, so at round-to-nearest it
  gives the scalar result, subnormal squares included; releases below the
  7.47.0 minimum flush them.
- FP16 needs a Cortex-M55-class platform. Each tensor's byte count fits
  INT32_MAX. The generated layer calls the flat heliaCORE entry without scratch
  or activation clamp.

## Split command streams

A Vela-compiled model may contain `ETHOS_U_STREAM_SPLIT` version 1 between
`ethos-u` operators. heliaAOT keeps the marker as an identity-copy operator:
each surrounding Ethos-U operator issues its own driver invocation, and the
fence runs between them. Identity pruning does not remove it.

- The fence takes matching nonempty input and output lists; each pair has the
  same shape, dtype and quantization, and pairs may differ from each other.
- Internal pairs may carry per-axis metadata; model inputs and outputs need
  per-tensor quantization, because the I/O descriptors expose one scale and
  zero point.
- Each pair independently preserves its eligibility to carry a zero scale from
  a constant through internal scratch tensors; model I/O and multiple-writer
  tensors keep the usual safeguards.
- Version 1 supports int8, uint8, int16, int32, int64, FP16, FP32 and bool.
  Byte counts come from tensor metadata; packed INT4 and variable-length
  storage are unsupported.
- Distinct buffers are copied; an exact alias within a pair is a no-op.
  Partial overlap, or a destination overlapping another pair, returns an error
  before any copying. The marker has no options and issues no NPU command.
- Insert the markers before compiling with Vela, then pass the compiled
  `.tflite` to heliaAOT, which neither inserts fences nor runs Vela. Execution
  stays sequential; interleaving another model or yielding to an RTOS needs
  application or driver scheduling.
- The producer's
  [version-1 contract](https://github.com/AmbiqAI/helia-estimator/pull/177)
  defines the wire format. Splitting can change scheduling, memory use and
  latency, so measure the resulting model.
