Renderingen-US

tensor

Translation status: English reader-locale proof. Term names and code fences follow the en pack; supporting prose may still be English.

Tensor comparisons and logic lift per element: comparisons give a bool tensor, and/or/not and the ✓ ✗ select evaluate both sides, and vel fills the none elements.

Syntax: tensor > tensor | tensor ≤ scalar | mask and mask | not mask | mask ✓ a ✗ b | tensor coalesce fallback

Category#

collection

Examples#

radix/corpus/tensor/decl.fab (canonical · type)#

tensor<T, Figura> declaration shell with rank-0 empty.

# =============================================================================
# tensor — tensor<T, Figura> declaration shell with rank-0 empty.
# =============================================================================
#
# What this teaches:
#   • Tensor declaration — `tensor<T, Figura>` type syntax with `empty` as the empty initializer
#   • Identity functions — passing tensors through functions preserves shape and rank
#
# Common mistakes:
#   • declaring tensor<T> without a Figura shape parameter — use tensor<T, Figura> with explicit dimensions
#
# See also: tensor, list
# =============================================================================




# tensor — declaration shell for tensor<T, Figura>
#
# tensor<T, []> name ← empty
# fn f(tensor<T, []> value) → tensor<T, []>
#
# GRAMMAR:
#   type :← 'tensor' '<' type ',' shape '>'
#
# EXPECTED OUTPUT:
#   Rank-0 tensor longitudo after round-trip identity call.

fn identity(tensor<f32, []> value) → tensor<f32, []> {
    return value
}

main {
    const tensor<f32, []> empty ← empty
    const tensor<f32, []> roundtrip ← identity(empty)

    print roundtrip.length()
}

Expected output:

0

radix/corpus/tensor/lift-arith.fab (canonical · operator-group)#

Tensor arithmetic lifts over a scalar: + - * / % ÷ and unary minus apply per element; ⤒ ⤓ take the elementwise extremum of two tensors.

# =============================================================================
# tensor — Elementwise arithmetic lifted over a scalar.
# =============================================================================
#
# What this teaches:
#   • Lifting — a scalar operator applied per element: `a + 1.0`, `10.0 - a`, `a * 0.5`
#   • `* / %` lift only against a scalar; a tensor times a tensor is `⊙` (elementwise) or `·` (contraction)
#   • Division has two kinds — `/` floors on integers, `÷` is true division (16-bit and narrower integers give f32)
#   • Extrema — `a ⤒ b` and `a ⤓ b` pick the larger or smaller element of two same-shape tensors
#   • Widths follow the scalar rules: an untyped constant adopts the element type, a family crossing needs `↦`
#
# Common mistakes:
#   • writing `a * b` for an elementwise product — use `a ⊙ b`
#   • mixing a float scalar with an integer tensor (`n * 0.5`) — convert the tensor with `↦` first
#   • expecting tensors of different shapes to stretch — shapes must be identical
#
# See also: tensor, ⊙, ÷
# =============================================================================

# tensor lift — arithmetic against a scalar
#
# WHY: each operator is the scalar operator applied to every element, so the
# runner's answer for one element is the answer for the whole tensor. Results
# are stored per element under the element type.
#
# EXPECTED stdout:
#   [2.0, 3.0, 4.0, 5.0]
#   [9.0, 8.0, 7.0, 6.0]
#   [0.5, 1.0, 1.5, 2.0]
#   [0.25, 0.5, 0.75, 1.0]
#   [1.0, 2.0, 0.0, 1.0]
#   [-1.0, -2.0, -3.0, -4.0]
#   [0, 1, 1, 2]
#   [1, 0, 1, 0]
#   [0.5, 1.0, 1.5, 2.0]
#   [4.0, 3.0, 3.0, 4.0]
#   [1.0, 2.0, 2.0, 1.0]

