Files
mmath/matrix.nim
2018-12-26 08:14:25 +00:00

330 lines
10 KiB
Nim

from sequtils import newSeqWith, map
from utils import `...`, `-->`
from vector import Vector, newVector, createVector
from options import Option, none
import future
from random import randomize, random
type
Matrix*[T] = object
rows, columns : int
data : seq[T]
SMatrix[W, H: static[int], T] =
array[1..W, array[1..H, T]]
Pivot* = ref object
data* : Vector[int]
permutations* : int
SingularMatrixError* = object of ValueError
SizeError* = object of ValueError
MatrixRef[T] = ref Matrix[T]
proc `[]`(p : Pivot, index : int) : int = p.data[index]
proc `[]=`(p : var Pivot, index : int, value : int) = p.data[index] = value
proc len(p : Pivot) : int = p.data.len
proc newPivot[T](size : int) : Pivot =
result = new (Pivot)
result = Pivot(data: newVector[int](size), permutations:0)
for i in 0...size:
result[i] = i
type AbtractMAtrix = Matrix or SMAtrix
iterator iter_walter[T](m: Matrix[T]) : (int,int, T) {.closure.} =
for i in 0...m.rows:
for j in 0...m.columns:
yield (i, j, m[i,j])
iterator items*[T](m: Matrix[T]): (int,int, T) =
for i in 0...m.rows:
for j in 0...m.columns:
yield (i, j, m[i,j])
proc size*[T](m : Matrix[T]) : (int, int) = (m.rows, m.columns)
proc newMatrix*[T](rows, columns : int, init : T = 0) : Matrix[T] =
result = Matrix[T](rows: rows, columns:columns, data: newSeq[T](rows * columns))
for i,j,_ in items(result):
result[i,j] = init
proc newMatrix*[T](rows, columns : int, values : openarray[T]) : Matrix[T] =
result = Matrix[T](rows: rows, columns:columns, data: newSeq[T](rows * columns))
for i,j,_ in items(result):
result[i,j] = values[i * columns + j]
proc identity*[T](sz : int) : Matrix[T] =
result = newMatrix[T](sz,sz,0)
for i in 0...sz:
result[i,i] = 1
proc `[]`*[T](m : Matrix[T], r,c :int) : T =
m.data[r*m.columns + c]
proc `[]`*[T](m : var Matrix[T], r,c :int) : var T =
m.data[r*m.columns + c]
proc `[]=`*[T](m : var Matrix[T], r,c :int, newValue : T) =
m.data[r*m.columns + c] = newValue
# proc `[]=`*[T](m : var Matrix[T], r,c :int, newValue : T) =
# m.data[r*m.columns + c] = newValue
proc `$`*[T](m : Matrix[T]) : string =
result = "["
for i,j,v in items(m):
if j == 0:
if i > 0: result &= " "
result &= "["
result &= $v
if j == (m.columns - 1):
result &= "]"
if i != (m.rows - 1):
result &= ",\n"
else:
result &= "]\n"
else: result &= ", "
proc `*`*[T](m1 : Matrix[T], m2 : Matrix[T]) : Matrix[T] =
result = newMatrix[T](m1.rows, m2.columns, 0)
for i in 0...result.rows:
for j in 0...result.columns:
for k in 0...m1.columns:
result[i, j] = result[i, j] + m1[i, k] * m2[k, j]
proc `*`*[T](m1 : Matrix[T], v2 : Vector[T]) : Vector[T] =
result = newVector[T](m1.rows, 0)
for i in 0...m1.rows:
for j in 0...m1.columns:
result[i] = result[i] + m1[i, j] * v2[j]
proc `+`*[T](m1 : Matrix[T], m2 : Matrix[T]) : Matrix[T] =
result = newMatrix[T](m1.rows, m1.columns)
for i in 0...m1.rows:
for j in 0...m1.columns:
result[i,j] = m1[i,j] + m2[i,j]
proc `+`*[T](m1 : Matrix[T], v : T) : Matrix[T] =
result = newMatrix[T](m1.rows, m1.columns)
for i in 0...m1.rows:
for j in 0...m1.columns:
result[i,j] = m1[i,j] + v
proc `-`*[T](m1 : Matrix[T], m2 : Matrix[T]) : Matrix[T] =
result = newMatrix[T](m1.rows, m2.columns)
for i in 0...m1.rows:
for j in 0...m1.columns:
result[i,j] = m1[i,j] - m2[i,j]
proc `-`*[T](m1 : Matrix[T], v : T) : Matrix[T] =
result = newMatrix[T](m1.rows, m1.columns)
for i in 0...m1.rows:
for j in 0...m1.columns:
result[i,j] = m1[i,j] - v
proc `+=`*[T](m1 : var Matrix[T], m2 : Matrix[T]) =
for i in 0...m1.rows:
for j in 0...m1.columns:
m1[i,j] += m2[i,j]
proc `+=`*[T](m1 : var Matrix[T], v : T) : Matrix[T] =
for i in 0...m1.rows:
for j in 0...m1.columns:
m1[i,j] += v
proc `-=`*[T](m1 : var Matrix[T], v : T) : Matrix[T] =
for i in 0...m1.rows:
for j in 0...m1.columns:
m1[i,j] -= v
proc `-=`*[T](m1 : var Matrix[T], m2 : Matrix[T]) =
for i in 0...m1.rows:
for j in 0...m1.columns:
m1[i,j] -= m2[i,j]
proc `-`*[T](m : Matrix[T]) : Matrix[T] =
result = newMatrix[T](m.rows, m.columns)
for i,j,v in m:
result[i,j] = -v
proc `==`*[T](m1 : Matrix[T], m2 : Matrix[T]) : bool =
if m1.size() != m2.size():
return false
for i in 0...m1.rows:
for j in 0...m1.columns:
if m1[i,j] != m2[i,j]:
return false
return true
proc clone*[T](m : Matrix[T]) : Matrix[T] = newMatrix[T](m.rows, m.columns, m.data)
proc transpose*[T](m : Matrix[T]) : Matrix[T] =
result = newMatrix[T](m.rows, m.columns)
for i, j, v in m:
result[j, i] = v
proc det*[T](m : Matrix[T]) : T =
var clone = m.clone()
clone.gauss_jordan_low()
result = 1
for i in 0...clone.rows:
result *= clone[i, i]
proc swap_rows[T](m : var Matrix[T], id1 : int, id2 : int, pivot : Pivot=nil, other : MatrixRef[T]=nil) =
for i in 0...m.columns:
let tmp = m[id1, i]
m[id1, i] = m[id2, i]
m[id2, i] = tmp
if other != nil:
other[].swap_rows(id1, id2)
if pivot != nil:
var pv = pivot
let tmp = pv[id1]
pv[id1] = pv[id2]
pv[id2] = tmp
pv.permutations = pv.permutations + 1
proc add_row[T](m : var Matrix[T], sourceIndex : int, destIndex : int, factor : T, other : MatrixRef[T]=nil) =
for i in 0...m.columns:
m[destIndex, i] = m[destIndex, i] + m[sourceIndex, i] * factor
if other != nil:
other[].add_row(sourceIndex, destIndex, factor)
proc gauss_jordan_low*[T](m : var Matrix[T], other : MatrixRef[T]=nil) =
var pivot = newPivot[T](m.rows)
for i in 0...m.rows:
if m[i, i] == 0:
for j in (i + 1)...m.columns:
if m[j, i] != 0:
m.swap_rows(i, j, pivot, other)
break
for j in (i + 1)...m.rows:
if m[i, i] != 0:
let factor = -m[j, i] / m[i, i]
m.add_row(i, j, factor, other)
proc gauss_jordan_high*[T](m : var Matrix[T], other : MatrixRef[T]=nil) =
var pivot = newPivot[T](m.rows)
for i in m.rows-->0:
if m[i, i] == 0:
for j in i-->0:
if m[j, i] != 0:
m.swap_rows(i, j, pivot, other)
break
for j in i-->0:
if m[i, i] != 0:
let factor = -m[j, i] / m[i, i]
m.add_row(i, j, factor, other)
proc invert*[T](m : Matrix[T]) : Matrix[T] =
var tmp = m.clone()
var res = new(MatrixRef[T])
res[] = identity[T](tmp.rows)
tmp.gauss_jordan_low(res)
tmp.gauss_jordan_high(res)
for i in 0...res.rows:
let f = tmp[i, i]
for j in 0...res.columns:
res[][i, j] /= f
res[]
proc triu*[T](m : Matrix[T], diag_replace=nil) : Matrix[T] =
result = Matrix(m.rows, m.columns, 0)
for i in range(m.rows):
for j in range(i, m.columns):
if diag_replace and i == j:
result[i, j] = diag_replace
else:
result[i, j] = m[i, j]
proc tril*[T](m : Matrix[T], diag_replace=nil) : Matrix[T] =
result = Matrix(m.rows, m.columns, 0)
for i in range(m.rows):
for j in range(i + 1):
if diag_replace and i == j:
result[i, j] = diag_replace
else:
result[i, j] = m[i, j]
proc lu_row[T](m : var Matrix[T], i : int) =
if m[i, i] == 0:
raise newException(SingularMatrixError, "Matrix is singular")
for j in i...m.columns:
for k in 0...i:
m[i, j] = m[i, j] - m[i, k] * m[k, j]
for j in (i + 1)...m.columns:
for k in 0...i:
m[j, i] = m[j, i] - m[j, k] * m[k, i]
m[j, i] = m[j, i] / m[i, i]
proc lu_pivot[T](m : var Matrix[T], i : int, pivot : Pivot) =
var max = abs(m[i, i])
var max_index = i
for j in (i + 1)...m.rows:
if abs(m[j, i]) > max:
max = abs(m[i, j])
max_index = j
if max_index != i:
m.swap_rows(i, max_index, pivot)
proc lu*[T](m : var Matrix[T], pivoting=true) : Pivot =
var pivot = newPivot[T](m.rows)
if pivoting:
for i in 0...m.rows:
m.lu_pivot(i, pivot)
m.lu_row(i)
else:
for i in 0...m.rows:
m.lu_row(i)
return pivot
proc lu_solve*[T](m : Matrix[T], b : Vector[T], p : Pivot = nil) : Vector[T] =
var pivot = p
if pivot == nil:
pivot = new(Pivot)
pivot = newPivot[T](m.rows)
var x = newVector[T](m.rows)
for i in 0...m.rows:
x[i] = b[pivot[i]]
for k in 0...i:
x[i] = x[i] - m[i, k] * x[k]
for i in m.rows-->0:
for k in (i + 1)...m.rows:
x[i] = x[i] - m[i, k] * x[k]
x[i] = x[i] / m[i, i]
return x
proc lu_invert*[T](m : Matrix[T], pivot : Pivot=nil) : Matrix[T] =
if not pivot:
pivot = newPivot[T](m.rows)
result = newMatrix[T](m.rows, m.columns)
for i in 0...m.rows:
for j in 0...m.rows:
if pivot[j] == i:
result[j, i] = 1
else:
result[j, i] = 0
for k in range(j):
result[j, i] -= m[j, k] * result[k, i]
for j in m.rows-->0:
for k in range(j + 1, m.rows):
result[j, i] -= m[j, k] * result[k, i]
result[j, i] = result[j, i] / m[j, j]
proc lu_det*[T](m : Matrix[T], pivot : Pivot=nil) : T =
if not pivot:
pivot = newPivot[T](m.rows)
result = 1
for i in 0...m.rows:
result *= m[i, i]
if pivot.permutations mod 2 != 0:
result *= -1
proc from_pivot[T](pivot : Pivot): Matrix[T] =
result = Matrix(len(pivot), len(pivot))
for i in 0...pivot.len:
result[pivot[i], i] = 1
# result[j, i] = 1