algebrite
Version:
Computer Algebra System in Coffeescript
258 lines (195 loc) • 4.22 kB
text/coffeescript
#-----------------------------------------------------------------------------
#
# Input: Matrix on stack (must have two dimensions but
# it can be non-numerical)
#
# Output: Inverse on stack
#
# Example:
#
# > inv(((1,2),(3,4))
# ((-2,1),(3/2,-1/2))
#
# > inv(((a,b),(c,d))
# ((d / (a d - b c),-b / (a d - b c)),(-c / (a d - b c),a / (a d - b c)))
#
# Note:
#
# THIS IS DIFFERENT FROM INVERSE OF AN EXPRESSION (inv)
# Uses Gaussian elimination for numerical matrices.
#
#-----------------------------------------------------------------------------
INV_check_arg = ->
if (!istensor(p1))
return 0
else if (p1.tensor.ndim != 2)
return 0
else if (p1.tensor.dim[0] != p1.tensor.dim[1])
return 0
else
return 1
inv = ->
i = 0
n = 0
#U **a
save()
p1 = pop()
# an inv just goes away when
# applied to another inv
if (isinv(p1))
push car(cdr(p1))
restore()
return
# inverse goes away in case
# of identity matrix
if isidentitymatrix(p1)
push p1
restore()
return
# distribute the inverse of a dot
# if in expanding mode
# note that the distribution happens
# in reverse.
# The dot operator is not
# commutative, so, it matters.
if (expanding && isinnerordot(p1))
p1 = cdr(p1)
accumulator = []
while (iscons(p1))
accumulator.push car(p1)
p1 = cdr(p1)
for eachEntry in [accumulator.length-1..0]
push(accumulator[eachEntry])
inv()
if eachEntry != accumulator.length-1
inner()
restore()
return
if (INV_check_arg() == 0)
push_symbol(INV)
push(p1)
list(2)
restore()
return
if isnumerictensor p1
yyinvg()
else
push(p1)
adj()
push(p1)
det()
p2 = pop()
if (iszero(p2))
stop("inverse of singular matrix")
push(p2)
divide()
restore()
invg = ->
save()
p1 = pop()
if (INV_check_arg() == 0)
push_symbol(INVG)
push(p1)
list(2)
restore()
return
yyinvg()
restore()
# inverse using gaussian elimination
yyinvg = ->
h = 0
i = 0
j = 0
n = 0
n = p1.tensor.dim[0]
h = tos
for i in [0...n]
for j in [0...n]
if (i == j)
push(one)
else
push(zero)
for i in [0...(n * n)]
push(p1.tensor.elem[i])
INV_decomp(n)
p1 = alloc_tensor(n * n)
p1.tensor.ndim = 2
p1.tensor.dim[0] = n
p1.tensor.dim[1] = n
for i in [0...(n * n)]
p1.tensor.elem[i] = stack[h + i]
moveTos tos - 2 * n * n
push(p1)
#-----------------------------------------------------------------------------
#
# Input: n * n unit matrix on stack
#
# n * n operand on stack
#
# Output: n * n inverse matrix on stack
#
# n * n garbage on stack
#
# p2 mangled
#
#-----------------------------------------------------------------------------
#define A(i, j) stack[a + n * (i) + (j)]
#define U(i, j) stack[u + n * (i) + (j)]
INV_decomp = (n) ->
a = 0
d = 0
i = 0
j = 0
u = 0
a = tos - n * n
u = a - n * n
for d in [0...n]
# diagonal element zero?
if (equal( (stack[a + n * (d) + (d)]) , zero))
# find a new row
for i in [(d + 1)...n]
if (!equal( (stack[a + n * (i) + (d)]) , zero))
break
if (i == n)
stop("inverse of singular matrix")
# exchange rows
for j in [0...n]
p2 = stack[a + n * (d) + (j)]
stack[a + n * (d) + (j)] = stack[a + n * (i) + (j)]
stack[a + n * (i) + (j)] = p2
p2 = stack[u + n * (d) + (j)]
stack[u + n * (d) + (j)] = stack[u + n * (i) + (j)]
stack[u + n * (i) + (j)] = p2
# multiply the pivot row by 1 / pivot
p2 = stack[a + n * (d) + (d)]
for j in [0...n]
if (j > d)
push(stack[a + n * (d) + (j)])
push(p2)
divide()
stack[a + n * (d) + (j)] = pop()
push(stack[u + n * (d) + (j)])
push(p2)
divide()
stack[u + n * (d) + (j)] = pop()
# clear out the column above and below the pivot
for i in [0...n]
if (i == d)
continue
# multiplier
p2 = stack[a + n * (i) + (d)]
# add pivot row to i-th row
for j in [0...n]
if (j > d)
push(stack[a + n * (i) + (j)])
push(stack[a + n * (d) + (j)])
push(p2)
multiply()
subtract()
stack[a + n * (i) + (j)] = pop()
push(stack[u + n * (i) + (j)])
push(stack[u + n * (d) + (j)])
push(p2)
multiply()
subtract()
stack[u + n * (i) + (j)] = pop()