main {
    const list<f32> flat_a ← [1.0, 2.0, 3.0, 4.0]
    const list<f32> flat_b ← [4.0, 3.0, 2.0, 1.0]
    const tensor<f32, []> seed ← empty
    var tensor<f32, [2, 2]> a ← seed.from_flat(flat_a, [2, 2])
    var tensor<f32, [2, 2]> b ← seed.from_flat(flat_b, [2, 2])

    const tensor<f32, [2, 2]> plus ← a + 1.0
    const list<f32> plus_out ← plus.flatten()
    print plus_out
    const tensor<f32, [2, 2]> minus ← 10.0 - a
    const list<f32> minus_out ← minus.flatten()
    print minus_out
    const tensor<f32, [2, 2]> times ← a * 0.5
    const list<f32> times_out ← times.flatten()
    print times_out
    const tensor<f32, [2, 2]> divided ← a / 4.0
    const list<f32> divided_out ← divided.flatten()
    print divided_out
    const tensor<f32, [2, 2]> rest ← a % 3.0
    const list<f32> rest_out ← rest.flatten()
    print rest_out
    const tensor<f32, [2, 2]> negated ← -a
    const list<f32> negated_out ← negated.flatten()
    print negated_out

    const list<i16> flat_n ← [1, 2, 3, 4]
    const tensor<i16, []> nseed ← empty
    var tensor<i16, [4]> n ← nseed.from_flat(flat_n, [4])
    const tensor<i16, [4]> floored ← n / 2
    const list<i16> floored_out ← floored.flatten()
    print floored_out
    const tensor<i16, [4]> odd ← n % 2
    const list<i16> odd_out ← odd.flatten()
    print odd_out
    const tensor<f32, [4]> true_half ← n ÷ 2
    const list<f32> true_half_out ← true_half.flatten()
    print true_half_out

    const tensor<f32, [2, 2]> larger ← a ⤒ b
    const list<f32> larger_out ← larger.flatten()
    print larger_out
    const tensor<f32, [2, 2]> smaller ← a ⤓ b
    const list<f32> smaller_out ← smaller.flatten()
    print smaller_out
}

Expected output:

[2.0, 3.0, 4.0, 5.0]
[9.0, 8.0, 7.0, 6.0]
[0.5, 1.0, 1.5, 2.0]
[0.25, 0.5, 0.75, 1.0]
[1.0, 2.0, 0.0, 1.0]
[-1.0, -2.0, -3.0, -4.0]
[0, 1, 1, 2]
[1, 0, 1, 0]
[0.5, 1.0, 1.5, 2.0]
[4.0, 3.0, 3.0, 4.0]
[1.0, 2.0, 2.0, 1.0]

radix/corpus/tensor/lift-compare.fab (canonical · operator-group)#

Tensor comparisons and logic lift per element: comparisons give a bool tensor, and/or/not and the ✓ ✗ select evaluate both sides, and vel fills the none elements.

# =============================================================================
# tensor — Elementwise comparison, logic, select and vel.
# =============================================================================
#
# What this teaches:
#   • Comparisons — `< > ≤ ≥ ≡ ≠ ≅ ≇` on tensors give `tensor<bool, S>`, one verdict per element
#   • Logic — `and`, `or`, `not` act per element on bool tensors and always evaluate both sides
#   • Select — `mask ✓ a ✗ b` takes `a` where the mask is true and `b` elsewhere; both branches are evaluated
#   • vel (spelled `coalesce` in English) — `t coalesce fallback` fills the `none` elements of a `T ∪ none` tensor
#   • NaN compares false to everything, including itself
#
# Common mistakes:
#   • using a tensor mask as an `if` condition — a mask is a tensor; select or reduce it
#   • expecting `and` to skip its right side on a mask — both sides run
#   • using `coalesce` on a tensor whose elements can never be none
#
# See also: tensor, vel, ✓ ✗
# =============================================================================

# tensor lift — comparisons, logic, select, vel
#
# WHY: a comparison is the scalar comparison per element, and its result is a
# bool tensor of the same shape. Logic and select take such masks.
#
# EXPECTED stdout:
#   [false, false, true, true]
#   [true, true, false, false]
#   [false, false, true, true]
#   [true, false, false, true]
#   [false, true, false, false]
#   [4.0, 3.0, 3.0, 4.0]
#   [0.0, 0.0, 1.0, 1.0]
#   [1, 0, 3, 0]
#   [1, 20, 3, 40]

