algebrite
Version:
Computer Algebra System in Coffeescript
263 lines (189 loc) • 5.32 kB
text/coffeescript
# find the roots of a polynomial numerically
NROOTS_YMAX = 101
NROOTS_DELTA = 1.0e-6
NROOTS_EPSILON = 1.0e-9
NROOTS_ABS = (z) ->
return Math.sqrt((z).r * (z).r + (z).i * (z).i)
# random between -2 and 2
theRandom = 0.0
NROOTS_RANDOM = ->
#theRandom += 0.2
#return theRandom
return 4.0 * Math.random() - 2.0
class numericRootOfPolynomial
r: 0.0
i: 0.0
nroots_a = new numericRootOfPolynomial()
nroots_b = new numericRootOfPolynomial()
nroots_x = new numericRootOfPolynomial()
nroots_y = new numericRootOfPolynomial()
nroots_fa = new numericRootOfPolynomial()
nroots_fb = new numericRootOfPolynomial()
nroots_dx = new numericRootOfPolynomial()
nroots_df = new numericRootOfPolynomial()
nroots_c = []
for initNRoots in [0...NROOTS_YMAX]
nroots_c[initNRoots] = new numericRootOfPolynomial()
Eval_nroots = ->
h = 0
i = 0
k = 0
n = 0
push(cadr(p1))
Eval()
push(caddr(p1))
Eval()
p2 = pop()
if (p2 == symbol(NIL))
guess()
else
push(p2)
p2 = pop()
p1 = pop()
if (!ispoly(p1, p2))
stop("nroots: polynomial?")
# mark the stack
h = tos
# get the coefficients
push(p1)
push(p2)
n = coeff()
if (n > NROOTS_YMAX)
stop("nroots: degree?")
# convert the coefficients to real and imaginary doubles
for i in [0...n]
push(stack[h + i])
real()
yyfloat()
Eval()
p1 = pop()
push(stack[h + i])
imag()
yyfloat()
Eval()
p2 = pop()
if (!isdouble(p1) || !isdouble(p2))
stop("nroots: coefficients?")
nroots_c[i].r = p1.d
nroots_c[i].i = p2.d
# pop the coefficients
moveTos h
# n is the number of coefficients, n = deg(p) + 1
monic(n)
for k in [n...1] by -1
findroot(k)
if (Math.abs(nroots_a.r) < NROOTS_DELTA)
nroots_a.r = 0.0
if (Math.abs(nroots_a.i) < NROOTS_DELTA)
nroots_a.i = 0.0
push_double(nroots_a.r)
push_double(nroots_a.i)
push(imaginaryunit)
multiply()
add()
NROOTS_divpoly(k)
# now make n equal to the number of roots
n = tos - h
if (n > 1)
sort_stack(n)
p1 = alloc_tensor(n)
p1.tensor.ndim = 1
p1.tensor.dim[0] = n
for i in [0...n]
p1.tensor.elem[i] = stack[h + i]
moveTos h
push(p1)
# divide the polynomial by its leading coefficient
monic = (n) ->
k = 0
t = 0.0
nroots_y.r = nroots_c[n - 1].r
nroots_y.i = nroots_c[n - 1].i
t = nroots_y.r * nroots_y.r + nroots_y.i * nroots_y.i
for k in [0...(n - 1)]
nroots_c[k].r = (nroots_c[k].r * nroots_y.r + nroots_c[k].i * nroots_y.i) / t
nroots_c[k].i = (nroots_c[k].i * nroots_y.r - nroots_c[k].r * nroots_y.i) / t
nroots_c[n - 1].r = 1.0
nroots_c[n - 1].i = 0.0
# uses the secant method
findroot = (n) ->
j = 0
k = 0
t = 0.0
if (NROOTS_ABS(nroots_c[0]) < NROOTS_DELTA)
nroots_a.r = 0.0
nroots_a.i = 0.0
return
for j in [0...100]
nroots_a.r = NROOTS_RANDOM()
nroots_a.i = NROOTS_RANDOM()
compute_fa(n)
nroots_b.r = nroots_a.r
nroots_b.i = nroots_a.i
nroots_fb.r = nroots_fa.r
nroots_fb.i = nroots_fa.i
nroots_a.r = NROOTS_RANDOM()
nroots_a.i = NROOTS_RANDOM()
for k in [0...1000]
compute_fa(n)
nrabs = NROOTS_ABS(nroots_fa)
if DEBUG then console.log "nrabs: " + nrabs
if ( nrabs < NROOTS_EPSILON)
return
if (NROOTS_ABS(nroots_fa) < NROOTS_ABS(nroots_fb))
nroots_x.r = nroots_a.r
nroots_x.i = nroots_a.i
nroots_a.r = nroots_b.r
nroots_a.i = nroots_b.i
nroots_b.r = nroots_x.r
nroots_b.i = nroots_x.i
nroots_x.r = nroots_fa.r
nroots_x.i = nroots_fa.i
nroots_fa.r = nroots_fb.r
nroots_fa.i = nroots_fb.i
nroots_fb.r = nroots_x.r
nroots_fb.i = nroots_x.i
# dx = nroots_b - nroots_a
nroots_dx.r = nroots_b.r - nroots_a.r
nroots_dx.i = nroots_b.i - nroots_a.i
# df = fb - fa
nroots_df.r = nroots_fb.r - nroots_fa.r
nroots_df.i = nroots_fb.i - nroots_fa.i
# y = dx / df
t = nroots_df.r * nroots_df.r + nroots_df.i * nroots_df.i
if (t == 0.0)
break
nroots_y.r = (nroots_dx.r * nroots_df.r + nroots_dx.i * nroots_df.i) / t
nroots_y.i = (nroots_dx.i * nroots_df.r - nroots_dx.r * nroots_df.i) / t
# a = b - y * fb
nroots_a.r = nroots_b.r - (nroots_y.r * nroots_fb.r - nroots_y.i * nroots_fb.i)
nroots_a.i = nroots_b.i - (nroots_y.r * nroots_fb.i + nroots_y.i * nroots_fb.r)
stop("nroots: convergence error")
compute_fa = (n) ->
k = 0
t = 0.0
# x = a
nroots_x.r = nroots_a.r
nroots_x.i = nroots_a.i
# fa = c0 + c1 * x
nroots_fa.r = nroots_c[0].r + nroots_c[1].r * nroots_x.r - nroots_c[1].i * nroots_x.i
nroots_fa.i = nroots_c[0].i + nroots_c[1].r * nroots_x.i + nroots_c[1].i * nroots_x.r
for k in [2...n]
# x = a * x
t = nroots_a.r * nroots_x.r - nroots_a.i * nroots_x.i
nroots_x.i = nroots_a.r * nroots_x.i + nroots_a.i * nroots_x.r
nroots_x.r = t
# fa += c[k] * x
nroots_fa.r += nroots_c[k].r * nroots_x.r - nroots_c[k].i * nroots_x.i
nroots_fa.i += nroots_c[k].r * nroots_x.i + nroots_c[k].i * nroots_x.r
# divide the polynomial by x - a
NROOTS_divpoly = (n) ->
k = 0
for k in [(n - 1)...0]
nroots_c[k - 1].r += nroots_c[k].r * nroots_a.r - nroots_c[k].i * nroots_a.i
nroots_c[k - 1].i += nroots_c[k].i * nroots_a.r + nroots_c[k].r * nroots_a.i
if (NROOTS_ABS(nroots_c[0]) > NROOTS_DELTA)
stop("nroots: residual error")
for k in [0...(n - 1)]
nroots_c[k].r = nroots_c[k + 1].r
nroots_c[k].i = nroots_c[k + 1].i