matrix.e
- Posted by "Arthur Adamson (by way of Arthur Adamson Jan 17, 1997
- 1340 views
Test. -- -- -- -- --Matrix.e file by A. P. Adamson, Jan. 1997, for use with EUPHORIA language, --based in part on the matrix Ada programs in matrix directory of --Walnut Creek Ada CDRom and its predecessors. --Send error notes to me at euclid at horandata.net --Additional routines can also be added. If you have one you like, send it --to me or post it to the news group. --A modest amount of checking has been done but there may still be errors. --Use at your own risk. I make no claim for the correctness of the routines. -- --I define row vector as: sequence {1,2,3,...} and --I define column vector as: sequence {{1},{2},{3},...} -- --The following functions are implemented in the order shown. -- --global function makeRowVector(integer iIn) --global function makeColumnVector(integer iIn) --global function make2DMatrix(integer rows, integer cols) --global function make3DMatrix(integer rows, integer cols, integer layers) --global function transpose( sequence matIn) --global function dotRowRowVec (sequence seqIn) --global function dotRowColVec (sequence seqIn) --global function vectorsCrossProduct (sequence seqIn) --global function matrixProduct (sequence seqIn) --global function matrixTimesRowVector(sequence seqIn) --global function matrixTimesColumnVector(sequence seqIn) --global function rowVectorTimesMatrix(sequence seqIn) --global function columnVectorTimesMatrix(sequence seqIn) --global function matrixTrace(sequence matIn) --global function abs(atom numIn) --global function matrixPower(sequence seqIn) --global function rot2DVectorCW90 (sequence seqIn) --global function rot2DVectorCCW90 (sequence seqIn) --global function reverseXin2DVector(sequence seqIn) --global function reverseYin2DVector(sequence seqIn) --global function getValueACrossB(sequence seqIn) --global function getVectorLength(sequence seqIn) --global function getCosIncludedAngleBetweenAB(sequence seqIn) --global function crout(sequence seqIn, sequence vecIn) --global function simultaneousInvert(sequence seqIn, sequence vecIn) -- global function makeRowVector(integer iIn) return repeat(0,iIn) end function -- -- ******************************************************************-- -- global function makeColumnVector(integer iIn) return repeat(repeat(0,1),iIn) end function -- -- ******************************************************************-- -- global function make2DMatrix(integer rows, integer cols) return repeat(repeat(0,cols), rows) end function -- -- ******************************************************************-- -- global function make3DMatrix(integer rows, integer cols, integer layers) return repeat(repeat(repeat(0,cols), rows),layers) end function -- -- ******************************************************************-- -- global function transpose( sequence matIn) -- This function performs the tranpose of a vector or 2 dimensional -- matrix matIn. -- (A) {1,2,3,4} becomes a column vector {{1},{2},{3},{4}} -- (B) {{1},{2},{3},{4}} becomes a column vector {1,2,3,4} -- (C) {{1,2,3,4},{-1,3,-1,0}} becomes {{1,-1},{2,3},{3,-2},{4,0}} -- etc -- sequence matOut if atom(matIn[1]) then --case A matOut = make2DMatrix(length(matIn),1) for i = 1 to length(matIn) by 1 do matOut[i][1] = matIn[i] end for return matOut elsif length(matIn[1]) = 1 then --case B matOut = makeRowVector(length(matIn)) for i = 1 to length(matIn) by 1 do matOut[i] = matIn[i][1] end for return matOut else matOut = make2DMatrix(length(matIn[1]),length(matIn)) --case C for i = 1 to length(matIn) by 1 do for j = 1 to length(matIn[1]) by 1 do matOut[j][i] = matIn[i][j] end for end for end if return matOut end function -- -- ******************************************************************-- -- global function dotRowRowVec (sequence seqIn) --seqIn is of the form {{a, b, c}, {d, e, f}} but more than 3 --terms cam be used -- This function performs the inner (dot) product of two vectors A -- and B resulting in a floating point number. -- Comparability of dimensions is checked. -- atom flo flo = 0.0 if (length(seqIn[1]) = length(seqIn[2]) and atom(seqIn[1][1]) and atom(seqIn[2][1])) then for i = 1 to length(seqIn[1]) do flo = flo + seqIn[1][i] * seqIn[2][i] end for return flo else puts (1, "vector lengths unequal\n") return 0 end if end function -- -- ******************************************************************-- -- global function dotRowColVec (sequence seqIn) --seqIn is of the form {{a, b, c}, {{d}, {e}, {f}}} but more than 3 --terms cam be used -- -- This function performs the inner (dot) product of two vectors A, -- a row vwctor and B, a column vector resulting in a floating point -- number. -- Comparability of dimensions is checked. -- atom flo flo = 0.0 if (length(seqIn[1]) = length(seqIn[2]) and atom(seqIn[1][1]) and length(seqIn[2][1] = 1)) then for i = 1 to length(seqIn[1]) do flo = flo + seqIn[1][i] * seqIn[2][i][1] end for return flo else puts (1, "vector lengths unequal\n") return 0 end if end function -- -- ******************************************************************* -- global function vectorsCrossProduct (sequence seqIn) -- This function performs the cross product of two 3 element vectors -- resulting in a 3 element vector. -- Usage, v = vectorsCrossProduct(seqIn) where -- seqIn is of the form {{{number},{number},{number}}, -- {{number},{number},{number}}} -- Compatability of dimensions is checked. -- sequence sOut sOut = makeRowVector(3) if (length(seqIn[1]) = 3 and length(seqIn[2]) = 3 and atom(seqIn[1][1]) and atom(seqIn[2][1]) ) then sOut[1] = seqIn[1][2] * seqIn[2][3] - seqIn[1][3] * seqIn[2][2] sOut[2] = seqIn[1][3] * seqIn[2][1] - seqIn[1][1] * seqIn[2][3] sOut[3] = seqIn[1][1] * seqIn[2][2] - seqIn[1][2] * seqIn[2][1] -- end if return sOut end function -- ******************************************************************-- -- -- functions "+, -, *, /" to term by term add, subtract, multiply, divide -- 2 vectors (or 2 matricees, or a number and a matrix or vector) is -- inherent in Euphoria. C = A + B -- -- ******************************************************************-- -- global function matrixProduct (sequence seqIn) --seqIn is of the form seqIn[1] = mat1In with mat1In having m rows --and n columns -- seqIn[2] = mat2In with mat2In having n rows --and q columns, ie, the number of columns in the first matrix equals --the number of rows in the second matrix. --m, n, q must be > 1. --The output matrix will have m rows and q columns. -- Comparability of dimensions is checked. -- atom sum sequence vecOut sum = 0 if (length(seqIn[1][1]) != length(seqIn[2])) then puts(1,"Error in dimensions of input matricees") return 1=0 else vecOut = make2DMatrix(length(seqIn[1]),length(seqIn[2][2]) ) for i = 1 to length(seqIn[1]) by 1 do for j =1 to length(seqIn[2][1]) by 1 do sum = 0 for k = 1 to length(seqIn[1][1]) by 1 do sum = sum + (seqIn[1][i][k] * seqIn[2][k][j]) end for vecOut[i][j] = sum sum = 0 end for end for end if return vecOut end function -- -- ********************************************************************** -- global function matrixTimesRowVector(sequence seqIn) -- -- This function performs the multiplication of a matrix (seqIn[1]) -- and a row vector (seqIn[2] resulting in a row vector. -- seqIn format is: -- {{matIn},{vecIn}} where matIn is a 2 dimensional matrix and -- vecIn is a 1 dimensional vector of -- the same order and form {1,2,3...}. vecOut form is the same as vecIn. -- Comparability of dimensions is checked. -- ********************************************************************** -- sequence vecOut atom sum vecOut = makeRowVector(length(seqIn[2])) if length(seqIn[1][1]) != length(seqIn[2]) then return 1=0 else for i = 1 to length(seqIn[1]) do sum = 0.0 for k = 1 to length(seqIn[2]) do sum = sum + seqIn[1][i][k] * seqIn[2][k] vecOut[i] = sum end for end for end if return vecOut end function -- -- ********************************************************************** global function matrixTimesColumnVector(sequence seqIn) -- -- ********************************************************************** -- This function performs the multiplication of a matrix (seqIn[1]) -- and a column vector (seqIn[2] resulting in a column vector. -- seqIn format is: -- {{matIn},{vecIn}} where matIn is a 2 dimensional matrix and -- vecIn is a 1 dimensional vector of -- the same order and form {{1},{2},{3}...}. vecOut form is the -- same as vecIn. -- Comparability of dimensions is checked. -- ********************************************************************** -- sequence vecOut atom sum vecOut = makeRowVector(length(seqIn[2])) if length(seqIn[1][1]) != length(seqIn[2]) then return 1=0 else for i = 1 to length(seqIn[1]) do sum = 0.0 for k = 1 to length(seqIn[2]) do sum = sum + seqIn[1][i][k] * seqIn[2][k][1] vecOut[i] = sum end for end for end if return vecOut end function -- -- ********************************************************************** -- global function rowVectorTimesMatrix(sequence seqIn) -- -- ********************************************************************** -- This function performs the multiplication of a row vector -- (seqIn[1] and a matrix (seqIn[2]) resulting in a row vector. -- seqIn format is: {{vecIn},{matIn}} where matIn is a 2 dimensional -- matrix and vecIn is a 1 dimensional vector of form {1,2,3} and of -- the same order. vecOut form is the same as vecIn. -- Comparability of dimensions is checked. -- ********************************************************************** -- sequence vecOut atom sum -- vecOut = makeRowVector(length(seqIn[1])) -- if length(seqIn[1]) != length(seqIn[2][1]) then return 1=0 else for i = 1 to length(seqIn[2]) do sum = 0.0 for k = 1 to length(seqIn[1]) do sum = sum + seqIn[2][k][i] * seqIn[1][i] vecOut[i] = sum end for end for end if return vecOut end function --*************************************************************************** -- global function columnVectorTimesMatrix(sequence seqIn) -- -- ********************************************************************** -- This function performs the multiplication of a (column) vector -- (seqIn[1] and a matrix (seqIn[2]) resulting in a (column) vector. -- seqIn format is: {{vecIn},{matIn}} where matIn is a 2 dimensional -- matrix and vecIn is a 1 dimensional vector of the form {{1},{2},{3}} -- and of the same order. vecOut form is the same as vecIn. -- Comparability of dimensions is checked. -- ********************************************************************** -- sequence vecOut atom sum -- vecOut = makeColumnVector(length(seqIn[1])) -- if length(seqIn[1]) != length(seqIn[2][1]) then return 1=0 else for i = 1 to length(seqIn[2]) do sum = 0.0 for k = 1 to length(seqIn[1]) do sum = sum + seqIn[2][k][i] * seqIn[1][i][1] vecOut[i][1] = sum end for end for end if return vecOut end function --*************************************************************************** -- global function matrixTrace(sequence matIn) --matIn is a 2 dimensional square matrix, format {{1,2,3},{},...} --This function returns the "trace", ie the sum of the major diagonal terms -- atom sum sum = 0 for i = 1 to length(matIn[1]) by 1 do sum = sum + matIn[i][i] end for return sum end function -- --************************************************************************ -- global function abs(atom numIn) if numIn < 0 then return -numIn else return numIn end if end function -- --************************************************************************ -- global function matrixPower(sequence seqIn) sequence A, B, C, L, M integer rows, cols, P, I_PIVOT, J_PIVOT atom BIG_ENTRY, TEMP, EPSILON A = seqIn[1] rows = length(seqIn[1]) cols = length(seqIn[1][1]) P = seqIn[2] B = make2DMatrix(rows,rows) C = make2DMatrix(rows,rows) L = makeRowVector(rows) M = makeRowVector(rows) -- ******************************************************************* -- This function performs the square matrix operation of " matrix A -- raise to integer power P ". When P is negative , say P = -N , -- matrixPower(A, -N) = (inverse(A))**N , that is, the inverse of A -- raise to power N . In this case, matrix A must be non-singular. -- Exceptions will be raised if the matrix A is not a square matrix, -- or if matrix A is singular. -- The form of seqIn is {square matrix, power} where seqIn[1] is -- the input matrix and power is an integer, + or -. If power = -1, -- the returned matrix is the inverse of the matrix in. -- -- I tested the inversion operation for matrix size capability -- on a Pentium 120, 32MHZ, DOS6.22 and found: -- 80 X 80 OK with emm386 active OK, 100 NG -- 240 X 240 OK with emm386 inactive OK,85 seconds for inversion. -- Locked up for 300 X 300 -- -- Bob Craig tested on Pentium 150, WIN 95, 32MHZ and found: -- 500 X 500 worked OK, 707 seconds for the inversion step -- The size capability is no doubt due to config.sys details. -- -- ******************************************************************* -- if rows != cols then puts(1,"Input matrix is not square") return 1=0 end if -- if P = 0 then -- B = identity matrix for I = 1 to rows do for J = 1 to rows do if I != J then B[I][J] = 0.0 else B[I][J] = 1.0 end if end for end for return B end if -- B = A -- if P > 0 then --& B = A multiplied itself for P times for c = 1 to P-1 by 1 do B = matrixProduct({A, B}) end for return B end if -- -- P is negative, find inverse first -- initiate the row and column interchange information -- for K = 1 to rows do L[K] = K -- row interchage information M[K] = K -- column interchange information end for -- -- major loop for inverse -- for K = 1 to rows do -- & search for row and column index I_PIVOT, J_PIVOT -- & both in (K .. B'LAST(1) ) for maximum B(I,J) -- & in absolute value :BIG_ENTRY BIG_ENTRY = 0.0 -- check matrix singularity for I = K to rows do for J = K to rows do if abs(B[I][J]) > abs(BIG_ENTRY) then BIG_ENTRY = B[I][J] I_PIVOT = I J_PIVOT = J end if end for end for -- if K = 1 then if BIG_ENTRY = 0.0 then puts(1, "SINGULAR Matrix") return 1=0 else EPSILON = rows * (abs(BIG_ENTRY)) * 0.000001 end if else if abs(BIG_ENTRY) < EPSILON then puts(1, "SINGULAR Matrix") end if end if -- -- interchange row and column -- --& interchange K-th and I_PIVOT-th rows if I_PIVOT != K then for J = 1 to rows do TEMP = B[I_PIVOT][J] B[I_PIVOT][J] = B[K][J] B[K][J] = TEMP end for L[K] = I_PIVOT end if --& interchange K-th and J_PIVOT-th columns if J_PIVOT !=K then for I = 1 to rows do TEMP = B[I][J_PIVOT] B[I][J_PIVOT] = B[I][K] B[I][K] = TEMP end for M[K] = J_PIVOT end if -- --& divide K-th column by minus pivot (-BIG_ENTRY) -- for I = 1 to rows do if I != K then B[I][K] = B[I][K] / (-BIG_ENTRY) end if end for -- -- reduce matrix row by row -- for I = 1 to rows do if I != K then for J = 1 to rows do if J != K then B[I][J] = B[I][J] + B[I][K] * B[K][J] end if end for end if end for -- --& divide K-th row by pivot -- for J = 1 to rows do if J != K then B[K][J] = B[K][J] / BIG_ENTRY end if end for B[K][K] = 1.0 / BIG_ENTRY -- end for -- end of major inverse loop -- -- final column and row interchange to obtain -- inverse of A, i.e. A**(-1) -- for K = rows to 1 by -1 do -- column interchage J_PIVOT = L[K] if J_PIVOT != K then --& intechange B(I,J_PIVOT) and B(I,K) for each row I for I = 1 to rows do TEMP = B[I][J_PIVOT] B[I][J_PIVOT] = B[I][K] B[I][K] = TEMP end for end if -- row interchage I_PIVOT = M[K] if I_PIVOT != K then --& INTECHANGE B(I_PIVOT,J) and B(K,J) for each column J for J = 1 to rows do TEMP = B[I_PIVOT][J] B[I_PIVOT][J] = B[K][J] B[K][J] = TEMP end for end if end for -- -- inverse of A is obtained and stored in B -- now ready to handle the negative power -- -- & C = B**(-P) if P = -1 then return B end if -- C = B -- for I = P+1 to -1 by -1 do --C = C * B C = matrixProduct({C, B}) end for return C end function --**************************************************************************** -- global function rot2DVectorCW90 (sequence seqIn) --********************************************************************** ****** -- This function rotates a 2D vector 90 degrees cw and returns the -- rotated vector. seqIn format is {2,4} --********************************************************************** ****** return {seqIn[2],-seqIn[1]} end function -- --********************************************************************** ****** global function rot2DVectorCCW90 (sequence seqIn) --********************************************************************** ****** -- This function rotates a 2D vector 90 degrees ccw and returns the -- rotated vector. seqIn format is {2,4} --********************************************************************** ****** return {-seqIn[2],seqIn[1]} end function --********************************************************************** ****** -- global function reverseXin2DVector(sequence seqIn) --************************************************************************ -- This function rotates a 2D vector 180 degrees about the Y axis. -- seqIn is of the form {2,4} --************************************************************************ return {-seqIn[1], seqIn[2]} end function --**************************************************************************** -- global function reverseYin2DVector(sequence seqIn) --********************************************************************** ****** -- Rotates 2D vector 180 degrees about the X axis. seqIn form is -- {2,4} --********************************************************************** ****** return {seqIn[1], -seqIn[2]} end function --************************************************************************ -- global function getValueACrossB(sequence seqIn) --A : VEC2T; B : VEC2T) return FLOAT is --************************************************************************ --Gets magnitude of A cross B for 2 2D vectors. --************************************************************************ sequence A, B A = seqIn[1] B = seqIn[2] return A[1] * B[2] - A[2] * B[1] end function -- -- ******************************************************************-- global function getVectorLength(sequence seqIn) --seqIn is of the form {2,3,4}, assumed to be x, y, z coordinates of a vector --in x,y,z space with x, y, z mutually perpendicular ()more than 3 coords --can be used atom a a = 0 --return sqrt(seqIn[1] * seqIn[1] + seqIn[2] * seqIn[2] + seqIn[3] * seqIn[3]) for i = 1 to length(seqIn) by 1 do a = a + seqIn[i] * seqIn[i] end for return sqrt(a) end function -- --************************************************************************ -- global function getCosIncludedAngleBetweenAB(sequence seqIn) --seqIn is of form {vecA , vecB} --Gets cos(THETA) where THETA is angle between 2 vectors. --************************************************************************ sequence A, B atom lenA, lenB A = seqIn[1] B = seqIn[2] lenA = getVectorLength(A) lenB = getVectorLength(B) if lenA = 0 or lenB = 0 then puts(1,"One vec has 0 length!") return 1=0 else return ((dotRowRowVec({A,B})) / ( getVectorLength(A) * getVectorLength(B))) end if end function -- --************************************************************************ -- --following function solves a simultaneous equation set via Crout's --reduction. It should take half the time of a solution via inversion. --Actually it takes 4 times as long???? --include matrix.e -- global function crout(sequence seqIn, sequence vecIn) -- --For a n by n simultaneous equation system such as: -- x1 + 2*x2 - x4 = 1 -- x1 + x2 - x3 + x4 = 4 -- 2*x1 - x2 + 2*x3 = 6 -- x1 + x2 - x4 = -1 -- --Seqin is the square matrix 1 2 0 -1 -- 1 1 -1 1 -- 2 -1 2 0 -- 1 1 0 -1 --Vecin is the column vector {{1},{4},{6},{-1}} -- --They will be manipulated by the crout reduction and forward and backward --substitution to give the solution to the equations, a new column vector, --which is returned. --Reference, Matricees for Engineers, by Alan D. Kraus, page 118 -- --The code fragment is: sequence U, L, Y, X integer mord atom suml, sumu mord = length(seqIn) U = make2DMatrix(mord,mord) --upper triangle L = make2DMatrix(mord,mord) --lower triangle Y = makeRowVector(mord) --Y = U * X X = makeRowVector(mord) --X = answer vector --code for finding U and L follows for k = 1 to mord do U[k][k] = U[k][k] + 1.0 for i = k to mord do if k = 1 then L[i][k] = seqIn[i][k] for j = k+1 to mord do if k != j then U[k][j] = (1.0/L[k][k]) * (seqIn[k][j]) end if end for else suml = 0.0 for m = 1 to k-1 do suml = suml + L[i][m] * U[m][k] L[i][k] = seqIn[i][k] - suml end for end if for j = k to mord do sumu = 0.0 for m = 1 to k-1 do sumu = sumu + L[k][m] * U[m][j] end for U[k][j] = (1.0/L[k][k]) * (seqIn[k][j] - sumu) end for end for end for --U and L are set by above -- --Code Fragment for substitutions, set Y for i = 1 to mord do --value Y per 5.20, page 125 for g if i = 1 then Y[i] = (1.0 / L[i][i]) * vecIn[i][1] else suml = 0.0 for m = 1 to i - 1 do suml = suml + L[i][m] * Y[m] end for Y[i] = (1.0 / L[i][i]) * (vecIn[i][1] - suml) end if end for -- --Now use back subs to set X for k = mord to 1 by -1 do --value X per 5.21, page 125 sumu = 0.0 for m = k+1 to mord do sumu = sumu + U[k][m] * X[m] end for X[k] = Y[k] - sumu end for return X end function -- --************************************************************************ -- global function simultaneousInvert(sequence seqIn, sequence vecIn) -- --For a n by n simultaneous equation system such as: -- x1 + 2*x2 - x4 = 1 -- x1 + x2 - x3 + x4 = 4 -- 2*x1 - x2 + 2*x3 = 6 -- x1 + x2 - x4 = -1 -- --seqIn is the square matrix 1 2 0 -1 -- 1 1 -1 1 -- 2 -1 2 0 -- 1 1 0 -1 -- --seqIn = {{1,2,0,-1,},{1,1,-1,1},{2,-1,2,0},{1,1,0,-1}} --vecIn is the column vector {{1},{4},{6},{-1}} -- --The solutions will be determined via inversion of A and multiplication --by B with the result returned. It is a new column vector containing the --solutions for x. Reference, Matricees for Engineers, by Alan D. Kraus return matrixProduct({matrixPower({seqIn, -1}), vecIn}) end function -- --************************************************************************ --end MATRIX_PACKAGE;