main {
    const list<f32> flat_a ← [1.0, 2.0, 3.0, 4.0]
    const list<f32> flat_b ← [4.0, 3.0, 2.0, 1.0]
    const tensor<f32, []> seed ← empty
    var tensor<f32, [2, 2]> a ← seed.from_flat(flat_a, [2, 2])
    var tensor<f32, [2, 2]> b ← seed.from_flat(flat_b, [2, 2])

    const tensor<bool, [2, 2]> greater ← a > b
    const list<bool> greater_out ← greater.flatten()
    print greater_out
    const tensor<bool, [2, 2]> small ← a ≤ 2.0
    const list<bool> small_out ← small.flatten()
    print small_out
    const tensor<bool, [2, 2]> not_small ← not small
    const list<bool> not_small_out ← not_small.flatten()
    print not_small_out
    const tensor<bool, [2, 2]> outer ← (a < 2.0) or (a > 3.0)
    const list<bool> outer_out ← outer.flatten()
    print outer_out
    const tensor<bool, [2, 2]> inner ← (a > 1.0) and small
    const list<bool> inner_out ← inner.flatten()
    print inner_out

    const tensor<f32, [2, 2]> bigger ← greater ✓ a ✗ b
    const list<f32> bigger_out ← bigger.flatten()
    print bigger_out
    const tensor<f32, [2, 2]> clipped ← (a > 2.5) ✓ 1.0 ✗ 0.0
    const list<f32> clipped_out ← clipped.flatten()
    print clipped_out

    const list<int ∪ none> flat_h ← [1, none, 3, none]
    const tensor<int ∪ none, []> hseed ← empty
    var tensor<int ∪ none, [2, 2]> holes ← hseed.from_flat(flat_h, [2, 2])
    const tensor<int, [2, 2]> zeroed ← holes coalesce 0
    const list<int> zeroed_out ← zeroed.flatten()
    print zeroed_out
    const list<int> flat_d ← [10, 20, 30, 40]
    const tensor<int, []> dseed ← empty
    var tensor<int, [2, 2]> defaults ← dseed.from_flat(flat_d, [2, 2])
    const tensor<int, [2, 2]> filled ← holes coalesce defaults
    const list<int> filled_out ← filled.flatten()
    print filled_out
}

Expected output:

[falsum, falsum, verum, verum]
[verum, verum, falsum, falsum]
[falsum, falsum, verum, verum]
[verum, falsum, falsum, verum]
[falsum, verum, falsum, falsum]
[4.0, 3.0, 3.0, 4.0]
[0.0, 0.0, 1.0, 1.0]
[1, 0, 3, 0]
[1, 20, 3, 40]

radix/corpus/tensor/arithmetic-reject.fab (supporting · reject)#

Tensor arithmetic rejects non-numeric elements and mixed numeric widths.

# =============================================================================
# tensor — Tensor arithmetic rejects non-numeric elements and mixed numeric widths.
# =============================================================================
#
# What this teaches:
#   • Type guard — elementwise arithmetic rejects non-numeric element types at compile time
#   • Width safety — mixed-width operations (e.g. i32 + i64) require explicit `↦` conversion
#
# Common mistakes:
#   • expecting silent numeric widening between i32 and i64 — use explicit ↦ conversion instead
#
# See also: tensor, type, conversion
# =============================================================================



# tensor arithmetic reject cases — expected compile failure
#
# WHY: documents the typecheck contract for elementwise tensor arithmetic.
# Both cases must reject:
#   1. tensor<string, …> — non-numeric element (numeric gate).
#   2. i32 + i64 — mixed width is a ↦ conversion problem,
#      never silent promotion at the kernel.
#
# This exemplum intentionally fails to compile; it is registered in the
# per-backend EXPECTED_FAILURES lists.

main {
    const tensor<string, [2]> words_a ← empty
    const tensor<string, [2]> words_b ← empty
    const tensor<string, [2]> words_sum ← words_a.added(words_b)
    print words_sum.length()

    const tensor<i32, [2]> i32_a ← empty
    const tensor<i64, [2]> i64_b ← empty
    const tensor<i32, [2]> mixed ← i32_a.added(i64_b)
    print mixed.length()
}

Expected: compilation rejects this example.