Skip to content
/ avm Public

A Lua library for working with numerical arrays

License

Notifications You must be signed in to change notification settings

eigenbom/avm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Arrays, Vectors and Matrices for Lua

AVM is a pure Lua library for working with arrays, vectors and matrices. It operates on flat arrays of numerical data and provides linear algebra operations for vectors and matrices. An array is typically a Lua table but can be any compatible userdata.

This README covers installation and basic examples. To dive deeper you can read the full API documentation at doc/api.md and read about the design principles at doc/design.md. Reference tests are provided in tests/.

Installation

The library is available in different "distributions" provided in this repository (e.g, for lua 5.1). Most of these have a debug variant that performs additional data validation at run-time.

To install this library first choose a distribution and copy into your local project directory. You can then require each module as needed. For example:

local array = require 'avm.array'

-- Create a new array a with values {1,2,3,4,5,6,7,8,9,10}
local a = array.range(1, 10)

-- Create a new array b with values {10,9,8,7,6,5,4,3,2,1}
local b = array.range(10, 1, -1)

-- Add them together and return a new array
local c = array.add(a, b)

-- Check every element of c is equal to 11
assert(array.all_equals_constant(c, 11))

Some distributions like picotron come as a single avm.lua file that wraps all functions in an avm global. This file can be placed into your project and, in the case of picotron, included using the system include function:

include 'avm.lua'
local one_to_ten = avm.array.range(1, 10)

Modules

The two most useful modules in AVM are:

  • array: Functions for working with numerical arrays
  • linalg: Linear algebra functions for vectors and matrices

In addition there are some helper modules:

  • vector_2, vector_3, and vector_4: Vector objects
  • matrix_2, matrix_3, and matrix_4: Matrix objects
  • iterator: Special purpose iterators
  • format: Support for printing arrays
  • view: Special view objects

Examples

Here are some introductory examples. Refer to the source files or tests/ for further examples.

Basic Usage

local zeros = array.zeros(3) -- {0,0,0}
local ones = array.fill(1, 3) -- {1,1,1}
local copy = array.copy(ones) -- create copy
assert(array.equal(ones, copy)) -- true
local first_ten = array.range(1,10) -- {1,2,...,9,10}
local thirds = array.range(0, .9, 1/3) -- {0, 1/3, 2/3}
local a = {1, 2, 3}
local b = {4, 5, 6}
local c = array.join(a, b) -- {1,2,3,4,5,6}
-- NOTE: Join creates a new array. Instead we can append to an existing array:
array.append(b, a)
-- a = {1,2,3,4,5,6}
local a = {1, 2, 3}
array.reverse(a) -- {3,2,1}

Operators

Mathematical functions are provided that either operate element-wise on pairs of arrays, or operate on an array and a constant.

local ones = array.fill(1, 3) -- {1,1,1}
local first_three = array.range(1,3) -- {1,2,3}
array.add(ones, first_three) -- {2,3,4}
array.add_constant(first_three, 2) -- {3,4,5}
array.mul(first_three, first_three) -- {1,4,9}
array.pow_constant(first_three, 2) -- {1,4,9}
array.min({1,2,3},{3,2,1}) -- {1,2,1}

Functional

Basic functional programming is supported with generators and maps. For more comprehensive functional programming see a library like Batteries.

local a = {0,0,0,0,0}
array.generate_into(5, function(i) return i*0.1 end, a)
-- a = {.1,.2,.3,.4,.5}
local f = function(x, y) return 1+x^y; end
array.map(f, {4,3,2}, {1,2,3}) -- {5,10,9}

Linear Algebra

local x = {1, 2, 3, 4}
local y = {1, 2, 3, 4}
local result = { linalg.mul_vec4(x, y) }
-- result = {1, 4, 9, 16}
local length = linalg.length_2(3, 4)
-- length = 5

local p = {-1, 1}
local x, y = linalg.normalise_vec2(p)
-- x, y = -1/√2, 1/√2
local x1, y1 = 1, 2
local x2, y2 = 3, 4
local dot = linalg.inner_product_2(x1, y1, x2, y2)
-- dot = 11
local a = {1, 2, 3}
local b = {4, 5, 6}
local expected_result = {-3, 6, -3}
local result = { linalg.cross_product_vec3(a, b) }
assert(array.almost_equal(result, expected_result))

NOTE: Matrix operations assume matrices are flat arrays stored in column-major order. Here we multiply two 2x2 matrices:

-- | 2 3 |
-- | 4 1 |
local a = {2, 4, 3, 1}

-- | 5 2 |
-- | 1 6 |
local b = {5, 1, 2, 6}

-- | 13 22 |
-- | 21 14 |
local expected = {13, 21, 22, 14}

local result = { linalg.matmul_mat2_mat2(a, b) }
assert(array.almost_equals(expected, result))

Multiplying a 4x4 matrix with a homogenous 3-D vector:

-- |  1  2  3  4 |
-- |  5  6  7  8 |
-- |  9 10 11 12 |
-- | 13 14 15 16 |
local a = {1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16}
local v = {1, 2, 3}
local expected_result = {18, 46, 74}
local result = { linalg.matmul_mat4_vec3(a, v) }

Vector Object

Here's a brief example of using the vector_2 module to simplify using 2D vectors:

-- Create two 2D vectors and sum them
local v1 = vector_2.new(1, 2)
local v2 = vector_2.new(3, 4)
local v3 = v1 + v2 -- or v1:add(v2)
print(v:xy()) --> "1 2"
print(v3:xy()) --> "4 6"

-- Multiply v3 by a constant `-1` and store result directly in v3
v3:mul_into(-1, v3)
print(v3:xy()) --> "-4 -6"

-- Vectors are arrays so can use array functions
local arr = array.copy(v3)
assert(arr[1] == -4 and arr[2] == -6)

-- Use linear algebra functions
local dot = linalg.inner_product_vec2(v1, v2)
assert(dot == 11)

Iterator

Iterate over an array and group elements in pairs using group:

local arr = {1,2,3,4,5,6}
for i, a, b in iterator.group_2(arr) do
	if i==1 then
		-- a = 1, b = 2
	elseif i==2 then
		-- a = 3, b = 4
	elseif i==3 then
		-- a = 5, b = 6
	end
end

Iterate over two arrays with zip:

local as = { 1,-2, 3,-4}
local bs = {-1, 2,-3, 4}
for i, a, b in iterator.zip_1(as, bs) do
	assert(a == -b)
end

View

Create a view of an array:

local data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
local odds_view = view.stride(data, 1 --[[start index]], 2 --[[stride]], 5 --[[count]])
assert(#odds_view == 5)
odds_view[1] = 42
assert(data[1] == 42)
local copy = array.copy(odds_view)
-- copy = {42, 3, 5, 7, 9}

Userdata

(Picotron) Generate 100 random numbers and store in a userdata:

local N = 100
local ud = userdata("f64", N)
local random = function() return rnd() end
array.generate_into(N, random, ud, 0)

(LuaJIT) Create a cdata array with values 1 to 100:

local N = 100
local cdata = ffi.new("int[?]", N)
array.range_into(1, N, 1, cdata, 0)

About

A Lua library for working with numerical arrays

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages