242 lines
6.1 KiB
Lua
242 lines
6.1 KiB
Lua
|
|
-- Helper function to create a matrix from nested blocks
|
||
|
|
local function block_matrix(blocks)
|
||
|
|
local m = {}
|
||
|
|
local num_hblocks = #blocks
|
||
|
|
local num_vblocks = #blocks[1]
|
||
|
|
|
||
|
|
-- Determine dimensions
|
||
|
|
local block_height = #blocks[1][1]
|
||
|
|
local block_widths = {}
|
||
|
|
for j = 1, num_vblocks do
|
||
|
|
block_widths[j] = #blocks[1][j][1]
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Build the resulting matrix
|
||
|
|
for i = 1, block_height * num_hblocks do
|
||
|
|
m[i] = {}
|
||
|
|
end
|
||
|
|
|
||
|
|
for h = 1, num_hblocks do
|
||
|
|
local row_offset = (h - 1) * block_height
|
||
|
|
for i = 1, block_height do
|
||
|
|
local col_offset = 0
|
||
|
|
for v = 1, num_vblocks do
|
||
|
|
local block = blocks[h][v]
|
||
|
|
for j = 1, block_widths[v] do
|
||
|
|
m[row_offset + i][col_offset + j] = block[i][j]
|
||
|
|
end
|
||
|
|
col_offset = col_offset + block_widths[v]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return m
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Matrix multiplication (naive)
|
||
|
|
local function matrix_multiply(a, b)
|
||
|
|
local rows_a, cols_a = #a, #a[1]
|
||
|
|
local rows_b, cols_b = #b, #b[1]
|
||
|
|
|
||
|
|
assert(cols_a == rows_b, "Incompatible matrix dimensions for multiplication")
|
||
|
|
|
||
|
|
local result = {}
|
||
|
|
for i = 1, rows_a do
|
||
|
|
result[i] = {}
|
||
|
|
for j = 1, cols_b do
|
||
|
|
local sum = 0
|
||
|
|
for k = 1, cols_a do
|
||
|
|
sum = sum + a[i][k] * b[k][j]
|
||
|
|
end
|
||
|
|
result[i][j] = sum
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return result
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Matrix addition
|
||
|
|
local function matrix_add(a, b)
|
||
|
|
local rows, cols = #a, #a[1]
|
||
|
|
assert(rows == #b and cols == #b[1], "Matrices must have the same dimensions")
|
||
|
|
|
||
|
|
local result = {}
|
||
|
|
for i = 1, rows do
|
||
|
|
result[i] = {}
|
||
|
|
for j = 1, cols do
|
||
|
|
result[i][j] = a[i][j] + b[i][j]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return result
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Matrix subtraction
|
||
|
|
local function matrix_subtract(a, b)
|
||
|
|
local rows, cols = #a, #a[1]
|
||
|
|
assert(rows == #b and cols == #b[1], "Matrices must have the same dimensions")
|
||
|
|
|
||
|
|
local result = {}
|
||
|
|
for i = 1, rows do
|
||
|
|
result[i] = {}
|
||
|
|
for j = 1, cols do
|
||
|
|
result[i][j] = a[i][j] - b[i][j]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return result
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Get submatrix
|
||
|
|
local function get_submatrix(m, start_row, end_row, start_col, end_col)
|
||
|
|
local result = {}
|
||
|
|
for i = start_row, end_row do
|
||
|
|
local row = {}
|
||
|
|
for j = start_col, end_col do
|
||
|
|
table.insert(row, m[i][j])
|
||
|
|
end
|
||
|
|
table.insert(result, row)
|
||
|
|
end
|
||
|
|
return result
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Strassen's algorithm
|
||
|
|
local function strassen_multiply(a, b)
|
||
|
|
local n = #a
|
||
|
|
local m = #a[1]
|
||
|
|
|
||
|
|
assert(n == m, "Matrix must be square")
|
||
|
|
assert(n == #b and n == #b[1], "Matrices must have the same dimensions")
|
||
|
|
|
||
|
|
-- Check if size is a power of 2
|
||
|
|
local temp = n
|
||
|
|
while temp > 1 do
|
||
|
|
assert(temp % 2 == 0, "Matrix dimension must be a power of 2")
|
||
|
|
temp = temp / 2
|
||
|
|
end
|
||
|
|
|
||
|
|
if n == 1 then
|
||
|
|
return {{a[1][1] * b[1][1]}}
|
||
|
|
end
|
||
|
|
|
||
|
|
local half = n // 2
|
||
|
|
|
||
|
|
-- Partition matrices into quadrants
|
||
|
|
local a11 = get_submatrix(a, 1, half, 1, half)
|
||
|
|
local a12 = get_submatrix(a, 1, half, half+1, n)
|
||
|
|
local a21 = get_submatrix(a, half+1, n, 1, half)
|
||
|
|
local a22 = get_submatrix(a, half+1, n, half+1, n)
|
||
|
|
|
||
|
|
local b11 = get_submatrix(b, 1, half, 1, half)
|
||
|
|
local b12 = get_submatrix(b, 1, half, half+1, n)
|
||
|
|
local b21 = get_submatrix(b, half+1, n, 1, half)
|
||
|
|
local b22 = get_submatrix(b, half+1, n, half+1, n)
|
||
|
|
|
||
|
|
-- Calculate the seven products
|
||
|
|
local m1 = strassen_multiply(matrix_add(a11, a22), matrix_add(b11, b22))
|
||
|
|
local m2 = strassen_multiply(matrix_add(a21, a22), b11)
|
||
|
|
local m3 = strassen_multiply(a11, matrix_subtract(b12, b22))
|
||
|
|
local m4 = strassen_multiply(a22, matrix_subtract(b21, b11))
|
||
|
|
local m5 = strassen_multiply(matrix_add(a11, a12), b22)
|
||
|
|
local m6 = strassen_multiply(matrix_subtract(a21, a11), matrix_add(b11, b12))
|
||
|
|
local m7 = strassen_multiply(matrix_subtract(a12, a22), matrix_add(b21, b22))
|
||
|
|
|
||
|
|
-- Calculate the four quadrants of the result
|
||
|
|
local c11 = matrix_add(matrix_subtract(matrix_add(m1, m4), m5), m7)
|
||
|
|
local c12 = matrix_add(m3, m5)
|
||
|
|
local c21 = matrix_add(m2, m4)
|
||
|
|
local c22 = matrix_add(matrix_subtract(matrix_add(m1, m3), m2), m6)
|
||
|
|
|
||
|
|
-- Combine quadrants into a single matrix
|
||
|
|
return block_matrix({{c11, c12}, {c21, c22}})
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Round matrix values
|
||
|
|
local function matrix_round(m, digits)
|
||
|
|
local result = {}
|
||
|
|
local mult = 10^digits
|
||
|
|
|
||
|
|
for i = 1, #m do
|
||
|
|
result[i] = {}
|
||
|
|
for j = 1, #m[1] do
|
||
|
|
if digits then
|
||
|
|
result[i][j] = math.floor(m[i][j] * mult + 0.5) / mult
|
||
|
|
else
|
||
|
|
result[i][j] = math.floor(m[i][j] + 0.5)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return result
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Print matrix
|
||
|
|
local function print_matrix(name, m)
|
||
|
|
print(name .. " = {")
|
||
|
|
for i = 1, #m do
|
||
|
|
io.write(" {")
|
||
|
|
for j = 1, #m[1] do
|
||
|
|
io.write(m[i][j])
|
||
|
|
if j < #m[1] then
|
||
|
|
io.write(", ")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
print("}")
|
||
|
|
end
|
||
|
|
print("}")
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Examples
|
||
|
|
local function run_examples()
|
||
|
|
local a = {
|
||
|
|
{1, 2},
|
||
|
|
{3, 4}
|
||
|
|
}
|
||
|
|
|
||
|
|
local b = {
|
||
|
|
{5, 6},
|
||
|
|
{7, 8}
|
||
|
|
}
|
||
|
|
|
||
|
|
local c = {
|
||
|
|
{1, 1, 1, 1},
|
||
|
|
{2, 4, 8, 16},
|
||
|
|
{3, 9, 27, 81},
|
||
|
|
{4, 16, 64, 256}
|
||
|
|
}
|
||
|
|
|
||
|
|
local d = {
|
||
|
|
{4, -3, 4/3, -1/4},
|
||
|
|
{-13/3, 19/4, -7/3, 11/24},
|
||
|
|
{3/2, -2, 7/6, -1/4},
|
||
|
|
{-1/6, 1/4, -1/6, 1/24}
|
||
|
|
}
|
||
|
|
|
||
|
|
local e = {
|
||
|
|
{1, 2, 3, 4},
|
||
|
|
{5, 6, 7, 8},
|
||
|
|
{9, 10, 11, 12},
|
||
|
|
{13, 14, 15, 16}
|
||
|
|
}
|
||
|
|
|
||
|
|
local f = {
|
||
|
|
{1, 0, 0, 0},
|
||
|
|
{0, 1, 0, 0},
|
||
|
|
{0, 0, 1, 0},
|
||
|
|
{0, 0, 0, 1}
|
||
|
|
}
|
||
|
|
|
||
|
|
print("Naive matrix multiplication:")
|
||
|
|
print_matrix(" a * b", matrix_multiply(a, b))
|
||
|
|
print_matrix(" c * d", matrix_round(matrix_multiply(c, d), 0))
|
||
|
|
print_matrix(" e * f", matrix_multiply(e, f))
|
||
|
|
|
||
|
|
print("\nStrassen's matrix multiplication:")
|
||
|
|
print_matrix(" a * b", strassen_multiply(a, b))
|
||
|
|
print_matrix(" c * d", matrix_round(strassen_multiply(c, d), 0))
|
||
|
|
print_matrix(" e * f", strassen_multiply(e, f))
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Run examples
|
||
|
|
run_examples()
|