Vectors, Matrices, and Tensors
Vectors, matrices, and higher-rank tensors carry dimensions in their types.
Float[#3]
Float[#3] is a vector with exactly three Float values.
point: Float[#3] = {1.0, 2.0, 3.0}
-- point[2] == 3.0
Constructors
Constructors need compile-time dimension evidence, not arbitrary runtime integers.
import:
tensor: matrix, tensor3, vector
pure func default_weights() -> (Float[#3], Float[#2, #2]):
(vector(0.0, 3), matrix(1.0, 2, 2))
pure func filled_cube() -> Int[#2, #2, #2]:
tensor3(7, 2, 2, 2)
Shape-Preserving Maps
A map changes elements while retaining the tensor's dimensions.
import:
vector: map
func doubled[#N](values: Int[#N]) -> Int[#N]:
map(values, pure func(value: Int): value * 2)
Reductions
Reductions return a scalar or, for argmax and argmin, a proven-safe index. ..#N is an integer type proving 0 <= index < N, not a range value. An index with this type can safely select from a vector of length N.
import:
vector: argmax, dot, mean, sum
pure func summarize(scores: Int[#4]) -> (Int, Float, ..#4):
(scores.sum(), scores.mean(), scores.argmax())
pure func similarity(a: Float[#3], b: Float[#3]) -> Float:
a.dot(b)
Compile-Time Bounds
indices yields proven-safe subscripts in a loop. For a runtime index, use get; invalid indices return None.
import:
tensor: get, indices
pure func indexed_total[#N](values: Int[#N]) -> Int:
var total: Int = 0
for i in indices(values):
total += values[i]
total
func main(args: List[String]) -> Void:
values: Int[#3] = {2, 4, 6}
print(indexed_total(values)) -- prints: 12
match get(values, 3):
Some(value): print(value)
None: print("out of bounds") -- prints: out of bounds
Dot Product
vector.dot requires both operands to have the same element type and dimension.
import:
vector: dot
func main(args: List[String]) -> Void:
a: Float[#3] = {1.0, 2.0, 3.0}
b: Float[#3] = {4.0, 5.0, 6.0}
score: Float = dot(a, b)
-- score == 32.0
Matrix Basics
a * b multiplies corresponding elements. matrix.multiply performs matrix multiplication; multiply_vector applies a matrix to a vector. Their result types preserve the required dimensions.
m: Float[#2, #2] = {
{1.0, 0.0},
{0.0, 1.0},
}
-- m[1, 1] == 1.0
Parallel Transformations
A pure callback builds a shape-preserving ParallelVector pipeline. The scoped view supports mapping, not filtering, indexing, or escape.
import:
parallel_vector: map
vector: ParallelVector, parallel
pure func boost(values: Float[#4]) -> Float[#4]:
values.parallel(pure func(chunk: ParallelVector[Float, #4]):
chunk.map(pure func(value: Float): value + 1.0)
)
func main(args: List[String]) -> Void:
result: Float[#4] = boost({1.0, 2.0, 3.0, 4.0})
print(result[0]) -- prints: 2
print(result[3]) -- prints: 5
Example
import:
float: sqrt
matrix: multiply_vector
vector: dot
pure func distance(a: Float[#3], b: Float[#3]) -> Float:
delta: Float[#3] = a - b
sqrt(dot(delta, delta))
func main(args: List[String]) -> Void:
p: Float[#3] = {1.0, 2.0, 3.0}
q: Float[#3] = {1.0, 4.0, 3.0}
transform: Float[#2, #2] = {
{1.0, 2.0},
{3.0, 4.0},
}
point: Float[#2] = {5.0, 6.0}
moved: Float[#2] = multiply_vector(transform, point)
print(distance(p, q)) -- prints: 2
print(moved[0]) -- prints: 17
print(moved[1]) -- prints: 39
Try it
blorp run vectors.brp