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
Related#
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.