UNPKG

algebrite

Version:

Computer Algebra System in Coffeescript

542 lines (427 loc) 10.9 kB
### eigen ===================================================================== Tags ---- scripting, JS, internal, treenode, general concept Parameters ---------- m General description ------------------- Compute eigenvalues and eigenvectors. Matrix m must be both numerical and symmetric. The eigenval function returns a matrix with the eigenvalues along the diagonal. The eigenvec function returns a matrix with the eigenvectors arranged as row vectors. The eigen function does not return anything but stores the eigenvalue matrix in D and the eigenvector matrix in Q. Input: stack[tos - 1] symmetric matrix Output: D diagnonal matrix Q eigenvector matrix D and Q have the property that A == dot(transpose(Q),D,Q) where A is the original matrix. The eigenvalues are on the diagonal of D. The eigenvectors are row vectors in Q. The eigenvalue relation: A X = lambda X can be checked as follows: lambda = D[1,1] X = Q[1] dot(A,X) - lambda X Example 1. Check the relation AX = lambda X where lambda is an eigenvalue and X is the associated eigenvector. Enter: A = hilbert(3) eigen(A) lambda = D[1,1] X = Q[1] dot(A,X) - lambda X Result: -1.16435e-14 -6.46705e-15 -4.55191e-15 Example 2: Check the relation A = QTDQ. Enter: A - dot(transpose(Q),D,Q) Result: 6.27365e-12 -1.58236e-11 1.81902e-11 -1.58236e-11 -1.95365e-11 2.56514e-12 1.81902e-11 2.56514e-12 1.32627e-11 ### #define D(i, j) yydd[EIG_N * (i) + (j)] #define Q(i, j) yyqq[EIG_N * (i) + (j)] EIG_N = 0 EIG_yydd = [] EIG_yyqq = [] Eval_eigen = -> if (EIG_check_arg() == 0) stop("eigen: argument is not a square matrix") eigen(EIGEN) p1 = usr_symbol("D") set_binding(p1, p2) p1 = usr_symbol("Q") set_binding(p1, p3) push(symbol(NIL)) ### eigenval ===================================================================== Tags ---- scripting, JS, internal, treenode, general concept Parameters ---------- m General description ------------------- Compute eigenvalues of m. See "eigen" for more info. ### Eval_eigenval = -> if (EIG_check_arg() == 0) push_symbol(EIGENVAL) push(p1) list(2) return eigen(EIGENVAL) push(p2) ### eigenvec ===================================================================== Tags ---- scripting, JS, internal, treenode, general concept Parameters ---------- m General description ------------------- Compute eigenvectors of m. See "eigen" for more info. ### Eval_eigenvec = -> if (EIG_check_arg() == 0) push_symbol(EIGENVEC) push(p1) list(2) return eigen(EIGENVEC) push(p3) EIG_check_arg = -> i = 0 j = 0 push(cadr(p1)) Eval() yyfloat() Eval() p1 = pop() if (!istensor(p1)) return 0 if (p1.tensor.ndim != 2 || p1.tensor.dim[0] != p1.tensor.dim[1]) stop("eigen: argument is not a square matrix") EIG_N = p1.tensor.dim[0] for i in [0...EIG_N] for j in [0...EIG_N] if (!isdouble(p1.tensor.elem[EIG_N * i + j])) stop("eigen: matrix is not numerical") for i in [0...(EIG_N-1)] for j in [(i+1)...EIG_N] if (Math.abs(p1.tensor.elem[EIG_N * i + j].d - p1.tensor.elem[EIG_N * j + i].d) > 1e-10) stop("eigen: matrix is not symmetrical") return 1 #----------------------------------------------------------------------------- # # Input: p1 matrix # # Output: p2 eigenvalues # # p3 eigenvectors # #----------------------------------------------------------------------------- eigen = (op) -> i = 0 j = 0 # malloc working vars #EIG_yydd = (double *) malloc(n * n * sizeof (double)) for i in [0...(EIG_N*EIG_N)] EIG_yydd[i] = 0.0 #if (EIG_yydd == NULL) # stop("malloc failure") #EIG_yyqq = (double *) malloc(n * n * sizeof (double)) for i in [0...(EIG_N*EIG_N)] EIG_yyqq[i] = 0.0 #if (EIG_yyqq == NULL) # stop("malloc failure") # initialize D for i in [0...EIG_N] EIG_yydd[EIG_N * (i) + (i)] = p1.tensor.elem[EIG_N * i + i].d for j in [(i+1)...EIG_N] EIG_yydd[EIG_N * (i) + (j)] = p1.tensor.elem[EIG_N * i + j].d EIG_yydd[EIG_N * (j) + (i)] = p1.tensor.elem[EIG_N * i + j].d # initialize Q for i in [0...EIG_N] EIG_yyqq[EIG_N * (i) + (i)] = 1.0 for j in [(i+1)...EIG_N] EIG_yyqq[EIG_N * (i) + (j)] = 0.0 EIG_yyqq[EIG_N * (j) + (i)] = 0.0 # step up to 100 times for i in [0...100] if (step() == 0) break if (i == 100) printstr("\nnote: eigen did not converge\n") # p2 = D if (op == EIGEN || op == EIGENVAL) push(p1) copy_tensor() p2 = pop() for i in [0...EIG_N] for j in [0...EIG_N] push_double(EIG_yydd[EIG_N * (i) + (j)]) p2.tensor.elem[EIG_N * i + j] = pop() # p3 = Q if (op == EIGEN || op == EIGENVEC) push(p1) copy_tensor() p3 = pop() for i in [0...EIG_N] for j in [0...EIG_N] push_double(EIG_yyqq[EIG_N * (i) + (j)]) p3.tensor.elem[EIG_N * i + j] = pop() # free working vars #----------------------------------------------------------------------------- # # Example: p = 1, q = 3 # # c 0 s 0 # # 0 1 0 0 # G = # -s 0 c 0 # # 0 0 0 1 # # The effect of multiplying G times A is... # # row 1 of A = c (row 1 of A ) + s (row 3 of A ) # n+1 n n # # row 3 of A = c (row 3 of A ) - s (row 1 of A ) # n+1 n n # # In terms of components the overall effect is... # # row 1 = c row 1 + s row 3 # # A[1,1] = c A[1,1] + s A[3,1] # # A[1,2] = c A[1,2] + s A[3,2] # # A[1,3] = c A[1,3] + s A[3,3] # # A[1,4] = c A[1,4] + s A[3,4] # # row 3 = c row 3 - s row 1 # # A[3,1] = c A[3,1] - s A[1,1] # # A[3,2] = c A[3,2] - s A[1,2] # # A[3,3] = c A[3,3] - s A[1,3] # # A[3,4] = c A[3,4] - s A[1,4] # # T # The effect of multiplying A times G is... # # col 1 of A = c (col 1 of A ) + s (col 3 of A ) # n+1 n n # # col 3 of A = c (col 3 of A ) - s (col 1 of A ) # n+1 n n # # In terms of components the overall effect is... # # col 1 = c col 1 + s col 3 # # A[1,1] = c A[1,1] + s A[1,3] # # A[2,1] = c A[2,1] + s A[2,3] # # A[3,1] = c A[3,1] + s A[3,3] # # A[4,1] = c A[4,1] + s A[4,3] # # col 3 = c col 3 - s col 1 # # A[1,3] = c A[1,3] - s A[1,1] # # A[2,3] = c A[2,3] - s A[2,1] # # A[3,3] = c A[3,3] - s A[3,1] # # A[4,3] = c A[4,3] - s A[4,1] # # What we want to do is just compute the upper triangle of A since we # know the lower triangle is identical. # # In other words, we just want to update components A[i,j] where i < j. # #----------------------------------------------------------------------------- # # Example: p = 2, q = 5 # # p q # # j=1 j=2 j=3 j=4 j=5 j=6 # # i=1 . A[1,2] . . A[1,5] . # # p i=2 A[2,1] A[2,2] A[2,3] A[2,4] A[2,5] A[2,6] # # i=3 . A[3,2] . . A[3,5] . # # i=4 . A[4,2] . . A[4,5] . # # q i=5 A[5,1] A[5,2] A[5,3] A[5,4] A[5,5] A[5,6] # # i=6 . A[6,2] . . A[6,5] . # #----------------------------------------------------------------------------- # # This is what B = GA does: # # row 2 = c row 2 + s row 5 # # B[2,1] = c * A[2,1] + s * A[5,1] # B[2,2] = c * A[2,2] + s * A[5,2] # B[2,3] = c * A[2,3] + s * A[5,3] # B[2,4] = c * A[2,4] + s * A[5,4] # B[2,5] = c * A[2,5] + s * A[5,5] # B[2,6] = c * A[2,6] + s * A[5,6] # # row 5 = c row 5 - s row 2 # # B[5,1] = c * A[5,1] + s * A[2,1] # B[5,2] = c * A[5,2] + s * A[2,2] # B[5,3] = c * A[5,3] + s * A[2,3] # B[5,4] = c * A[5,4] + s * A[2,4] # B[5,5] = c * A[5,5] + s * A[2,5] # B[5,6] = c * A[5,6] + s * A[2,6] # # T # This is what BG does: # # col 2 = c col 2 + s col 5 # # B[1,2] = c * A[1,2] + s * A[1,5] # B[2,2] = c * A[2,2] + s * A[2,5] # B[3,2] = c * A[3,2] + s * A[3,5] # B[4,2] = c * A[4,2] + s * A[4,5] # B[5,2] = c * A[5,2] + s * A[5,5] # B[6,2] = c * A[6,2] + s * A[6,5] # # col 5 = c col 5 - s col 2 # # B[1,5] = c * A[1,5] - s * A[1,2] # B[2,5] = c * A[2,5] - s * A[2,2] # B[3,5] = c * A[3,5] - s * A[3,2] # B[4,5] = c * A[4,5] - s * A[4,2] # B[5,5] = c * A[5,5] - s * A[5,2] # B[6,5] = c * A[6,5] - s * A[6,2] # #----------------------------------------------------------------------------- # # Step 1: Just do upper triangle (i < j), B[2,5] = 0 # # B[1,2] = c * A[1,2] + s * A[1,5] # # B[2,3] = c * A[2,3] + s * A[5,3] # B[2,4] = c * A[2,4] + s * A[5,4] # B[2,6] = c * A[2,6] + s * A[5,6] # # B[1,5] = c * A[1,5] - s * A[1,2] # B[3,5] = c * A[3,5] - s * A[3,2] # B[4,5] = c * A[4,5] - s * A[4,2] # # B[5,6] = c * A[5,6] + s * A[2,6] # #----------------------------------------------------------------------------- # # Step 2: Transpose where i > j since A[i,j] == A[j,i] # # B[1,2] = c * A[1,2] + s * A[1,5] # # B[2,3] = c * A[2,3] + s * A[3,5] # B[2,4] = c * A[2,4] + s * A[4,5] # B[2,6] = c * A[2,6] + s * A[5,6] # # B[1,5] = c * A[1,5] - s * A[1,2] # B[3,5] = c * A[3,5] - s * A[2,3] # B[4,5] = c * A[4,5] - s * A[2,4] # # B[5,6] = c * A[5,6] + s * A[2,6] # #----------------------------------------------------------------------------- # # Step 3: Same as above except reorder # # k < p (k = 1) # # A[1,2] = c * A[1,2] + s * A[1,5] # A[1,5] = c * A[1,5] - s * A[1,2] # # p < k < q (k = 3..4) # # A[2,3] = c * A[2,3] + s * A[3,5] # A[3,5] = c * A[3,5] - s * A[2,3] # # A[2,4] = c * A[2,4] + s * A[4,5] # A[4,5] = c * A[4,5] - s * A[2,4] # # q < k (k = 6) # # A[2,6] = c * A[2,6] + s * A[5,6] # A[5,6] = c * A[5,6] - s * A[2,6] # #----------------------------------------------------------------------------- step = -> i = 0 j = 0 count = 0 # for each upper triangle "off-diagonal" component do step2 for i in [0...(EIG_N-1)] for j in [(i+1)...EIG_N] if (EIG_yydd[EIG_N * (i) + (j)] != 0.0) step2(i, j) count++ return count step2 = (p,q) -> k = 0 t = 0.0 theta = 0.0 c = 0.0 cc = 0.0 s = 0.0 ss = 0.0 # compute c and s # from Numerical Recipes (except they have a_qq - a_pp) theta = 0.5 * (EIG_yydd[EIG_N * (p) + (p)] - EIG_yydd[EIG_N * (q) + (q)]) / EIG_yydd[EIG_N * (p) + (q)] t = 1.0 / (Math.abs(theta) + Math.sqrt(theta * theta + 1.0)) if (theta < 0.0) t = -t c = 1.0 / Math.sqrt(t * t + 1.0) s = t * c # D = GD # which means "add rows" for k in [0...EIG_N] cc = EIG_yydd[EIG_N * (p) + (k)] ss = EIG_yydd[EIG_N * (q) + (k)] EIG_yydd[EIG_N * (p) + (k)] = c * cc + s * ss EIG_yydd[EIG_N * (q) + (k)] = c * ss - s * cc # D = D transpose(G) # which means "add columns" for k in [0...EIG_N] cc = EIG_yydd[EIG_N * (k) + (p)] ss = EIG_yydd[EIG_N * (k) + (q)] EIG_yydd[EIG_N * (k) + (p)] = c * cc + s * ss EIG_yydd[EIG_N * (k) + (q)] = c * ss - s * cc # Q = GQ # which means "add rows" for k in [0...EIG_N] cc = EIG_yyqq[EIG_N * (p) + (k)] ss = EIG_yyqq[EIG_N * (q) + (k)] EIG_yyqq[EIG_N * (p) + (k)] = c * cc + s * ss EIG_yyqq[EIG_N * (q) + (k)] = c * ss - s * cc EIG_yydd[EIG_N * (p) + (q)] = 0.0 EIG_yydd[EIG_N * (q) + (p)] = 0.0