From fe6837f6dcf852ca55921c6dca4ca3d57917a4b4 Mon Sep 17 00:00:00 2001 From: David Manura Date: Wed, 22 Sep 2010 21:41:53 -0400 Subject: [PATCH] import version 0.2.9, 2008-08-26 - decoupled symbol class from matrix class: - matrix.replace has new semantics, applying a function to each element. For old behavior: mtx = mtx:replace(matrix.symbol.makereplacer(...)) - replaced mtx:gsub(a,b) with mtx = mtx:replace(symbol.gsub,a,b) - replaced matrix.tosymbol(mtx) with mtx = mtx:replace(symbol) - eliminated dependency on complex: - replaced matrix.tocomplex(mtx) with mtx = mtx:replace(complex) - replaced matrix.conjugate(mtx) with mtx = mtx:replace(complex.conjugate) - mulnum and divnum no longer can take num of type string - complex table no longer returned on module load - renamed remcomplex to elementstostrings and changed it to return new matrix rather than doing in-place modification - fixed matrix.numround (numround variable mispelled). Reported by Goeff Richards. --- LICENSE.txt | 35 ++ README.txt | 22 + .../complex_changelog.txt | 5 +- fit_changelog.txt => doc/fit_changelog.txt | 0 .../matrix_changelog.txt | 62 +- complex.lua => lua/complex.lua | 16 +- matrix.lua => lua/matrix.lua | 530 ++++++++---------- rockspec | 31 + fit.lua => samples/fit.lua | 3 +- test_fit.lua | 22 - tests/test.lua | 8 + test_complex.lua => tests/test_complex.lua | 10 +- tests/test_fit.lua | 25 + test_matrix.lua => tests/test_matrix.lua | 80 +-- 14 files changed, 448 insertions(+), 401 deletions(-) create mode 100644 LICENSE.txt create mode 100644 README.txt rename complex_changelog.txt => doc/complex_changelog.txt (96%) rename fit_changelog.txt => doc/fit_changelog.txt (100%) rename matrix_changelog.txt => doc/matrix_changelog.txt (85%) rename complex.lua => lua/complex.lua (97%) rename matrix.lua => lua/matrix.lua (78%) create mode 100644 rockspec rename fit.lua => samples/fit.lua (96%) delete mode 100644 test_fit.lua create mode 100644 tests/test.lua rename test_complex.lua => tests/test_complex.lua (96%) create mode 100644 tests/test_fit.lua rename test_matrix.lua => tests/test_matrix.lua (77%) diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..cce010c --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,35 @@ +LuaMatrix License +----------- + +LuaMatrix ( http://luamatrix.luaforge.net/ ) is licensed under the +same terms as Lua (MIT license) reproduced below. This means that +LuaMatrix is free software and can be used for both academic and +commercial purposes at absolutely no cost. + +For details and rationale, see http://www.lua.org/license.html . + +=============================================================================== + +Copyright (C) 2007-2010 Michael Lutz. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +=============================================================================== + +(end of COPYRIGHT) diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..a0e8b2b --- /dev/null +++ b/README.txt @@ -0,0 +1,22 @@ +== Description == + +LuaMatrix - Matrices and matrix operations implemented in pure Lua. + +This supports operations on matrices and vectors whose elements are +real, complex, or symbolic. Implemented entirely in Lua as tables. +Includes a complex number data type too. + +For details on use, see the comments in matrix.lua and complex.lua, +as well as the test suite. + +To install, copy matrix.lua and complex.lua into your LUA_PATH. The +modules can alternately be installed via LuaRocks ("luarocks install +luamatrix"). + +== Project Page == + +http://lua-users.org/wiki/LuaMatrix + +== License == + +See the LICENSE.txt file for licensing details. diff --git a/complex_changelog.txt b/doc/complex_changelog.txt similarity index 96% rename from complex_changelog.txt rename to doc/complex_changelog.txt index bd99662..372d0bb 100644 --- a/complex_changelog.txt +++ b/doc/complex_changelog.txt @@ -1,5 +1,8 @@ +complex changelog -complex changelog: +v 0.3.1: 2007-11-12 + [ David Manura ] + rename mulconjugate to norm2 v 0.3.0: 2007-08-26 - fixed print function diff --git a/fit_changelog.txt b/doc/fit_changelog.txt similarity index 100% rename from fit_changelog.txt rename to doc/fit_changelog.txt diff --git a/matrix_changelog.txt b/doc/matrix_changelog.txt similarity index 85% rename from matrix_changelog.txt rename to doc/matrix_changelog.txt index fc9ee31..63d8b6a 100644 --- a/matrix_changelog.txt +++ b/doc/matrix_changelog.txt @@ -1,49 +1,21 @@ -matrix function list: +matrix changelog -matrix.add -matrix.columns -matrix.concath -matrix.concatv -matrix.conjugate -matrix.copy -matrix.cross -matrix.det -matrix.div -matrix.divnum -matrix.dogauss -matrix.getelement -matrix.gsub -matrix.invert -matrix.ipairs -matrix.latex -matrix.len -matrix.mul -matrix.mulnum -matrix:new -matrix.normf -matrix.normmax -matrix.pow -matrix.print -matrix.random -matrix.remcomplex -matrix.replace -matrix.root -matrix.rotl -matrix.rotr -matrix.round -matrix.rows -matrix.scalar -matrix.setelement -matrix.size -matrix.solve -matrix.sqrt -matrix.sub -matrix.subm -matrix.tocomplex -matrix.tostring -matrix.tosymbol -matrix.transpose -matrix.type +v 0.2.9: 2008-08-26 + [ David Manura ] + - decoupled symbol class from matrix class: + - matrix.replace has new semantics, applying a function to each element. + For old behavior: mtx = mtx:replace(matrix.symbol.makereplacer(...)) + - replaced mtx:gsub(a,b) with mtx = mtx:replace(symbol.gsub,a,b) + - replaced matrix.tosymbol(mtx) with mtx = mtx:replace(symbol) + - eliminated dependency on complex: + - replaced matrix.tocomplex(mtx) with mtx = mtx:replace(complex) + - replaced matrix.conjugate(mtx) with mtx = mtx:replace(complex.conjugate) + - mulnum and divnum no longer can take num of type string + - complex table no longer returned on module load + - renamed remcomplex to elementstostrings and changed it to return new + matrix rather than doing in-place modification + - Fixed matrix.numround (numround variable mispelled). + Reported by Goeff Richards. v 0.2.8: 2007-08-26 [ Michael Lutz ] diff --git a/complex.lua b/lua/complex.lua similarity index 97% rename from complex.lua rename to lua/complex.lua index c6dd552..86bfaea 100644 --- a/complex.lua +++ b/lua/complex.lua @@ -1,4 +1,4 @@ --- complex 0.3.0 +-- complex 0.3.1 -- Lua 5.1 -- 'complex' provides common tasks with complex numbers @@ -15,8 +15,7 @@ -- the access is faster than in a hash table -- the metatable is just a add on, when it comes to speed, one is faster using a direct function call --- http://luaforge.net/projects/LuaMatrix --- http://lua-users.org/wiki/ComplexNumbers +-- http://luamatrix.luaforge.net -- Licensed under the same terms as Lua itself. @@ -191,9 +190,10 @@ function complex.polardeg( cx ) return math.sqrt( cx[1]^2 + cx[2]^2 ), math.atan2( cx[2], cx[1] ) / math.pi * 180 end --- complex.mulconjugate( cx ) --- multiply with conjugate, function returning a number -function complex.mulconjugate( cx ) +-- complex.norm2( cx ) +-- multiply with conjugate, function returning a scalar number +-- norm2(x + i*y) returns x^2 + y^2 +function complex.norm2( cx ) return cx[1]^2 + cx[2]^2 end @@ -330,6 +330,10 @@ function complex.round( cx,idp ) math.floor( cx[2] * mult + 0.5 ) / mult }, complex_meta ) end +--// variables +complex.zero = complex.new(0, 0) +complex.one = complex.new(1, 0) + --// metatable functions complex_meta.__add = function( cx1,cx2 ) diff --git a/matrix.lua b/lua/matrix.lua similarity index 78% rename from matrix.lua rename to lua/matrix.lua index 8096097..96f3c03 100644 --- a/matrix.lua +++ b/lua/matrix.lua @@ -1,5 +1,5 @@ --[[ - matrix v 0.2.8 + matrix v 0.2.9 Lua 5.1 compatible @@ -30,10 +30,53 @@ where num will be a matrix with the result in mtx[1][1], or use num = vec1:scalar( vec2 ), where num is a number - Sites: - http://luaforge.net/projects/LuaMatrix - http://lua-users.org/wiki/SimpleMatrix - + Site: http://luamatrix.luaforge.net + + matrix function list: + + matrix.add + matrix.columns + matrix.concath + matrix.concatv + matrix.copy + matrix.cross + matrix.det + matrix.div + matrix.divnum + matrix.dogauss + matrix.elementstostring + matrix.getelement + matrix.gsub + matrix.invert + matrix.ipairs + matrix.latex + matrix.len + matrix.mul + matrix.mulnum + matrix:new + matrix.normf + matrix.normmax + matrix.pow + matrix.print + matrix.random + matrix.replace + matrix.root + matrix.rotl + matrix.rotr + matrix.round + matrix.rows + matrix.scalar + matrix.setelement + matrix.size + matrix.solve + matrix.sqrt + matrix.sub + matrix.subm + matrix.tostring + matrix.transpose + matrix.type + + Licensed under the same terms as Lua itself. Developers: @@ -41,10 +84,6 @@ David Manura http://lua-users.org/wiki/DavidManura ]]-- --- for speed and clearer code load the complex function table --- in there we define the complex number -local complex = require "complex" - --//////////// --// matrix // --//////////// @@ -54,13 +93,6 @@ local matrix = {} -- access to the metatable we set at the end of the file local matrix_meta = {} --- access to the symbolic metatable -local symbol_meta = {}; symbol_meta.__index = symbol_meta --- set up a symbol type -local function newsymbol(o) - return setmetatable({tostring(o)}, symbol_meta) -end - --///////////////////////////// --// Get 'new' matrix object // --///////////////////////////// @@ -124,7 +156,7 @@ setmetatable( matrix, { __call = function( ... ) return matrix.new( ... ) end } --// matrix 'matrix' functions // --/////////////////////////////// ---// for real, complx and symbolic matrices //-- +--// for real, complex and symbolic matrices //-- -- note: real and complex matrices may be added, subtracted, etc. -- real and symbolic matrices may also be added, subtracted, etc. @@ -132,33 +164,35 @@ setmetatable( matrix, { __call = function( ... ) return matrix.new( ... ) end } -- since it is not clear which metatable then is used --// matrix.add ( m1, m2 ) --- Add 2 matrices; m2 may be of bigger size than m1 +-- Add two matrices; m2 may be of bigger size than m1 function matrix.add( m1, m2 ) local mtx = {} for i = 1,#m1 do - mtx[i] = {} + local m3i = {} + mtx[i] = m3i for j = 1,#m1[1] do - mtx[i][j] = m1[i][j] + m2[i][j] + m3i[j] = m1[i][j] + m2[i][j] end end return setmetatable( mtx, matrix_meta ) end --// matrix.sub ( m1 ,m2 ) --- Subtract 2 matrices; m2 may be of bigger size than m1 +-- Subtract two matrices; m2 may be of bigger size than m1 function matrix.sub( m1, m2 ) local mtx = {} for i = 1,#m1 do - mtx[i] = {} + local m3i = {} + mtx[i] = m3i for j = 1,#m1[1] do - mtx[i][j] = m1[i][j] - m2[i][j] + m3i[j] = m1[i][j] - m2[i][j] end end return setmetatable( mtx, matrix_meta ) end --// matrix.mul ( m1, m2 ) --- Multiply 2 matrices; m1 columns must be equal to m2 rows +-- Multiply two matrices; m1 columns must be equal to m2 rows -- e.g. #m1[1] == #m2 function matrix.mul( m1, m2 ) -- multiply rows with columns @@ -177,7 +211,7 @@ function matrix.mul( m1, m2 ) end --// matrix.div ( m1, m2 ) --- Divide 2 matrices; m1 columns must be equal to m2 rows +-- Divide two matrices; m1 columns must be equal to m2 rows -- m2 must be square, to be inverted, -- if that fails returns the rank of m2 as second argument -- e.g. #m1[1] == #m2; #m2 == #m2[1] @@ -189,12 +223,9 @@ end --// matrix.mulnum ( m1, num ) -- Multiply matrix with a number --- num may be of type 'number','complex number' or 'string' +-- num may be of type 'number' or 'complex number' -- strings get converted to complex number, if that fails then to symbol function matrix.mulnum( m1, num ) - if type(num) == "string" then - num = complex.to(num) or newsymbol(num) - end local mtx = {} -- multiply elements with number for i = 1,#m1 do @@ -208,18 +239,16 @@ end --// matrix.divnum ( m1, num ) -- Divide matrix by a number --- num may be of type 'number','complex number' or 'string' +-- num may be of type 'number' or 'complex number' -- strings get converted to complex number, if that fails then to symbol function matrix.divnum( m1, num ) - if type(num) == "string" then - num = complex.to(num) or newsymbol(num) - end local mtx = {} -- divide elements by number for i = 1,#m1 do - mtx[i] = {} + local mtxi = {} + mtx[i] = mtxi for j = 1,#m1[1] do - mtx[i][j] = m1[i][j] / num + mtxi[j] = m1[i][j] / num end end return setmetatable( mtx, matrix_meta ) @@ -251,6 +280,10 @@ function matrix.pow( m1, num ) return mtx end +local function number_norm2(x) + return x * x +end + --// matrix.det ( m1 ) -- Calculate the determinant of a matrix -- m1 needs to be square @@ -261,8 +294,6 @@ end -- here we try to get the nearest element to |1|, (smallest pivot element) -- os that usually we have |mtx[i][j]/subdet| > 1 or mtx[i][j]; -- with complex matrices we use the complex.abs function to check if it is bigger or smaller -local fiszerocomplex = function( cx ) return complex.is(cx,0,0) end -local fiszeronumber = function( num ) return num == 0 end function matrix.det( m1 ) -- check if matrix is quadratic @@ -284,16 +315,10 @@ function matrix.det( m1 ) end --// no symbolic matrix supported below here - - local fiszero, abs - if matrix.type( m1 ) == "complex" then - fiszero = fiszerocomplex - abs = complex.mulconjugate - else - fiszero = fiszeronumber - abs = math.abs - end - + local e = m1[1][1] + local zero = type(e) == "table" and e.zero or 0 + local norm2 = type(e) == "table" and e.norm2 or number_norm2 + --// matrix is bigger than 3x3 -- get determinant -- using Gauss elimination and Laplace @@ -313,12 +338,12 @@ function matrix.det( m1 ) -- if no subdet has been found if not subdet then -- check if element it is not zero - if not fiszero(e) then + if e ~= zero then -- use element as new subdet subdet,xrow = e,i end -- check for elements nearest to 1 or -1 - elseif (not fiszero(e)) and math.abs(abs(e)-1) < math.abs(abs(subdet)-1) then + elseif e ~= zero and math.abs(norm2(e)-1) < math.abs(norm2(subdet)-1) then subdet,xrow = e,i end end @@ -335,7 +360,7 @@ function matrix.det( m1 ) for i = 1,rows-1 do -- factor is the dividor of the first element -- if element is not already zero - if not fiszero( mtx[i][j] ) then + if mtx[i][j] ~= zero then local factor = mtx[i][j]/subdet -- update all remaining fields of the matrix, with value from xrow for n = j+1,#mtx[1] do @@ -366,23 +391,23 @@ end -- locals -- checking here for the nearest element to 1 or -1; (smallest pivot element) --- this way the factor of the evolving number division should be > 1 or the divided number itself, --- what gives better results -local setelementtosmallest = function( mtx,i,j,fiszero,fisone,abs ) +-- this way the factor of the evolving number division should be > 1 or the +-- divided number itself, what gives better results +local setelementtosmallest = function( mtx,i,j,zero,one,norm2 ) -- check if element is one - if fisone(mtx[i][j]) then return true end + if mtx[i][j] == one then return true end -- check for lowest value local _ilow for _i = i,#mtx do local e = mtx[_i][j] - if fisone(e) then + if e == one then break end if not _ilow then - if not fiszero(e) then + if e ~= zero then _ilow = _i end - elseif (not fiszero(e)) and math.abs(abs(e)-1) < math.abs(abs(mtx[_ilow][j])-1) then + elseif (e ~= zero) and math.abs(norm2(e)-1) < math.abs(norm2(mtx[_ilow][j])-1) then _ilow = _i end end @@ -395,40 +420,28 @@ local setelementtosmallest = function( mtx,i,j,fiszero,fisone,abs ) return true end end -local cxfiszero = function( cx ) return complex.is(cx,0,0) end -local cxfsetzero = function( mtx,i,j ) complex.set(mtx[i][j],0,0) end -local cxfisone = function( cx ) return complex.abs(cx) == 1 end -local cxfsetone = function( mtx,i,j ) complex.set(mtx[i][j],1,0) end -local numfiszero = function( num ) return num == 0 end -local numfsetzero = function( mtx,i,j ) mtx[i][j] = 0 end -local numfisone = function( num ) return math.abs(num) == 1 end -local numfsetone = function( mtx,i,j ) mtx[i][j] = 1 end + +local function copy(x) + return type(x) == "table" and x.copy(x) or x +end + -- note: in --// ... //-- we have a way that does no divison, -- however with big number and matrices we get problems since we do no reducing function matrix.dogauss( mtx ) - local fiszero,fsetzero,fisone,fsetone,abs - if matrix.type( mtx ) == "complex" then - fiszero = cxfiszero - fsetzero = cxfsetzero - fisone = cxfisone - fsetone = cxfsetone - abs = complex.mulconjugate - else - fiszero = numfiszero - fsetzero = numfsetzero - fisone = numfisone - fsetone = numfsetone - abs = math.abs - end + local e = mtx[1][1] + local zero = type(e) == "table" and e.zero or 0 + local one = type(e) == "table" and e.one or 1 + local norm2 = type(e) == "table" and e.norm2 or number_norm2 + local rows,columns = #mtx,#mtx[1] -- stairs left -> right for j = 1,rows do -- check if element can be setted to one - if setelementtosmallest( mtx,j,j,fiszero,fisone,abs ) then + if setelementtosmallest( mtx,j,j,zero,one,norm2 ) then -- start parsing rows for i = j+1,rows do -- check if element is not already zero - if not fiszero(mtx[i][j]) then + if mtx[i][j] ~= zero then -- we may add x*otherline row, to set element to zero -- tozero - x*mtx[j][j] = 0; x = tozero/mtx[j][j] local factor = mtx[i][j]/mtx[j][j] @@ -436,7 +449,7 @@ function matrix.dogauss( mtx ) -- yet with big matrices (since we do no reducing and other things) -- we get too big numbers --local factor1,factor2 = mtx[i][j],mtx[j][j] //-- - fsetzero(mtx,i,j) + mtx[i][j] = copy(zero) for _j = j+1,columns do --// mtx[i][_j] = mtx[i][_j] * factor2 - factor1 * mtx[j][_j] //-- mtx[i][_j] = mtx[i][_j] - factor * mtx[j][_j] @@ -459,15 +472,15 @@ function matrix.dogauss( mtx ) -- start parsing rows for i = j-1,1,-1 do -- check if element is not already zero - if not fiszero(mtx[i][j]) then + if mtx[i][j] ~= zero then local factor = mtx[i][j] for _j = j+1,columns do mtx[i][_j] = mtx[i][_j] - factor * mtx[j][_j] end - fsetzero(mtx,i,j) + mtx[i][j] = copy(zero) end end - fsetone(mtx,j,j) + mtx[j][j] = copy(one) end return true end @@ -481,27 +494,14 @@ function matrix.invert( m1 ) assert(#m1 == #m1[1], "matrix not square") local mtx = matrix.copy( m1 ) local ident = setmetatable( {},matrix_meta ) - if matrix.type( mtx ) == "complex" then - for i = 1,#m1 do - ident[i] = {} - for j = 1,#m1 do - if i == j then - ident[i][j] = complex.new( 1,0 ) - else - ident[i][j] = complex.new( 0,0 ) - end - end - end - else - for i = 1,#m1 do - ident[i] = {} - for j = 1,#m1 do - if i == j then - ident[i][j] = 1 - else - ident[i][j] = 0 - end - end + local e = m1[1][1] + local zero = type(e) == "table" and e.zero or 0 + local one = type(e) == "table" and e.one or 1 + for i = 1,#m1 do + local identi = {} + ident[i] = identi + for j = 1,#m1 do + identi[j] = copy((i == j) and one or zero) end end mtx = matrix.concath( mtx,ident ) @@ -527,7 +527,8 @@ end -- local average error local function get_abs_avg( m1, m2 ) local dist = 0 - local abs = matrix.type(m1) == "complex" and complex.abs or math.abs + local e = m1[1][1] + local abs = type(e) == "table" and e.abs or math.abs for i=1,#m1 do for j=1,#m1[1] do dist = dist + abs(m1[i][j]-m2[i][j]) @@ -650,7 +651,7 @@ local tround = function( t,mult ) end function matrix.round( mtx, idp ) local mult = 10^( idp or 0 ) - local fround = matrix.type( mtx ) == "number" and numound or tround + local fround = matrix.type( mtx ) == "number" and numround or tround for i = 1,#mtx do for j = 1,#mtx[1] do mtx[i][j] = fround(mtx[i][j],mult) @@ -691,12 +692,10 @@ end --// matrix.type ( mtx ) -- get type of matrix, normal/complex/symbol or tensor function matrix.type( mtx ) - if type(mtx[1][1]) == "table" then - if complex.type(mtx[1][1]) then - return "complex" - end - if getmetatable(mtx[1][1]) == symbol_meta then - return "symbol" + local e = mtx[1][1] + if type(e) == "table" then + if e.type then + return e:type() end return "tensor" end @@ -766,7 +765,7 @@ function matrix.subm( m1,i1,j1,i2,j2 ) end --// matrix.concath( m1, m2 ) --- Concatenate 2 matrices, horizontal +-- Concatenate two matrices, horizontal -- will return m1m2; rows have to be the same -- e.g.: #m1 == #m2 function matrix.concath( m1,m2 ) @@ -787,7 +786,7 @@ function matrix.concath( m1,m2 ) end --// matrix.concatv ( m1, m2 ) --- Concatenate 2 matrices, vertical +-- Concatenate two matrices, vertical -- will return m1 -- m2 -- columns have to be the same; e.g.: #m1[1] == #m2[1] @@ -838,59 +837,32 @@ function matrix.rotr( m1 ) return mtx end --- local get_elemnts in string -local get_tstr = function( t ) - return "["..table.concat(t,",").."]" -end -local get_str = function( e ) - return tostring(e) -end --- local get_elemnts in string and formated -local getf_tstr = function( t,fstr ) +local function tensor_tostring( t,fstr ) + if not fstr then return "["..table.concat(t,",").."]" end local tval = {} for i,v in ipairs( t ) do tval[i] = string.format( fstr,v ) end return "["..table.concat(tval,",").."]" end -local getf_cxstr = function( e,fstr ) - return complex.tostring( e,fstr ) -end -local getf_symstr = function( e,fstr ) - return string.format( fstr,e[1] ) -end -local getf_str = function( e,fstr ) - return string.format( fstr,e ) +local function number_tostring( e,fstr ) + return fstr and string.format( fstr,e ) or e end --// matrix.tostring ( mtx, formatstr ) -- tostring function function matrix.tostring( mtx, formatstr ) local ts = {} - local getstr - if formatstr then -- get str formatted - local mtype = matrix.type( mtx ) - if mtype == "tensor" then getstr = getf_tstr - elseif mtype == "complex" then getstr = getf_cxstr - elseif mtype == "symbol" then getstr = getf_symstr - else getstr = getf_str end - -- iteratr - for i = 1,#mtx do - local tstr = {} - for j = 1,#mtx[1] do - tstr[j] = getstr(mtx[i][j],formatstr) - end - ts[i] = table.concat(tstr, "\t") - end - else - getstr = matrix.type( mtx ) == "tensor" and get_tstr or get_str - for i = 1,#mtx do - local tstr = {} - for j = 1,#mtx[1] do - tstr[j] = getstr(mtx[i][j]) - end - ts[i] = table.concat(tstr, "\t") + local mtype = matrix.type( mtx ) + local e = mtx[1][1] + local tostring = mtype == "tensor" and tensor_tostring or + type(e) == "table" and e.tostring or number_tostring + for i = 1,#mtx do + local tstr = {} + for j = 1,#mtx[1] do + tstr[j] = tostring(mtx[i][j],formatstr) end + ts[i] = table.concat(tstr, "\t") end return table.concat(ts, "\n") end @@ -909,7 +881,7 @@ function matrix.latex( mtx, align ) -- \usepackage{dcolumn}; D{.}{,}{-1}; aligns number by . replaces it with , local align = align or "c" local str = "$\\left( \\begin{array}{"..string.rep( align, #mtx[1] ).."}\n" - local getstr = matrix.type( mtx ) == "tensor" and get_tstr or get_str + local getstr = matrix.type( mtx ) == "tensor" and tensor_tostring or number_tostring for i = 1,#mtx do str = str.."\t"..getstr(mtx[i][1]) for j = 2,#mtx[1] do @@ -1014,96 +986,29 @@ function matrix.len( m1 ) return math.sqrt( m1[1][1]^2 + m1[2][1]^2 + m1[3][1]^2 ) end ---//////////////////////////////// ---// matrix 'complex' functions // ---//////////////////////////////// ---// matrix.tocomplex ( mtx ) --- we set now all elements to a complex number --- also set the metatable -function matrix.tocomplex( mtx ) - assert( matrix.type(mtx) == "number", "matrix not of type 'number'" ) - for i = 1,#mtx do - for j = 1,#mtx[1] do - mtx[i][j] = complex.to( mtx[i][j] ) +--// matrix.replace (mtx, func, ...) +-- for each element e in the matrix mtx, replace it with func(mtx, ...). +function matrix.replace( m1, func, ... ) + local mtx = {} + for i = 1,#m1 do + local m1i = m1[i] + local mtxi = {} + for j = 1,#m1i do + mtxi[j] = func( m1i[j], ... ) end + mtx[i] = mtxi end return setmetatable( mtx, matrix_meta ) end --// matrix.remcomplex ( mtx ) --- set the matrix elements to a number or complex number string -function matrix.remcomplex( mtx ) - assert( matrix.type(mtx) == "complex", "matrix not of type 'complex'" ) - for i = 1,#mtx do - for j = 1,#mtx[1] do - mtx[i][j] = complex.tostring( mtx[i][j] ) - end - end - return setmetatable( mtx, matrix_meta ) -end - ---// matrix.conjugate ( m1 ) --- get the conjugate complex matrix -function matrix.conjugate( m1 ) - assert( matrix.type(m1) == "complex", "matrix not of type 'complex'" ) - local mtx = {} - for i = 1,#m1 do - mtx[i] = {} - for j = 1,#m1[1] do - mtx[i][j] = complex.conjugate( m1[i][j] ) - end - end - return setmetatable( mtx, matrix_meta ) -end - ---///////////////////////////////// ---// matrix 'symbol' functions // ---///////////////////////////////// - ---// matrix.tosymbol ( mtx ) --- set the matrix elements to symbolic values -function matrix.tosymbol( mtx ) - assert( matrix.type( mtx ) ~= "tensor", "cannot convert type 'tensor' to 'symbol'" ) - for i = 1,#mtx do - for j = 1,#mtx[1] do - mtx[i][j] = newsymbol( mtx[i][j] ) - end - end - return setmetatable( mtx, matrix_meta ) -end - ---// matrix.gsub( m1, from, to ) --- perform gsub on all elements -function matrix.gsub( m1,from,to ) - assert( matrix.type( m1 ) == "symbol", "matrix not of type 'symbol'" ) - local mtx = {} - for i = 1,#m1 do - mtx[i] = {} - for j = 1,#m1[1] do - mtx[i][j] = newsymbol( string.gsub( m1[i][j][1],from,to ) ) - end - end - return setmetatable( mtx, matrix_meta ) -end - ---// matrix.replace ( m1, ... ) --- replace one letter by something else --- replace( "a",4,"b",7, ... ) will replace a with 4 and b with 7 -function matrix.replace( m1,... ) - assert( matrix.type( m1 ) == "symbol", "matrix not of type 'symbol'" ) - local tosub,args = {},{...} - for i = 1,#args,2 do - tosub[args[i]] = args[i+1] - end - local mtx = {} - for i = 1,#m1 do - mtx[i] = {} - for j = 1,#m1[1] do - mtx[i][j] = newsymbol( string.gsub( m1[i][j][1], "%a", function( a ) return tosub[a] or a end ) ) - end - end - return setmetatable( mtx, matrix_meta ) +-- set the matrix elements to strings +-- IMPROVE: tostring v.s. tostringelements confusing +function matrix.elementstostrings( mtx ) + local e = mtx[1][1] + local tostring = type(e) == "table" and e.tostring or tostring + return matrix.replace(mtx, tostring) end --// matrix.solve ( m1 ) @@ -1120,46 +1025,6 @@ function matrix.solve( m1 ) return setmetatable( mtx, matrix_meta ) end -function symbol_meta.__add(a,b) - return newsymbol(a .. "+" .. b) -end - -function symbol_meta.__sub(a,b) - return newsymbol(a .. "-" .. b) -end - -function symbol_meta.__mul(a,b) - return newsymbol("(" .. a .. ")*(" .. b .. ")") -end - -function symbol_meta.__div(a,b) - return newsymbol("(" .. a .. ")/(" .. b .. ")") -end - -function symbol_meta.__pow(a,b) - return newsymbol("(" .. a .. ")^(" .. b .. ")") -end - -function symbol_meta.__eq(a,b) - return a[1] == b[1] -end - -function symbol_meta.__tostring(a) - return a[1] -end - -function symbol_meta.__concat(a,b) - return tostring(a) .. tostring(b) -end - -function symbol_meta.abs(a) - return newsymbol("(" .. a[1] .. "):abs()") -end - -function symbol_meta.sqrt(a) - return newsymbol("(" .. a[1] .. "):sqrt()") -end - --////////////////////////-- --// METATABLE HANDLING //-- --////////////////////////-- @@ -1232,7 +1097,7 @@ matrix_meta.__eq = function( m1, m2 ) if #m1 ~= #m2 or #m1[1] ~= #m2[1] then return false end - -- check normal,complex and symbolic + -- check elements equal for i = 1,#m1 do for j = 1,#m1[1] do if m1[i][j] ~= m2[i][j] then @@ -1259,8 +1124,107 @@ for k,v in pairs( matrix ) do matrix_meta.__index[k] = v end --- return the matrix and complex -return matrix, complex + +--///////////////////////////////// +--// symbol class implementation +--///////////////////////////////// + +-- access to the symbolic metatable +local symbol_meta = {}; symbol_meta.__index = symbol_meta +local symbol = symbol_meta + +function symbol_meta.new(o) + return setmetatable({tostring(o)}, symbol_meta) +end +symbol_meta.to = symbol_meta.new + +-- symbol( arg ) +-- same as symbol.to( arg ) +-- set __call behaviour of symbol +setmetatable( symbol_meta, { __call = function( _,s ) return symbol_meta.to( s ) end } ) + + +-- Converts object to string, optionally with formatting. +function symbol_meta.tostring( e,fstr ) + return string.format( fstr,e[1] ) +end + +-- Returns "symbol" if object is a symbol type, else nothing. +function symbol_meta:type() + if getmetatable(self) == symbol_meta then + return "symbol" + end +end + +-- Performs string.gsub on symbol. +-- for use in matrix.replace +function symbol_meta:gsub(from, to) + return symbol.to( string.gsub( self[1],from,to ) ) +end + +-- creates function that replaces one letter by something else +-- makereplacer( "a",4,"b",7, ... )(x) +-- will replace a with 4 and b with 7 in symbol x. +-- for use in matrix.replace +function symbol_meta.makereplacer( ... ) + local tosub = {} + local args = {...} + for i = 1,#args,2 do + tosub[args[i]] = args[i+1] + end + local function func( a ) return tosub[a] or a end + return function(sym) + return symbol.to( string.gsub( sym[1], "%a", func ) ) + end +end + +-- applies abs function to symbol +function symbol_meta.abs(a) + return symbol.to("(" .. a[1] .. "):abs()") +end + +-- applies sqrt function to symbol +function symbol_meta.sqrt(a) + return symbol.to("(" .. a[1] .. "):sqrt()") +end + +function symbol_meta.__add(a,b) + return symbol.to(a .. "+" .. b) +end + +function symbol_meta.__sub(a,b) + return symbol.to(a .. "-" .. b) +end + +function symbol_meta.__mul(a,b) + return symbol.to("(" .. a .. ")*(" .. b .. ")") +end + +function symbol_meta.__div(a,b) + return symbol.to("(" .. a .. ")/(" .. b .. ")") +end + +function symbol_meta.__pow(a,b) + return symbol.to("(" .. a .. ")^(" .. b .. ")") +end + +function symbol_meta.__eq(a,b) + return a[1] == b[1] +end + +function symbol_meta.__tostring(a) + return a[1] +end + +function symbol_meta.__concat(a,b) + return tostring(a) .. tostring(b) +end + +matrix.symbol = symbol + + +-- return matrix +return matrix --///////////////-- --// chillcode //-- diff --git a/rockspec b/rockspec new file mode 100644 index 0000000..ad138a4 --- /dev/null +++ b/rockspec @@ -0,0 +1,31 @@ +package = "LuaMatrix" +version = "[VERSION]" +source = { + url = "[URL]", +} +description = { + summary = "Matrices and matrix operations implemented in pure Lua.", + detailed = [[ + This supports operations on matrices and vectors whose elements are + real, complex, or symbolic. Implemented entirely in Lua as tables. + Includes a complex number data type too. + ]], + license = "MIT/X11", + homepage = "http://luamatrix.luaforge.net/", + maintainer = "David Manura ", +} +dependencies = { + "lua >= 5.1", +} +build = { + type = "none", + install = { + lua = { + ["complex"] = "lua/complex.lua", + ["matrix"] = "lua/matrix.lua", + } + }, + copy_directories = {"doc", "samples", "tests"}, +} +-- test: tests/test.lua + diff --git a/fit.lua b/samples/fit.lua similarity index 96% rename from fit.lua rename to samples/fit.lua index de58a38..a48e952 100644 --- a/fit.lua +++ b/samples/fit.lua @@ -8,8 +8,7 @@ -- little add-on to the matrix module, to show some curve fitting --- http://luaforge.net/projects/LuaMatrix --- http://lua-users.org/wiki/SimpleFit +-- http://luamatrix.luaforge.net -- Licensed under the same terms as Lua itself. diff --git a/test_fit.lua b/test_fit.lua deleted file mode 100644 index 80ccb84..0000000 --- a/test_fit.lua +++ /dev/null @@ -1,22 +0,0 @@ --- require fit -local fit = require "fit" - -print( "Fit a straight line " ) --- x(i) = 2 | 3 | 4 | 5 --- y(i) = 5 | 9 | 15 | 21 --- model = y = a + b * x --- r(i) = y(i) - ( a + b * x(i) ) -local a,b = fit.linear( { 2,3, 4, 5 }, - { 5,9,15,21 } ) -print( "=> y = ( "..a.." ) + ( "..b.." ) * x") - -print( "Fit a parabola " ) -local a, b, c = fit.parabola( { 0,1,2,4,6 }, - { 3,1,0,1,4 } ) -print( "=> y = ( "..a.." ) + ( "..b.." ) * x + ( "..c.." ) * x²") - -print( "Fit exponential" ) -local a, b = fit.exponential( {1, 2, 3, 4, 5}, - {1,3.1,5.6,9.1,12.9} ) -print( "=> y = ( "..a.." ) * x^( "..b.." )") - \ No newline at end of file diff --git a/tests/test.lua b/tests/test.lua new file mode 100644 index 0000000..80b665a --- /dev/null +++ b/tests/test.lua @@ -0,0 +1,8 @@ +-- test suite (run from parent directory). + +package.path = './lua/?.lua;' .. package.path + +dofile 'tests/test_complex.lua' +dofile 'tests/test_matrix.lua' +dofile 'tests/test_fit.lua' +print 'ALL PASSED' diff --git a/test_complex.lua b/tests/test_complex.lua similarity index 96% rename from test_complex.lua rename to tests/test_complex.lua index 0b1aa5c..d4d7fae 100644 --- a/test_complex.lua +++ b/tests/test_complex.lua @@ -82,9 +82,9 @@ local r,phi = complex.polardeg( {0,-3} ) assert( r == 3 ) assert( phi == -90 ) --- complex.mulconjugate( cx ) +-- complex.norm2( cx ) cx = complex "2+3i" -assert( complex.mulconjugate( cx ) == 13 ) +assert( complex.norm2( cx ) == 13 ) -- complex.abs( cx ) cx = complex "3+4i" @@ -168,7 +168,7 @@ assert( cx:ln():round( 4 ) == complex "1.6094+0.9273i" ) -- complex.exp( cx ) cx = complex "2+3i" -assert( cx:ln():exp() == complex "2+3i" ) +assert( cx.abs( cx:ln():exp() - complex "2+3i" ) < 1e-7 ) -- complex.conjugate( cx ) cx = complex "2+3i" @@ -182,4 +182,6 @@ assert( cx+2 == complex "4+3i" ) -- __unm cx = complex "2+3i" -assert( -cx == complex "-2-3i" ) \ No newline at end of file +assert( -cx == complex "-2-3i" ) + +print 'PASSED' diff --git a/tests/test_fit.lua b/tests/test_fit.lua new file mode 100644 index 0000000..263f3d7 --- /dev/null +++ b/tests/test_fit.lua @@ -0,0 +1,25 @@ +-- require fit +package.path = "samples/?.lua;" .. package.path +local fit = require "fit" + +-- Fit a straight line +-- x(i) = 2 | 3 | 4 | 5 +-- y(i) = 5 | 9 | 15 | 21 +-- model = y = a + b * x +-- r(i) = y(i) - ( a + b * x(i) ) +local a,b = fit.linear( { 2,3, 4, 5 }, { 5,9,15,21 } ) +assert(math.abs(a - -6.4) < 0.001) +assert(math.abs(b - 5.4) < 0.001) + +-- Fit a parabola +local a, b, c = fit.parabola( { 0,1,2,4,6 }, { 3,1,0,1,4 } ) +assert(math.abs(a - 2.8251599147122) < 0.001) +assert(math.abs(b - -2.0490405117271) < 0.001) +assert(math.abs(c - 0.3773987206823) < 0.001) + +-- Fit exponential +local a, b = fit.exponential( {1, 2, 3, 4, 5}, {1,3.1,5.6,9.1,12.9} ) +assert(math.abs(a - 1.0077958966968) < 0.001) +assert(math.abs(b - 1.5834684450364) < 0.001) + +print 'PASSED' diff --git a/test_matrix.lua b/tests/test_matrix.lua similarity index 77% rename from test_matrix.lua rename to tests/test_matrix.lua index 1302bf2..021faf7 100644 --- a/test_matrix.lua +++ b/tests/test_matrix.lua @@ -1,4 +1,6 @@ -local matrix, complex = require "matrix" +local matrix = require "matrix" +local complex = require "complex" +local symbol = matrix.symbol local mtx, m1,m2,m3,m4,m5, ms,ms1,ms2,ms3,ms4 @@ -24,42 +26,42 @@ m1 = matrix{{8,4,1},{6,8,3}} m2 = matrix{{-8,1,3},{5,2,1}} assert(m1 + m2 == matrix{{0,5,4},{11,10,4}}) -- matrix.add; complex -m1 = matrix{{10,"2+6i",1},{5,1,"4-2i"}}:tocomplex() -m2 = matrix{{3,4,5},{"2+3i",4,"6i"}}:tocomplex() -assert(m1 + m2 == matrix{{13,"6+6i",6},{"7+3i",5,"4+4i"}}:tocomplex()) +m1 = matrix{{10,"2+6i",1},{5,1,"4-2i"}}:replace(complex) +m2 = matrix{{3,4,5},{"2+3i",4,"6i"}}:replace(complex) +assert(m1 + m2 == matrix{{13,"6+6i",6},{"7+3i",5,"4+4i"}}:replace(complex)) -- matrix.add; symbol -m1 = matrix{{8,4,1},{6,8,3}}:tosymbol() -m2 = matrix{{-8,1,3},{5,2,1}}:tosymbol() -assert(m1 + m2 == matrix{{"8+-8","4+1","1+3"},{"6+5","8+2","3+1"}}:tosymbol()) +m1 = matrix{{8,4,1},{6,8,3}}:replace(symbol) +m2 = matrix{{-8,1,3},{5,2,1}}:replace(symbol) +assert(m1 + m2 == matrix{{"8+-8","4+1","1+3"},{"6+5","8+2","3+1"}}:replace(symbol)) -- matrix.sub; number m1 = matrix{{8,4,1},{6,8,3}} m2 = matrix{{-8,1,3},{5,2,1}} assert(m1 - m2 == matrix{{16,3,-2},{1,6,2}}) -- matrix.sub; complex -m1 = matrix{{10,"2+6i",1},{5,1,"4-2i"}}:tocomplex() -m2 = matrix{{3,4,5},{"2+3i",4,"6i"}}:tocomplex() -assert(m1 - m2 == matrix{{7,"-2+6i",-4},{"3-3i",-3,"4-8i"}}:tocomplex()) +m1 = matrix{{10,"2+6i",1},{5,1,"4-2i"}}:replace(complex) +m2 = matrix{{3,4,5},{"2+3i",4,"6i"}}:replace(complex) +assert(m1 - m2 == matrix{{7,"-2+6i",-4},{"3-3i",-3,"4-8i"}}:replace(complex)) -- matrix.sub; symbol -m1 = matrix{{8,4,1},{6,8,3}}:tosymbol() -m2 = matrix{{-8,1,3},{5,2,1}}:tosymbol() -assert(m1 - m2 == matrix{{"8--8","4-1","1-3"},{"6-5","8-2","3-1"}}:tosymbol()) +m1 = matrix{{8,4,1},{6,8,3}}:replace(symbol) +m2 = matrix{{-8,1,3},{5,2,1}}:replace(symbol) +assert(m1 - m2 == matrix{{"8--8","4-1","1-3"},{"6-5","8-2","3-1"}}:replace(symbol)) -- matrix.mul; number m1 = matrix{{8,4,1},{6,8,3}} m2 = matrix{{3,1},{2,5},{7,4}} assert(m1 * m2 == matrix{{39,32},{55,58}}) -- matrix.mul; complex -m1 = matrix{{"1+2i","3-i"},{"2-2i","1+i"}}:tocomplex() -m2 = matrix{{"i","5-i"},{2,"1-i"}}:tocomplex() -assert( m1*m2 == matrix{{"4-i","9+5i"},{"4+4i","10-12i"}}:tocomplex() ) +m1 = matrix{{"1+2i","3-i"},{"2-2i","1+i"}}:replace(complex) +m2 = matrix{{"i","5-i"},{2,"1-i"}}:replace(complex) +assert( m1*m2 == matrix{{"4-i","9+5i"},{"4+4i","10-12i"}}:replace(complex) ) -- matrix.mul; symbol -m1 = matrix{{8,4,1},{6,8,3}}:tosymbol() -m2 = matrix{{3,1},{2,5},{7,4}}:tosymbol() +m1 = matrix{{8,4,1},{6,8,3}}:replace(symbol) +m2 = matrix{{3,1},{2,5},{7,4}}:replace(symbol) assert(m1 * m2 == matrix{ {"(8)*(3)+(4)*(2)+(1)*(7)", "(8)*(1)+(4)*(5)+(1)*(4)"}, {"(6)*(3)+(8)*(2)+(3)*(7)", "(6)*(1)+(8)*(5)+(3)*(4)"} -}:tosymbol()) +}:replace(symbol)) -- matrix.div; number, same for complex, not for symbol m1 = matrix {{1,2},{3,4}} @@ -72,9 +74,9 @@ assert( m2/2 == matrix{{2,2.5},{3,3.5}} ) mtx = matrix {{3,5,1},{2,4,5},{1,2,2}} assert( 2 / mtx == matrix{{4,16,-42},{-2,-10,26},{0,2,-4}} ) -- matrix.mulnum; symbol -m1 = m1:tosymbol() -assert( m1*2 == matrix{{"(1)*(2)","(2)*(2)"},{"(3)*(2)","(4)*(2)"}}:tosymbol() ) -assert( m1/2 == matrix{{"(1)/(2)","(2)/(2)"},{"(3)/(2)","(4)/(2)"}}:tosymbol() ) +m1 = m1:replace(symbol) +assert( m1*2 == matrix{{"(1)*(2)","(2)*(2)"},{"(3)*(2)","(4)*(2)"}}:replace(symbol) ) +assert( m1/2 == matrix{{"(1)/(2)","(2)/(2)"},{"(3)/(2)","(4)/(2)"}}:replace(symbol) ) -- matrix.pow; number, same complex mtx = matrix{{3,5,1},{2,4,5},{1,2,2}} @@ -94,8 +96,8 @@ assert(select(2, pcall(function() return mtx^-1 end)) mtx = matrix {{1,4,3,2},{2,1,-1,-1},{-3,2,2,-2},{-1,-5,-4,1}} assert( mtx:det() == 78 ) -- matrix.det; complex -m1 = matrix{{"1+2i","3-i"},{"2-2i","1+i"}}:tocomplex() -m2 = matrix{{"i","5-i"},{2,"1-i"}}:tocomplex() +m1 = matrix{{"1+2i","3-i"},{"2-2i","1+i"}}:replace(complex) +m2 = matrix{{"i","5-i"},{2,"1-i"}}:replace(complex) m3 = m1*m2 -- (checked in maple) assert( m3:det() == complex "12-114i" ) @@ -104,7 +106,7 @@ mtx = {{"2+3i","1+4i","-2i",3,2}, {3,"-2i",6,"4+5i",0}, {1,"1+2i",3,5,7}, {"-3+3i","3+3i",3,-8,2}} -matrix(mtx):tocomplex() +mtx = matrix(mtx):replace(complex) -- (checked in maple) assert( mtx:det():round(10) == complex "5527+2687i" ) @@ -123,7 +125,7 @@ mtx = { {3,"-1+5i",-3}, {4,0,7}, } -matrix.tocomplex( mtx ) +mtx = matrix.replace( mtx, complex ) local mtxinv = mtx^-1 local mtxinvcomp = { {"0.13349-0.07005i","0.14335+0.03609i","0.04237+0.02547i"}, @@ -131,7 +133,7 @@ local mtxinvcomp = { {"-0.07628+0.04003i","-0.08192-0.02062i","0.11865-0.01456i"},} mtxinvcomp = matrix( mtxinvcomp ) mtxinv:round( 5 ) -mtxinv:remcomplex() +mtxinv = mtxinv:elementstostrings() assert( mtxinvcomp == mtxinv ) -- matrix.sqrt; number @@ -140,7 +142,7 @@ local m2 = m1*m1 local msqrt = m2:sqrt() assert((m2 - msqrt^2):normmax() < 1E-12) -- matrix.sqrt; complex -local m1 = matrix{{4,"2+i",1},{1,5,"4-2i"},{1,"5+3i",2}}:tocomplex() +local m1 = matrix{{4,"2+i",1},{1,5,"4-2i"},{1,"5+3i",2}}:replace(complex) local m2 = m1*m1 local msqrt = m2:sqrt() assert((m2 - msqrt^2):normmax() < 1E-12) @@ -152,7 +154,7 @@ local m2 = m1^p local mroot = m2:root(p) assert((m2 - mroot^p):normmax() < 1E-7) -- matrix.root; complex -local m1 = matrix{{4,"2+i",1},{1,5,"4-2i"},{1,"5+3i",2}}:tocomplex() +local m1 = matrix{{4,"2+i",1},{1,5,"4-2i"},{1,"5+3i",2}}:replace(complex) local m2 = m1^p local mroot = m2:root(p) assert((m2 - mroot^p):normmax() < 1E-7) @@ -160,16 +162,16 @@ assert((m2 - mroot^p):normmax() < 1E-7) -- matrix.normf mtx = matrix{{2,3},{-2,-3}} assert(mtx:normf() == math.sqrt(2^2+3^2+2^2+3^2)) -mtx = matrix{{'2i','3'},{'-2i','-3'}}:tocomplex() +mtx = matrix{{'2i','3'},{'-2i','-3'}}:replace(complex) assert(mtx:normf() == math.sqrt(2^2+3^2+2^2+3^2)) -mtx = matrix{{'a','b'},{'c','d'}}:tosymbol() +mtx = matrix{{'a','b'},{'c','d'}}:replace(symbol) assert(tostring(mtx:normf()) == "(0+((a):abs())^(2)+((b):abs())^(2)+((c):abs())^(2)+((d):abs())^(2)):sqrt()") -- matrix.normmax -- note: symbolic matrices not supported mtx = matrix{{2,3},{-2,-4}} assert(mtx:normmax() == 4) -mtx = matrix{{'2i','3'},{'-2i','-4i'}}:tocomplex() +mtx = matrix{{'2i','3'},{'-2i','-4i'}}:replace(complex) assert(mtx:normmax() == 4) -- matrix.transpose @@ -187,7 +189,7 @@ assert( m1:rotr() == matrix{{6,4,2},{7,5,3}} ) mtx = matrix{{4,2,-3},{3,-5,2}} assert(tostring(mtx) == "4\t2\t-3\n3\t-5\t2" ) -- matrix.tostring; complex -mtx = matrix{{4,"2+i"},{"3-4i",5}}:tocomplex() +mtx = matrix{{4,"2+i"},{"3-4i",5}}:replace(complex) assert(tostring(mtx) == "4\t2+i\n3-4i\t5" ) -- matrix.tostring; tensor local mt = matrix {{{1,2},{3,4}},{{5,6},{7,8}}} @@ -216,14 +218,14 @@ assert( vx:scalar( v2 ) == 0 ) assert( v2:len() == math.sqrt( 3^2+4^2 ) ) --// test symbolic -ms = matrix {{ "a",1 },{2,"b"}}:tosymbol() -ms2 = matrix {{ "a",2 },{"b",3}}:tosymbol() +ms = matrix {{ "a",1 },{2,"b"}}:replace(symbol) +ms2 = matrix {{ "a",2 },{"b",3}}:replace(symbol) ms3 = ms2+ms -ms3 = ms3:replace( "a",4,"b",2 ) +ms3 = ms3:replace( symbol.makereplacer( "a",4,"b",2 ) ) ms3 = ms3:solve() assert( ms3 == matrix {{8,3},{4,5}} ) ms4 = ms2*ms -ms4 = ms4:replace( "a",4,"b",2 ) +ms4 = ms4:replace( symbol.makereplacer( "a",4,"b",2 ) ) ms4 = ms4:solve() assert( ms4 == matrix {{20,8},{14,8}} ) @@ -255,4 +257,6 @@ end table.sort( t ) for i,v in ipairs( t ) do --print( "matrix."..v ) -end \ No newline at end of file +end + +print 'PASSED'