Rotom

‘rotom’ Dialect

Dialect for Rotom’s layout representation. See the Rotom paper: https://eprint.iacr.org/2025/1319

Rotom attributes

DimAttr

A single Rotom layout dimension.

Encodes a traversal dimension: logical axis index, size, and stride.

Sentinel dim values (no source tensor axis):

  • -1: replication (logical fill along this layout piece)
  • -2: gap (padding / unused slots; constrained to zero in materialization)

Non-negative dim values index into the logical tensor shape. In the assembly form the sentinels are written R (replication) and G (gap), e.g. [R:4:1]; the numeric ids are also accepted on input.

Parameters:

ParameterC++ typeDescription
dimint64_t
sizeint64_t
strideint64_t

LayoutAttr

A Rotom layout made from an ordered list of dims.

A Rotom layout is an ordered list of rotom.dim dimensions plus the slot count n (ciphertext slot capacity).

Rotom’s split between ciphertext dims and slot dims is denoted by a | inside the dims list (e.g., dims = [[0:2:4] | [0:4:1]]). Slot dims must fill n exactly; unused capacity is denoted with an explicit gap piece (e.g. [G:4:1]). See Section 4.2 of the Rotom paper.

Optional rolls encode roll(from, by) metadata objects, applied left to right. Each argument is either a piece – a position in the dims list, written as a bare integer – or a whole tensor axis, written axis N (legal only when axis N is packed as more than one piece; the piece form is canonical for an unsplit axis, where the two coincide).

// Halevi-Shoup diagonal of a 4x4 matrix: slot j of ciphertext i holds
// A[i, (j + i) mod 4]. Argument 1 (the axis-1 piece) is rolled by
// argument 0 (the axis-0 piece).
#diag = #rotom.layout<n = 4, rolls = [(1, 0)], dims = [[0:4:1] | [1:4:1]]>

// Axis 1 is split across two pieces, so a whole-axis roll names its
// argument `axis 1` rather than a piece position.
#split = #rotom.layout<n = 16, rolls = [(axis 1, 2)],
                       dims = [[1:4:4], [1:4:1] | [0:16:1]]>

When an axis is split, each piece reads one part of that axis’s index: the piece of stride s and extent e reads (i / s) mod e, the way one digit of a number reads one part of its value. A piece FROM rewrites that part in place, part(from) <- (part(from) - shift(by)) mod extent(from), leaving the axis’s other pieces untouched. An axis FROM instead rewrites the whole axis index modulo its full extent and redistributes it over the pieces, so the subtraction can carry from one piece into the next.

For example, axis 0 of extent 4 split across two pieces of extent 2 and replicated twice, so replica d supplies the shift d:

// `[0:2:2]` carries the high part of axis 0's index and `[0:2:1]` the low
// part, so a replica's four slots hold elements 0, 1, 2, 3 in order. The
// two layouts differ only in whether the roll names the axis or the piece
// holding the low part.
#by_axis = #rotom.layout<n = 8, rolls = [(axis 0, 0)],
                         dims = [[R:2:1], [0:2:2], [0:2:1]]>
#by_piece = #rotom.layout<n = 8, rolls = [(2, 0)],
                          dims = [[R:2:1], [0:2:2], [0:2:1]]>

Replica 0 shifts by 0, so both layouts hold 0, 1, 2, 3. Replica 1 shifts by 1, and there they differ: #by_axis holds 1, 2, 3, 0, the whole axis rotated by one, while #by_piece holds 1, 0, 3, 2, each pair of slots rotated inside itself.

The shift is the by argument’s index: a traversal piece’s part of its axis index (the whole index when the axis is unsplit), a whole axis’s index via axis, a replication piece’s replica index, or a gap piece’s block index. The two extents need not match: the shift reduces modulo the rolled extent, so a smaller partner covers a prefix of the rotations and a larger one wraps.

Parameters:

ParameterC++ typeDescription
dims::mlir::ArrayAttr
nint64_t
rolls::mlir::DenseI64ArrayAttr

SeedAttr

A list of Rotom layouts for seeding.

Syntax:

#rotom.seed<
  ::mlir::ArrayAttr   # layouts
>

Contains a list of rotom.layout attributes representing the initial seeding layouts explored by Rotom.

Parameters:

ParameterC++ typeDescription
layouts::mlir::ArrayAttr