/* * Copyright (c) 2002-2006 Samit Basu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include "Core.hpp" #include "Exception.hpp" #include "Array.hpp" #include "Math.hpp" #include "Malloc.hpp" #include "SingularValueDecompose.hpp" #include "QRDecompose.hpp" #include "System.hpp" #include "Sparse.hpp" #include "LUDecompose.hpp" #include "Class.hpp" #include "Print.hpp" #include "MemPtr.hpp" #include "Parser.hpp" #include #undef max #undef min //! //@Module DISP Display a Variable or Expression //@@Section IO //@@Usage //Displays the result of a set of expressions. The @|disp| function //takes a variable number of arguments, each of which is an expression //to output: //@[ // disp(expr1,expr2,...,exprn) //@] //This is functionally equivalent to evaluating each of the expressions //without a semicolon after each. //@@Example //Here are some simple examples of using @|disp|. //@< //a = 32; //b = 1:4; //disp(a,b,pi) //@> //! ArrayVector DispFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { int length; Array C; ArrayVector retval; length = arg.size(); for (int i=0;igetPrintLimit(),eval); } // retval.push_back(C); return retval; } //! //@Module SPARSE Construct a Sparse Matrix //@@Section SPARSE //@@Usage //Creates a sparse matrix using one of several formats. The //first creates a sparse matrix from a full matrix //@[ // y = sparse(x). //@] //The second form creates a sparse matrix containing all zeros //that is of the specified size (the sparse equivalent of //@|zeros|). //@[ // y = sparse(m,n) //@] //where @|m| and @|n| are integers. Just like the @|zeros| function, //the sparse matrix returned is of type @|float|. The third form //constructs a sparse matrix from the IJV syntax. It has two forms. The //first version autosizes the sparse matrix //@[ // y = sparse(i,j,v) //@] //while the second version uses an explicit size specification //@[ // y = sparse(i,j,v,m,n) //@] //@@Tests //@{ test_sparse1.m //function x = test_sparse1 //a = [0,0,2,0;0,0,1,1;1,0,0,0]; //A = sparse(a); //b = float(a); //B = sparse(b); //c = double(a); //C = sparse(c); //d = complex(a + i*a); //D = sparse(d); //e = dcomplex(d); //E = sparse(e); //f = complex(a); //F = complex(A); //g = dcomplex(b); //G = dcomplex(B); //h = float(d); //H = float(D); //x = testeq(a,A) & testeq(b,B) & testeq(c,C) & testeq(d,D) & testeq(e,E) & testeq(f,F) & testeq(g,G) & testeq(h,H); //@} //@{ test_sparse2.m //function x = test_sparse2 //a = [0,0,2,0;0,0,1,1;1,0,0,0]; //b = [4,5,0,3]; //c = [0,0]; //d = [2,3]; //A = sparse(a); //B = sparse(b); //C = sparse(c); //D = sparse(d); //j = [a;c,d;b]; //J = [A;C,D;B]; //x = testeq(j,J); //@} //@{ test_sparse3.m //function x = test_sparse3 //a = float([0,0,2,0;0,0,1,1;1,0,0,0]); //b = float([4,5,0,3]); //c = [0,0]; //d = [2,3]; //A = sparse(a); //B = sparse(b); //C = sparse(c); //D = sparse(d); //j = [a;c,d;b]; //J = [A;C,D;B]; //x = testeq(j,J); //@} //@{ test_sparse4.m //function x = test_sparse4 //a = double([0,0,2,0;0,0,1,1;1,0,0,0]); //b = [4,5,0,3]; //c = double([0,0]); //d = [2,3]; //A = sparse(a); //B = sparse(b); //C = sparse(c); //D = sparse(d); //j = [a;c,d;b]; //J = [A;C,D;B]; //x = testeq(j,J); //@} //@{ test_sparse5.m //function x = test_sparse5 //a = complex([0,0,2,0;0,0,1,1;1,0,0,0] + i*[3,0,3,0;0,0,1,0;0,3,5,2]); //b = float([4,5,0,3]); //c = float([0,0]); //d = float([2+5*i,3]); //A = sparse(a); //B = sparse(b); //C = sparse(c); //D = sparse(d); //j = [a;c,d;b]; //J = [A;C,D;B]; //x = testeq(j,J); //@} //@{ test_sparse6.m //function x = test_sparse6 //a = dcomplex([0,0,2,0;0,0,1,1;1,0,0,0] + i*[3,0,3,0;0,0,1,0;0,3,5,2]); //b = float([4,5,0,3]); //c = double([0,0]); //d = float([2+5*i,3]); //A = sparse(a); //B = sparse(b); //C = sparse(c); //D = sparse(d); //j = [a;c,d;b]; //J = [A;C,D;B]; //x = testeq(j,J); //@} //@{ test_sparse7.m //function x = test_sparse7 //a = [0,0,2,0,0;0,1,0,1,0;0,0,0,1,3]; //A = sparse(a); //b = [1,4,5;2,5,15;3,6,11]; //C = A(b); //c = a(b); //x = testeq(c,C); //@} //@{ test_sparse8.m //function x = test_sparse8 //ar = [0,0,2,0,0;0,1,0,1,0;0,0,0,1,0]; //ai = [1,0,3,0,0;0,2,3,-1,3;0,1,0,4,3]; //a = complex(ar+i*ai); //A = sparse(a); //b = [1,4,5;2,5,15;3,6,11]; //C = A(b); //c = a(b); //x = testeq(c,C); //@} //@{ test_sparse9.m //function x = test_sparse9 //ar = [0,0,2,0,0;0,1,0,1,0;0,0,0,1,0]; //ai = [1,0,3,0,0;0,2,3,-1,3;0,1,0,4,3]; //a = dcomplex(ar+i*ai); //A = sparse(a); //b = [1,4,5;2,5,15;3,6,11]; //C = A(b); //c = a(b); //x = testeq(c,C); //@} //@{ test_sparse10.m //function x = test_sparse10 //a = int32(10*rand(8)); //a(a<7) = 0; //A = sparse(a); //b = [5;2;3;3;1]; //c = [1,4,6,8]; //A = sparse(a); //C = A(b,c); //c = a(b,c); //x = testeq(c,C); //@} //@{ test_sparse11.m //function x = test_sparse11 //a = float(10*rand(8)); //a(a<7) = 0; //A = sparse(a); //b = [5;2;3;3;1]; //c = [1,4,6,8]; //A = sparse(a); //C = A(b,c); //c = a(b,c); //x = testeq(c,C); //@} //@{ test_sparse12.m //function x = test_sparse12 //a = double(10*rand(8)); //a(a<7) = 0; //A = sparse(a); //b = [5;2;3;3;1]; //c = [1,4,6,8]; //A = sparse(a); //C = A(b,c); //c = a(b,c); //x = testeq(c,C); //@} //@{ test_sparse13.m //function x = test_sparse13 //ai = int32(10*rand(8)); //ar = int32(10*rand(8)); //ai(ai<7) = 0; //ar(ar<7) = 0; //a = complex(ar+i*ai); //A = sparse(a); //b = [5;2;3;3;1]; //d = [1,4,6,8]; //A = sparse(a); //C = A(b,d); //c = a(b,d); //x = testeq(c,C); //@} //@{ test_sparse14.m //function x = test_sparse14 //ai = int32(10*rand(8)); //ar = int32(10*rand(8)); //ai(ai<7) = 0; //ar(ar<7) = 0; //a = dcomplex(ar+i*ai); //A = sparse(a); //b = [5;2;3;3;1]; //c = [1,4,6,8]; //A = sparse(a); //C = A(b,c); //c = a(b,c); //x = testeq(c,C); //@} //@{ test_sparse15.m //function x = test_sparse15 //a = [1,2,0,0,4;3,2,0,0,5;0,0,3,0,2]; //A = sparse(a); //c = a(2,5); //C = A(2,5); //x = testeq(c,C); //@} //@{ test_sparse16.m //function x = test_sparse16 //a = float([1,2,0,0,4;3,2,0,0,5;0,0,3,0,2]); //A = sparse(a); //c = a(2,5); //C = A(2,5); //x = testeq(c,C); //@} //@{ test_sparse17.m //function x = test_sparse17 //a = double([1,2,0,0,4;3,2,0,0,5;0,0,3,0,2]); //A = sparse(a); //c = a(2,5); //C = A(2,5); //x = testeq(c,C); //@} //@{ test_sparse18.m //function x = test_sparse18 //ai = [0,2,6,0,1;3,0,3,0,2;0,0,3,0,2]; //ar = [1,2,0,0,4;3,2,0,0,5;0,0,3,0,2]; //a = complex(ar+i*ai); //A = sparse(a); //c = a(2,5); //C = A(2,5); //x = testeq(c,C); //@} //@{ test_sparse19.m //function x = test_sparse19 //ai = [0,2,6,0,1;3,0,3,0,2;0,0,3,0,2]; //ar = [1,2,0,0,4;3,2,0,0,5;0,0,3,0,2]; //a = dcomplex(ar+i*ai); //A = sparse(a); //c = a(2,5); //C = A(2,5); //x = testeq(c,C); //@} //@{ test_sparse22.m //function x = test_sparse22 //a = randn(1000); //A = sparse(a); //rc = int32(999*rand(7000,1))+1; //cc = int32(999*rand(7000,1))+1; //lc = rc+1000*(cc-1); //b = a(lc); //B = A(lc); //x = testeq(b,B); //@} //@{ test_sparse23.m //function x = test_sparse23 //N = 1000; //M = 7000; //a = double(40*randn(N)); //A = sparse(a); //rc = int32((N-1)*rand(M,1))+1; //cc = int32((N-1)*rand(M,1))+1; //lc = rc+N*(cc-1); //b = a(lc); //B = A(lc); //x = testeq(b,B); //@} //@{ test_sparse24.m //function x = test_sparse24 //N = 10; //M = 70; //a = double(40*randn(N)); //A = sparse(a); //rc = int32((N-1)*rand(M,1))+1; //cc = int32((N-1)*rand(M,1))+1; //lc = rc+N*(cc-1); //b = a(lc); //B = A(lc); //x = testeq(b,B); //@} //@{ test_sparse25.m //function x = test_sparse25 //N = 1000; //M = 7000; //a = complex(40*randn(N)+32*randn(N)); //A = sparse(a); //rc = int32((N-1)*rand(M,1))+1; //cc = int32((N-1)*rand(M,1))+1; //lc = rc+N*(cc-1); //b = a(lc); //B = A(lc); //x = testeq(b,B); //@} //@{ test_sparse26.m //function x = test_sparse26 //N = 1000; //M = 7000; //a = dcomplex(40*randn(N)+32*randn(N)); //A = sparse(a); //rc = int32((N-1)*rand(M,1))+1; //cc = int32((N-1)*rand(M,1))+1; //lc = rc+N*(cc-1); //b = a(lc); //B = A(lc); //x = testeq(b,B); //@} //@{ test_sparse27.m //function x = test_sparse27 //a = [1,0,3,4,5;6,2,3,5,0;0,0,0,0,2]; //A = sparse(a); //p = [3;4;5;9;10]; //a(p) = 7; //A(p) = 7; //x = testeq(a,A); //@} //@{ test_sparse28.m //function x = test_sparse28 //a = [1,0,3,4,5;6,2,3,5,0;0,0,0,0,2]; //A = sparse(a); //p = [3;4;5;9;10]; //a(p) = [4,6,8,0,3]; //A(p) = [4,6,8,0,3]; //x = testeq(a,A); //@} //@{ test_sparse29.m //function x = test_sparse29 //a = [1,0,3,4,5;6,2,3,5,0;0,0,0,0,2]; //A = sparse(a); //p = [3;4;5;9;10;20]; //a(p) = [4,6,8,0,3,7]; //A(p) = [4,6,8,0,3,7]; //x = testeq(a',A); //@} //@{ test_sparse30.m //function x = test_sparse30 //ar = [1,0,3,4,5;6,2,3,5,0;0,0,0,0,2]; //ai = [0,0,2,0,4;1,0,2,3,0;1,0,0,0,3]; //a = complex(ar+ai*i); //A = sparse(a); //p = [3;4;5;9;10]; //a(p) = complex(7+2*i); //A(p) = complex(7+2*i); //x = testeq(a,A); //@} //@{ test_sparse31.m //function x = test_sparse31 //a = [1,0,3,4,5;6,2,3,5,0;0,0,0,0,2;0,4,5,0,3]; //A = sparse(a); //b = [3;2]; //c = [2,4,3]; //d = [9,3,2,4,9,0]; //a(b,c) = d; //A(b,c) = d; //x = testeq(a,A); //@} //@{ test_sparse32.m //function x = test_sparse32 //a = [1,0,3,4,5;6,2,3,5,0;0,0,0,0,2;0,4,5,0,3]; //A = sparse(a); //b = [3;2;5]; //c = [2,4,3]; //d = [9,3,2,4,9,0,5,4,2]; //a(b,c) = d; //A(b,c) = d; //x = testeq(a,A); //@} //@{ test_sparse33.m //function x = test_sparse33 //ar = [1,0,3,4,5;6,2,3,5,0;0,0,0,0,2;0,4,5,0,3]; //ai = [0,2,9,0,2;0,0,0,2,0;1,3,4,0,2;1,0,0,0,2]; //a = complex(ar+ai); //A = sparse(a); //b = [3;2;5]; //c = [2,4,3]; //d = complex([9,3,2,4,9,0,5,4,2] + 3*i); //a(b,c) = d; //A(b,c) = d; //x = testeq(a,A); //@} //@{ test_sparse34.m //function x = test_sparse34 //a = sparse([0;0;2;3;0;0;4;5;0;0;0;2]); //n = [7,9,1,3,2,3,1,2,2]; //b = full(a); //a(n) = []; //b(n) = []; //x = testeq(a,b); //@} //@{ test_sparse35.m //function x = test_sparse35 //s = [0,3,4,5;0,0,0,0;0,2,3,0]; //a = sparse(s); //x = testeq(full(a),s) & testeq(full(float(a)),s) & testeq(full(double(a)),s) & testeq(full(complex(a)),s) & testeq(full(dcomplex(a)),s); //@} //@{ test_sparse36.m //function x = test_sparse36 //a = rand(8) + i*rand(8); //A = sparse(a); //B = float(A); //b = full(B); //x = testeq(b,B); //@} //@{ sparse_test_mat.m //function [y,z] = sparse_test_mat(typ, rows, cols) // if (~exist('cols')) cols = rows; end // a = rand(rows,cols); // a(a>0.1) = 0; // b = rand(rows,cols); // b(b>0.1) = 0; // switch(typ) // case 'int32' // z = (int32(100*a)); // case 'float' // z = (float(a)); // case 'double' // z = (double(a)); // case 'complex' // z = (float(a) + i*float(b)); // case 'dcomplex' // z = (a + i*b); // end // y = sparse(z); //@} //@{ test_sparse62.m //% Test sparse matrix array constructor //function x = test_sparse62 //[yi1,zi1] = sparse_test_mat('int32',300,400); //[yf1,zf1] = sparse_test_mat('float',300,400); //[yd1,zd1] = sparse_test_mat('double',300,400); //[yc1,zc1] = sparse_test_mat('complex',300,400); //[yz1,zz1] = sparse_test_mat('dcomplex',300,400); //[yi2,zi2] = sparse_test_mat('int32',300,450); //[yf2,zf2] = sparse_test_mat('float',300,450); //[yd2,zd2] = sparse_test_mat('double',300,450); //[yc2,zc2] = sparse_test_mat('complex',300,450); //[yz2,zz2] = sparse_test_mat('dcomplex',300,450); //[yi3,zi3] = sparse_test_mat('int32',30,400); //[yf3,zf3] = sparse_test_mat('float',30,400); //[yd3,zd3] = sparse_test_mat('double',30,400); //[yc3,zc3] = sparse_test_mat('complex',30,400); //[yz3,zz3] = sparse_test_mat('dcomplex',30,400); //[yi4,zi4] = sparse_test_mat('int32',30,450); //[yf4,zf4] = sparse_test_mat('float',30,450); //[yd4,zd4] = sparse_test_mat('double',30,450); //[yc4,zc4] = sparse_test_mat('complex',30,450); //[yz4,zz4] = sparse_test_mat('dcomplex',30,450); //a1 = [yi1,yi2;yi3,yi4]; //b1 = [zi1,zi2;zi3,zi4]; //a2 = [yf1,yf2;yf3,yf4]; //b2 = [zf1,zf2;zf3,zf4]; //a3 = [yd1,yd2;yd3,yd4]; //b3 = [zd1,zd2;zd3,zd4]; //a4 = [yc1,yc2;yc3,yc4]; //b4 = [zc1,zc2;zc3,zc4]; //a5 = [yz1,yz2;yz3,yz4]; //b5 = [zz1,zz2;zz3,zz4]; //x = testeq(a1,b1) & testeq(a2,b2) & testeq(a3,b3) & testeq(a4,b4) & testeq(a5,b5); //@} //! ArrayVector SparseFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { if (arg.size() == 1) { Array r(arg[0]); if ((r.dataClass() != FM_LOGICAL) && (r.dataClass() < FM_INT32)) r.promoteType(FM_INT32); r.makeSparse(); return singleArrayVector(r); } else if (arg.size() == 2) { Array m_arg(arg[0]); Array n_arg(arg[1]); int m, n; m = m_arg.getContentsAsIntegerScalar(); n = n_arg.getContentsAsIntegerScalar(); Dimensions dim(m,n); return singleArrayVector(Array(FM_FLOAT,dim,SparseFloatZeros(m,n),true)); } else if (arg.size() == 3) { Array i_arg(arg[0]); Array j_arg(arg[1]); Array v_arg(arg[2]); i_arg.promoteType(FM_UINT32); j_arg.promoteType(FM_UINT32); if (v_arg.dataClass() != FM_LOGICAL && v_arg.dataClass() < FM_INT32) v_arg.promoteType(FM_INT32); int ilen, jlen, vlen; ilen = i_arg.getLength(); jlen = j_arg.getLength(); vlen = v_arg.getLength(); int olen; olen = ilen > jlen ? ilen : jlen; olen = vlen > olen ? vlen : olen; int istride, jstride, vstride; if (olen > 1) { if (ilen == 1) istride = 0; else if (ilen == olen) istride = 1; else throw Exception("in I, J, V format, all three vectors must be the same size or be scalars"); if (jlen == 1) jstride = 0; else if (jlen == olen) jstride = 1; else throw Exception("in I, J, V format, all three vectors must be the same size or be scalars"); if (vlen == 1) vstride = 0; else if (vlen == olen) vstride = 1; else throw Exception("in I, J, V format, all three vectors must be the same size or be scalars"); } // Calculate the number of rows in the matrix uint32 *ip = (uint32*) i_arg.getDataPointer(); int rows = 0; for (int i=0;i rows) ? ip[i] : rows; uint32 *jp = (uint32*) j_arg.getDataPointer(); int cols = 0; for (int j=0;j cols) ? jp[j] : cols; Dimensions dim(rows,cols); return singleArrayVector(Array(v_arg.dataClass(),dim, makeSparseFromIJV(v_arg.dataClass(), rows,cols,olen, ip,istride,jp,jstride, v_arg.getDataPointer(), vstride), true)); } else if (arg.size() >= 5) { if (arg.size() > 5) eval->warningMessage("extra arguments to sparse (nnz to reserve) ignored"); Array i_arg(arg[0]); Array j_arg(arg[1]); Array v_arg(arg[2]); i_arg.promoteType(FM_UINT32); j_arg.promoteType(FM_UINT32); if (v_arg.dataClass() != FM_LOGICAL && v_arg.dataClass() < FM_INT32) v_arg.promoteType(FM_INT32); int ilen, jlen, vlen; ilen = i_arg.getLength(); jlen = j_arg.getLength(); vlen = v_arg.getLength(); int olen; olen = ilen > jlen ? ilen : jlen; olen = vlen > olen ? vlen : olen; int istride, jstride, vstride; if (olen > 1) { if (ilen == 1) istride = 0; else if (ilen == olen) istride = 1; else throw Exception("in I, J, V format, all three vectors must be the same size or be scalars"); if (jlen == 1) jstride = 0; else if (jlen == olen) jstride = 1; else throw Exception("in I, J, V format, all three vectors must be the same size or be scalars"); if (vlen == 1) vstride = 0; else if (vlen == olen) vstride = 1; else throw Exception("in I, J, V format, all three vectors must be the same size or be scalars"); } Array m_arg(arg[3]); Array n_arg(arg[4]); int rows = m_arg.getContentsAsIntegerScalar(); int cols = n_arg.getContentsAsIntegerScalar(); Dimensions dim(rows,cols); uint32 *ip = (uint32*) i_arg.getDataPointer(); uint32 *jp = (uint32*) j_arg.getDataPointer(); return singleArrayVector(Array(v_arg.dataClass(),dim, makeSparseFromIJV(v_arg.dataClass(), rows,cols,olen, ip,istride,jp,jstride, v_arg.getDataPointer(), vstride), true)); } throw Exception("unrecognized form of sparse - see help for the allowed forms of sparse"); } //! //@Module INV Invert Matrix //@@Section TRANSFORMS //@@Usage //Inverts the argument matrix, provided it is square and invertible. //The syntax for its use is //@[ // y = inv(x) //@] //Internally, the @|inv| function uses the matrix divide operators. //For sparse matrices, a sparse matrix solver is used. //@@Example //Here we invert some simple matrices //@< //a = randi(zeros(3),5*ones(3)) //b = inv(a) //a*b //b*a //@> //! ArrayVector InvFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { if (arg.size() != 1) throw Exception("inv function needs at least one argument"); Array r(arg[0]); return singleArrayVector(InvertMatrix(r,eval)); } //! //@Module FULL Convert Sparse Matrix to Full Matrix //@@Section SPARSE //@@Usage //Converts a sparse matrix to a full matrix. The syntax for //its use is //@[ // y = full(x) //@] //The type of @|x| is preserved. Be careful with the function. //As a general rule of thumb, if you can work with the @|full| //representation of a function, you probably do not need the //sparse representation. //@@Example //Here we convert a full matrix to a sparse one, and back again. //@< //a = [1,0,4,2,0;0,0,0,0,0;0,1,0,0,2] //A = sparse(a) //full(A) //@> //@@Tests //@{ test_sparse57.m //% Test makeDenseArray function //function x = test_sparse57 //[yi,zi] = sparse_test_mat('int32',400); //[yf,zf] = sparse_test_mat('float',400); //[yd,zd] = sparse_test_mat('double',400); //[yc,zc] = sparse_test_mat('complex',400); //[yz,zz] = sparse_test_mat('dcomplex',400); //x = testeq(full(yi),zi) & testeq(full(yf),zf) & testeq(full(yd),zd) & testeq(full(yc),zc) & testeq(full(yz),zz); //@} //! ArrayVector FullFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 1) throw Exception("Need one argument to full function"); Array r(arg[0]); r.makeDense(); return singleArrayVector(r); } //! //@Module LU LU Decomposition for Matrices //@@Section TRANSFORMS //@@Usage //Computes the LU decomposition for a matrix. The form of the //command depends on the type of the argument. For full (non-sparse) //matrices, the primary form for @|lu| is //@[ // [L,U,P] = lu(A), //@] //where @|L| is lower triangular, @|U| is upper triangular, and //@|P| is a permutation matrix such that @|L*U = P*A|. The second form is //@[ // [V,U] = lu(A), //@] //where @|V| is @|P'*L| (a row-permuted lower triangular matrix), //and @|U| is upper triangular. For sparse, square matrices, //the LU decomposition has the following form: //@[ // [L,U,P,Q,R] = lu(A), //@] //where @|A| is a sparse matrix of either @|double| or @|dcomplex| type. //The matrices are such that @|L*U=P*R*A*Q|, where @|L| is a lower triangular //matrix, @|U| is upper triangular, @|P| and @|Q| are permutation vectors //and @|R| is a diagonal matrix of row scaling factors. The decomposition // is computed using UMFPACK for sparse matrices, and LAPACK for dense // matrices. //@@Example //First, we compute the LU decomposition of a dense matrix. //@< //a = float([1,2,3;4,5,8;10,12,3]) //[l,u,p] = lu(a) //l*u //p*a //@> //Now we repeat the exercise with a sparse matrix, and demonstrate //the use of the permutation vectors. //@< //a = sparse([1,0,0,4;3,2,0,0;0,0,0,1;4,3,2,4]) //[l,u,p,q,r] = lu(a) //full(l*a) //b = r*a //full(b(p,q)) //@> //@@Tests //@{ test_lu1.m //% Test the LU decomposition with 2 argument return for full matrices //function x = test_lu1 //A = float(randn(20)); //[L,U] = lu(A); //x = testeq(100,L*U,A); //A = double(randn(20)); //[L,U] = lu(A); //x = x & testeq(100,L*U,A); //A = complex(randn(20)+i*randn(20)); //[L,U] = lu(A); //x = x & testeq(100,L*U,A); //A = dcomplex(randn(20)+i*randn(20)); //[L,U] = lu(A); //x = x & testeq(100,L*U,A); // //function x = testeq(tmag,a,b) // d = full(a)-full(b); // if (strcmp(typeof(d),'double') | strcmp(typeof(d),'dcomplex')) // x = isempty(find(abs(d)>tmag*eps)); // else // x = isempty(find(abs(d)>tmag*feps)); // end //@} //@{ test_lu2.m //% Test the LU decomposition with 3 argument return for full matrices //function x = test_lu2 //A = float(randn(20)); //[L,U,P] = lu(A); //x = testeq(100,L*U,P*A); //A = double(randn(20)); //[L,U,P] = lu(A); //x = x & testeq(100,L*U,P*A); //A = complex(randn(20)+i*randn(20)); //[L,U,P] = lu(A); //x = x & testeq(100,L*U,P*A); //A = dcomplex(randn(20)+i*randn(20)); //[L,U,P] = lu(A); //x = x & testeq(100,L*U,P*A); // //function x = testeq(tmag,a,b) // d = full(a)-full(b); // if (strcmp(typeof(d),'double') | strcmp(typeof(d),'dcomplex')) // x = isempty(find(abs(d)>tmag*eps)); // else // x = isempty(find(abs(d)>tmag*feps)); // end //@} //@{ test_sparse75.m //% Test sparse matrix array finite test //function x = test_sparse75 //[yi1,zi1] = sparse_test_mat('int32',300,400); //[yf1,zf1] = sparse_test_mat('float',300,400); //[yd1,zd1] = sparse_test_mat('double',300,400); //[yc1,zc1] = sparse_test_mat('complex',300,400); //[yz1,zz1] = sparse_test_mat('dcomplex',300,400); //x = 1; //yi1(150,200) = inf; //try // [l,u] = lu(yi1); // x = 0; //catch // x = x & 1; //end //try // [l,u] = lu(yf1); // x = 0; //catch // x = x & 1; //end //try // [l,u] = lu(yd1); // x = 0; //catch // x = x & 1; //end //try // [l,u] = lu(yc1); // x = 0; //catch // x = x & 1; //end //try // [l,u] = lu(yz1); // x = 0; //catch // x = x & 1; //end //@} //! ArrayVector LUFunction(int nargout, const ArrayVector& arg) { if (arg.size() < 1) throw Exception("lu function requires at least one argument - the matrix to decompose."); Array A(arg[0]); if (A.isReferenceType()) throw Exception("cannot apply lu decomposition to reference types."); if (!A.is2D()) throw Exception("cannot apply matrix operations to N-dimensional arrays"); if (!A.sparse()) { if (A.anyNotFinite()) throw Exception("lu decomposition only defined for matrices with finite entries."); return LUDecompose(nargout,A); } else { return SparseLUDecompose(nargout,A); } return ArrayVector(); } //! //@Module GETLINE Get a Line of Input from User //@@Section IO //@@Usage //Reads a line (as a string) from the user. This function has //two syntaxes. The first is //@[ // a = getline(prompt) //@] //where @|prompt| is a prompt supplied to the user for the query. //The second syntax omits the @|prompt| argument: //@[ // a = getline //@] //Note that this function requires command line input, i.e., it //will only operate correctly for programs or scripts written //to run inside the FreeMat GUI environment or from the X11 terminal. //If you build a stand-alone application and expect it to operate //cross-platform, do not use this function (unless you include //the FreeMat console in the final application). //! ArrayVector GetLineFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { string prompt; if (arg.size() < 1) prompt = ""; else { Array A(arg[0]); if (!A.isString()) throw Exception("getline requires a string prompt"); prompt = A.getContentsAsString(); } return singleArrayVector(Array::stringConstructor(eval->getLine(prompt))); } ArrayVector GenEigFunction(int nargout, const ArrayVector &arg, Interpreter* m_eval) { Array A(arg[0]); Array B(arg[1]); if (A.sparse() || B.sparse()) throw Exception("eig only defined for full matrices."); if (A.anyNotFinite() || B.anyNotFinite()) throw Exception("eig only defined for matrices with finite entries."); if (A.isReferenceType() || B.isReferenceType()) throw Exception("Cannot apply eigendecomposition to reference types."); if (!A.is2D() || !B.is2D()) throw Exception("Cannot apply eigendecomposition to N-Dimensional arrays."); if (A.getDimensionLength(0) != A.getDimensionLength(1)) throw Exception("Cannot eigendecompose a non-square matrix."); if (B.getDimensionLength(0) != B.getDimensionLength(1)) throw Exception("Cannot eigendecompose a non-square matrix."); if (A.getDimensionLength(0) != B.getDimensionLength(0)) throw Exception("B and A must be the same size when computing a generalized eigendecomposition"); // Handle the type of A - if it is an integer type, then promote to double Class Aclass = A.dataClass(); if (Aclass < FM_FLOAT) { A.promoteType(FM_DOUBLE); Aclass = FM_DOUBLE; } Class Bclass = B.dataClass(); if (Bclass < Aclass) { B.promoteType(Aclass); Bclass = Aclass; } else { A.promoteType(Bclass); Aclass = Bclass; } ArrayVector retval; Array V, D; if (nargout > 1) { if (A.isSymmetric() && B.isSymmetric()) { if (!GeneralizedEigenDecomposeFullSymmetric(A,B,V,D,m_eval)) GeneralizedEigenDecomposeFullGeneral(A,B,V,D,m_eval); } else GeneralizedEigenDecomposeFullGeneral(A,B,V,D,m_eval); retval.push_back(V); retval.push_back(D); } else { if (A.isSymmetric() && B.isSymmetric()) { if (!GeneralizedEigenDecomposeCompactSymmetric(A,B,D,m_eval)) GeneralizedEigenDecomposeCompactGeneral(A,B,D,m_eval); } else GeneralizedEigenDecomposeCompactGeneral(A,B,D,m_eval); retval.push_back(D); } return retval; } //! //@Module EIG Eigendecomposition of a Matrix //@@Section TRANSFORMS //@@Usage //Computes the eigendecomposition of a square matrix. The @|eig| function //has several forms. The first returns only the eigenvalues of the matrix: //@[ // s = eig(A) //@] //The second form returns both the eigenvectors and eigenvalues as two //matrices (the eigenvalues are stored in a diagonal matrix): //@[ // [V,D] = eig(A) //@] //where @|D| is the diagonal matrix of eigenvalues, and @|V| is the //matrix of eigenvectors. // //Eigenvalues and eigenvectors for asymmetric matrices @|A| normally //are computed with balancing applied. Balancing is a scaling step //that normaly improves the quality of the eigenvalues and eigenvectors. //In some instances (see the Function Internals section for more details) //it is necessary to disable balancing. For these cases, two additional //forms of @|eig| are available: //@[ // s = eig(A,'nobalance'), //@] //which computes the eigenvalues of @|A| only, and does not balance //the matrix prior to computation. Similarly, //@[ // [V,D] = eig(A,'nobalance') //@] //recovers both the eigenvectors and eigenvalues of @|A| without balancing. //Note that the 'nobalance' option has no affect on symmetric matrices. // //FreeMat also provides the ability to calculate generalized eigenvalues //and eigenvectors. Similarly to the regular case, there are two forms //for @|eig| when computing generalized eigenvector (see the Function //Internals section for a description of what a generalized eigenvector is). //The first returns only the generalized eigenvalues of the matrix //pair @|A,B| //@[ // s = eig(A,B) //@] //The second form also computes the generalized eigenvectors, and is //accessible via //@[ // [V,D] = eig(A,B) //@] //@@Function Internals //Recall that @|v| is an eigenvector of @|A| with associated eigenvalue //@|d| if //\[ // A v = d v. //\] //This decomposition can be written in matrix form as //\[ // A V = V D //\] //where //\[ // V = [v_1,v_2,\ldots,v_n], D = \mathrm{diag}(d_1,d_2,\ldots,d_n). //\] //The @|eig| function uses the @|LAPACK| class of functions @|GEEVX| //to compute the eigenvalue decomposition for non-symmetric //(or non-Hermitian) matrices @|A|. For symmetric matrices, @|SSYEV| // and @|DSYEV| are used for @|float| and @|double| matrices (respectively). //For Hermitian matrices, @|CHEEV| and @|ZHEEV| are used for @|complex| //and @|dcomplex| matrices. // //For some matrices, the process of balancing (in which the rows and //columns of the matrix are pre-scaled to facilitate the search for //eigenvalues) is detrimental to the quality of the final solution. //This is particularly true if the matrix contains some elements on //the order of round off error. See the Example section for an example. // //A generalized eigenvector of the matrix pair @|A,B| is simply a //vector @|v| with associated eigenvalue @|d| such that //\[ // A v = d B v, //\] //where @|B| is a square matrix of the same size as @|A|. This //decomposition can be written in matrix form as //\[ // A V = B V D //\] //where //\[ // V = [v_1,v_2,\ldots,v_n], D = \mathrm{diag}(d_1,d_2,\ldots,d_n). //\] //For general matrices @|A| and @|B|, the @|GGEV| class of routines are //used to compute the generalized eigendecomposition. If howevever, //@|A| and @|B| are both symmetric (or Hermitian, as appropriate), //Then FreeMat first attempts to use @|SSYGV| and @|DSYGV| for @|float| //and @|double| arguments and @|CHEGV| and @|ZHEGV| for @|complex| //and @|dcomplex| arguments (respectively). These routines requires //that @|B| also be positive definite, and if it fails to be, FreeMat //will revert to the routines used for general arguments. //@@Example //Some examples of eigenvalue decompositions. First, for a diagonal //matrix, the eigenvalues are the diagonal elements of the matrix. //@< //A = diag([1.02f,3.04f,1.53f]) //eig(A) //@> //Next, we compute the eigenvalues of an upper triangular matrix, //where the eigenvalues are again the diagonal elements. //@< //A = [1.0f,3.0f,4.0f;0,2.0f,6.7f;0.0f,0.0f,1.0f] //eig(A) //@> //Next, we compute the complete eigenvalue decomposition of //a random matrix, and then demonstrate the accuracy of the solution //@< //A = float(randn(2)) //[V,D] = eig(A) //A*V - V*D //@> //Now, we consider a matrix that requires the nobalance option //to compute the eigenvalues and eigenvectors properly. Here is //an example from MATLAB's manual. //@< //B = [3,-2,-.9,2*eps;-2,4,1,-eps;-eps/4,eps/2,-1,0;-.5,-.5,.1,1] //[VB,DB] = eig(B) //B*VB - VB*DB //[VN,DN] = eig(B,'nobalance') //B*VN - VN*DN //@> //@@Tests //@{ test_eig1.m //% Test eigenvalue function - general matrices //function t = test_eig1 //% First the float version //t1all = 1; //for i=2:4:100 // a = float(randn(i)); // [v,d] = eig(a); // emat = a*v - v*d; // er = max(abs(emat(:))); // bnd = 100*max(diag(abs(d)))*feps*i; // t1 = (er < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',er,bnd,i); end // g = eig(a); // e2 = max(abs(sort(g)-sort(diag(d)))); // tb = e2 1) { if (A.isSymmetric()) EigenDecomposeFullSymmetric(A,V,D, m_eval); else EigenDecomposeFullGeneral(A,V,D,balance, m_eval); retval.push_back(V); retval.push_back(D); } else { if (A.isSymmetric()) EigenDecomposeCompactSymmetric(A,D, m_eval); else EigenDecomposeCompactGeneral(A,D,balance, m_eval); retval.push_back(D); } return retval; } //! //@Module EIGS Sparse Matrix Eigendecomposition //@@Section SPARSE //@@Usage //Computes the eigendecomsition of a sparse square matrix. The //@|eigs| function has several forms. The most general form is //@[ // [V,D] = eigs(A,k,sigma) //@] //where @|A| is the matrix to analyze, @|k| is the number of //eigenvalues to compute and @|sigma| determines which eigenvallues //to solve for. Valid values for @|sigma| are // 'lm' - largest magnitude // 'sm' - smallest magnitude // 'la' - largest algebraic (for real symmetric problems) // 'sa' - smallest algebraic (for real symmetric problems) // 'be' - both ends (for real symmetric problems) // 'lr' - largest real part // 'sr' - smallest real part // 'li' - largest imaginary part // 'si' - smallest imaginary part // scalar - find the eigenvalues closest to @|sigma|. //The returned matrix @|V| contains the eigenvectors, and @|D| //stores the eigenvalues. The related form //@[ // d = eigs(A,k,sigma) //@] //computes only the eigenvalues and not the eigenvectors. If @|sigma| //is omitted, as in the forms //@[ // [V,D] = eigs(A,k) //@] //and //@[ // d = eigs(A,k) //@] //then @|eigs| returns the largest magnitude eigenvalues (and optionally //the associated eigenvectors). As an even simpler form, the forms //@[ // [V,D] = eigs(A) //@] //and //@[ // d = eigs(A) //@] //then @|eigs| returns the six largest magnitude eigenvalues of @|A| and //optionally the eigenvectors. The @|eigs| function uses ARPACK to //compute the eigenvectors and/or eigenvalues. Note that due to a //limitation in the interface into ARPACK from FreeMat, the number of //eigenvalues that are to be computed must be strictly smaller than the //number of columns (or rows) in the matrix. //@@Example //Here is an example of using @|eigs| to calculate eigenvalues //of a matrix, and a comparison of the results with @|eig| //@< //a = sparse(rand(9)) //eigs(a) //eig(full(a)) //@> //Next, we exercise some of the variants of @|eigs|: //@< //eigs(a,4,'sm') //eigs(a,4,'lr') //eigs(a,4,'sr') //@> //@@Tests //@{ test_sparse45.m //function x = test_sparse45 //a = rand(10); //A = sparse(a); //p = eigs(A,4,0.634); //d = eig(a); //e = min(abs(d*ones(1,4) - ones(10,1)*p.')); //x = max(e) < 100*eps; //@} //! ArrayVector EigsFunction(int nargout, const ArrayVector& arg) { if (arg.size() == 0) throw Exception("eigs function requires at least one argument"); Array A(arg[0]); if (!A.sparse()) throw Exception("eigs only applies to sparse matrix arguments"); int k; if (A.getDimensionLength(0) != A.getDimensionLength(1)) throw Exception("eigs can only be applied to square matrices."); if (arg.size() < 2) { k = 6; if (k >= A.getDimensionLength(0)) k = A.getDimensionLength(0) - 1; } else { Array kval(arg[1]); k = kval.getContentsAsIntegerScalar(); } if (A.isComplex()) A.promoteType(FM_DCOMPLEX); else A.promoteType(FM_DOUBLE); bool shiftFlag; string whichflag; double sigma[2]; if (arg.size() < 3) { shiftFlag = false; whichflag = "LM"; } else { Array S(arg[2]); if (S.isString()) { shiftFlag = false; string stxt = S.getContentsAsStringUpper(); if ((stxt == "LM") || (stxt == "SM") || (stxt == "LA") || (stxt == "SA") || (stxt == "BE") || (stxt == "LR") || (stxt == "SR") || (stxt == "LI") || (stxt == "SI")) whichflag = stxt; else throw Exception("Unrecognized option for sigma - it must be either 'lm', 'sm', 'la', 'sa', 'be', 'lr', 'sr', 'li', or 'si'"); } else { if (!S.isScalar()) throw Exception("shift parameter sigma must be a scalar"); if (S.isComplex()) { S.promoteType(FM_DCOMPLEX); double *cp = (double*) S.getDataPointer(); sigma[0] = cp[0]; sigma[1] = cp[1]; } else { sigma[0] = S.getContentsAsDoubleScalar(); sigma[1] = 0; } shiftFlag = true; } } if (!shiftFlag) return SparseEigDecompose(nargout,A,k,whichflag); else return SparseEigDecomposeShifted(nargout,A,k,sigma); } ArrayVector QRDNoPivotFunction(bool compactDec, Array A) { int nrows = A.getDimensionLength(0); int ncols = A.getDimensionLength(1); int orows = nrows; int ocols = ncols; if ((!compactDec) && (nrows > ncols)) { Dimensions newDim(nrows,nrows); A.resize(newDim); ncols = nrows; } else compactDec = true; Class Aclass(A.dataClass()); if (Aclass < FM_FLOAT) { A.promoteType(FM_DOUBLE); Aclass = FM_DOUBLE; } int minmn = (nrows < ncols) ? nrows : ncols; ArrayVector retval; Array rmat, qmat; Dimensions dim; switch (Aclass) { case FM_FLOAT: { float *q = (float*) Malloc(nrows*minmn*sizeof(float)); float *r = (float*) Malloc(ncols*minmn*sizeof(float)); floatQRD(nrows,ncols,q,r,(float*) A.getReadWriteDataPointer()); if (!compactDec) { float *r2 = (float*) Malloc(orows*ocols*sizeof(float)); memcpy(r2,r,orows*ocols*sizeof(float)); dim.set(0,orows); dim.set(1,ocols); rmat = Array(FM_FLOAT,dim,r2); Free(r); } else { dim.set(0,minmn); dim.set(1,ncols); rmat = Array(FM_FLOAT,dim,r); } dim.set(0,nrows); dim.set(1,minmn); qmat = Array(FM_FLOAT,dim,q); retval.push_back(qmat); retval.push_back(rmat); break; } case FM_DOUBLE: { double *q = (double*) Malloc(nrows*minmn*sizeof(double)); double *r = (double*) Malloc(ncols*minmn*sizeof(double)); doubleQRD(nrows,ncols,q,r,(double*) A.getReadWriteDataPointer()); if (!compactDec) { double *r2 = (double*) Malloc(orows*ocols*sizeof(double)); memcpy(r2,r,orows*ocols*sizeof(double)); dim.set(0,orows); dim.set(1,ocols); rmat = Array(FM_DOUBLE,dim,r2); Free(r); } else { dim.set(0,minmn); dim.set(1,ncols); rmat = Array(FM_DOUBLE,dim,r); } dim.set(0,nrows); dim.set(1,minmn); qmat = Array(FM_DOUBLE,dim,q); retval.push_back(qmat); retval.push_back(rmat); break; } case FM_COMPLEX: { float *q = (float*) Malloc(2*nrows*minmn*sizeof(float)); float *r = (float*) Malloc(2*ncols*minmn*sizeof(float)); complexQRD(nrows,ncols,q,r,(float*) A.getReadWriteDataPointer()); if (!compactDec) { float *r2 = (float*) Malloc(2*orows*ocols*sizeof(float)); memcpy(r2,r,2*orows*ocols*sizeof(float)); dim.set(0,orows); dim.set(1,ocols); rmat = Array(FM_COMPLEX,dim,r2); Free(r); } else { dim.set(0,minmn); dim.set(1,ncols); rmat = Array(FM_COMPLEX,dim,r); } dim.set(0,nrows); dim.set(1,minmn); qmat = Array(FM_COMPLEX,dim,q); retval.push_back(qmat); retval.push_back(rmat); break; } case FM_DCOMPLEX: { double *q = (double*) Malloc(2*nrows*minmn*sizeof(double)); double *r = (double*) Malloc(2*ncols*minmn*sizeof(double)); dcomplexQRD(nrows,ncols,q,r,(double*) A.getReadWriteDataPointer()); if (!compactDec) { double *r2 = (double*) Malloc(2*orows*ocols*sizeof(double)); memcpy(r2,r,2*orows*ocols*sizeof(double)); dim.set(0,orows); dim.set(1,ocols); rmat = Array(FM_DCOMPLEX,dim,r2); Free(r); } else { dim.set(0,minmn); dim.set(1,ncols); rmat = Array(FM_DCOMPLEX,dim,r); } dim.set(0,nrows); dim.set(1,minmn); qmat = Array(FM_DCOMPLEX,dim,q); retval.push_back(qmat); retval.push_back(rmat); break; } } return retval; } ArrayVector QRDPivotFunction(bool compactDec, Array A) { int nrows = A.getDimensionLength(0); int ncols = A.getDimensionLength(1); int orows = nrows; int ocols = ncols; int i; bool compactSav = compactDec; if ((!compactDec) && (nrows > ncols)) { Dimensions newDim(nrows,nrows); A.resize(newDim); ncols = nrows; } else compactDec = true; Class Aclass(A.dataClass()); if (Aclass < FM_FLOAT) { A.promoteType(FM_DOUBLE); Aclass = FM_DOUBLE; } int minmn = (nrows < ncols) ? nrows : ncols; ArrayVector retval; Array rmat, qmat, pmat; Dimensions dim; switch (Aclass) { case FM_FLOAT: { float *q = (float*) Malloc(nrows*minmn*sizeof(float)); float *r = (float*) Malloc(ncols*minmn*sizeof(float)); int *p = (int*) Malloc(ncols*sizeof(int)); floatQRDP(nrows,ncols,q,r,p,(float*) A.getReadWriteDataPointer()); if (!compactDec) { float *r2 = (float*) Malloc(orows*ocols*sizeof(float)); memcpy(r2,r,orows*ocols*sizeof(float)); dim.set(0,orows); dim.set(1,ocols); rmat = Array(FM_FLOAT,dim,r2); Free(r); } else { dim.set(0,minmn); dim.set(1,ncols); rmat = Array(FM_FLOAT,dim,r); } if (!compactSav) { int *p2 = (int*) Malloc(ncols*ncols*sizeof(int)); for (i=0;i N|). For these matrices, the //full decomposition is of a matrix @|Q| of size @|M x M| and //a matrix @|R| of size @|M x N|. The full decomposition is computed //using the same LAPACK routines as the compact decomposition, but //on an augmented matrix @|[a 0]|, where enough columns are added to //form a square matrix. // //Generally, the QR decomposition will not return a matrix @|R| with //diagonal elements in any specific order. The remaining two forms //of the @|qr| command utilize permutations of the columns of @|a| //so that the diagonal elements of @|r| are in decreasing magnitude. //To trigger this form of the decomposition, a third argument is //required, which records the permutation applied to the argument @|a|. //The compact version is //@[ // [q,r,e] = qr(a,0) //@] //where @|e| is an integer vector that describes the permutation of //the columns of @|a| necessary to reorder the diagonal elements of //@|r|. This result is computed using the LAPACK routines @|(s,d)geqp3|. //In the non-compact version of the QR decomposition with pivoting, //@[ // [q,r,e] = qr(a) //@] //the returned matrix @|e| is a permutation matrix, such that //@|q*r*e' = a|. //@@Tests //@{ test_qr1.m //% Test the QR decomposition (without pivoting, minimal decomposition, double) //function test_val = test_qr1 //a = [1,2,3;4,5,6;7,8,9;10,0,5]; //% double precision //[q,r] = qr(a,0); //qorg = q'*q - diag(ones(1,3)); //t1 = (max(abs(qorg(:)))<1e-15); //t2 = ((size(q,1) == 4) & (size(q,2) == 3)); //t3 = ((size(r,1) == 3) & (size(r,2) == 3)); //aorg = q*r - a; //t4 = (max(abs(qorg(:)))<1e-15); //t5 = max(diag(r,-1)) == 0; //t6 = max(diag(r,-2)) == 0; //test_val = test(t1) & test(t2) & test(t3) & test(t4) & test(t5) & test(t6); //@} //@{ test_qr2.m //% Test the QR decomposition (without pivoting, minimal decomposition, float) //function test_val = test_qr2 //a = float([1,2,3;4,5,6;7,8,9;10,0,5]); //% float precision //[q,r] = qr(a,0); //qorg = q'*q - diag(ones(1,3)); //t1 = (max(abs(qorg(:)))<1e-6); //t2 = ((size(q,1) == 4) & (size(q,2) == 3)); //t3 = ((size(r,1) == 3) & (size(r,2) == 3)); //aorg = q*r - a; //t4 = (max(abs(qorg(:)))<1e-6); //t5 = max(diag(r,-1)) == 0; //t6 = max(diag(r,-2)) == 0; //test_val = test(t1) & test(t2) & test(t3) & test(t4) & test(t5) & test(t6); //@} //@{ test_qr3.m //% Test the QR decomposition (without pivoting, minimal decomposition, complex) //function test_val = test_qr3 //a = float([1,2,3;4,5,6;7,8,9;10,0,5]); //a = a + i*(a-1); //% float precision //[q,r] = qr(a,0); //qorg = q'*q - diag(ones(1,3)); //t1 = (max(abs(qorg(:)))<1e-6); //t2 = ((size(q,1) == 4) & (size(q,2) == 3)); //t3 = ((size(r,1) == 3) & (size(r,2) == 3)); //aorg = q*r - a; //t4 = (max(abs(qorg(:)))<1e-6); //t5 = max(diag(r,-1)) == 0; //t6 = max(diag(r,-2)) == 0; //test_val = test(t1) & test(t2) & test(t3) & test(t4) & test(t5) & test(t6); //@} //@{ test_qr4.m //% Test the QR decomposition (without pivoting, minimal decomposition, dcomplex) //function test_val = test_qr4 //a = double([1,2,3;4,5,6;7,8,9;10,0,5]); //a = a + i*(a-1); //% float precision //[q,r] = qr(a,0); //qorg = q'*q - diag(ones(1,3)); //t1 = (max(abs(qorg(:)))<1e-15); //t2 = ((size(q,1) == 4) & (size(q,2) == 3)); //t3 = ((size(r,1) == 3) & (size(r,2) == 3)); //aorg = q*r - a; //t4 = (max(abs(qorg(:)))<1e-15); //t5 = max(diag(r,-1)) == 0; //t6 = max(diag(r,-2)) == 0; //test_val = test(t1) & test(t2) & test(t3) & test(t4) & test(t5) & test(t6); //@} //@{ test_qr5.m //% Test the QR decomposition (without pivoting, double) //function test_val = test_qr5 //a = [1,2,3;4,5,6;7,8,9;10,0,5]; //% double precision //[q,r] = qr(a); //qorg = q'*q - diag(ones(1,4)); //t1 = (max(abs(qorg(:)))<1e-15); //t2 = ((size(q,1) == 4) & (size(q,2) == 4)); //t3 = ((size(r,1) == 4) & (size(r,2) == 3)); //aorg = q*r - a; //t4 = (max(abs(qorg(:)))<1e-15); //t5 = max(diag(r,-1)) == 0; //t6 = max(diag(r,-2)) == 0; //t7 = max(diag(r,-3)) == 0; //test_val = test(t1) & test(t2) & test(t3) & test(t4) & test(t5) & test(t6) & test(t7); //@} //@{ test_qr6.m //% Test the QR decomposition (without pivoting, float) //function test_val = test_qr6 //a = float([1,2,3;4,5,6;7,8,9;10,0,5]); //% float precision //[q,r] = qr(a); //qorg = q'*q - diag(ones(1,4)); //t1 = (max(abs(qorg(:)))<1e-6); //t2 = ((size(q,1) == 4) & (size(q,2) == 4)); //t3 = ((size(r,1) == 4) & (size(r,2) == 3)); //aorg = q*r - a; //t4 = (max(abs(qorg(:)))<1e-6); //t5 = max(diag(r,-1)) == 0; //t6 = max(diag(r,-2)) == 0; //t7 = max(diag(r,-3)) == 0; //test_val = test(t1) & test(t2) & test(t3) & test(t4) & test(t5) & test(t6) & test(t7); //@} //@{ test_qr7.m //% Test the QR decomposition (without pivoting, complex) //function test_val = test_qr7 //a = float([1,2,3;4,5,6;7,8,9;10,0,5]); //a = a + i*(a-1); //% float precision //[q,r] = qr(a); //qorg = q'*q - diag(ones(1,4)); //t1 = (max(abs(qorg(:)))<1e-6); //t2 = ((size(q,1) == 4) & (size(q,2) == 4)); //t3 = ((size(r,1) == 4) & (size(r,2) == 3)); //aorg = q*r - a; //t4 = (max(abs(qorg(:)))<1e-6); //t5 = max(diag(r,-1)) == 0; //t6 = max(diag(r,-2)) == 0; //t7 = max(diag(r,-3)) == 0; //test_val = test(t1) & test(t2) & test(t3) & test(t4) & test(t5) & test(t6) & test(t7); //@} //@{ test_qr8.m //% Test the QR decomposition (without pivoting, dcomplex) //function test_val = test_qr8 //a = double([1,2,3;4,5,6;7,8,9;10,0,5]); //a = a + i*(a-1); //% float precision //[q,r] = qr(a); //qorg = q'*q - diag(ones(1,4)); //t1 = (max(abs(qorg(:)))<1e-15); //t2 = ((size(q,1) == 4) & (size(q,2) == 4)); //t3 = ((size(r,1) == 4) & (size(r,2) == 3)); //aorg = q*r - a; //t4 = (max(abs(qorg(:)))<1e-15); //t5 = max(diag(r,-1)) == 0; //t6 = max(diag(r,-2)) == 0; //t7 = max(diag(r,-3)) == 0; //test_val = test(t1) & test(t2) & test(t3) & test(t4) & test(t5) & test(t6) & test(t7); //@} //@{ test_qr9.m //% Test the QR decomposition (full, without pivoting) //function test_val = test_qr9 // t1all = 1; // for n=2:4:100 // a = float(rand(n,2*n)); // [q,r] = qr(a); // err = abs(a-q*r); // err = max(err(:)); // bnd = 8*max(abs(diag(r)))*feps*(n^(0.5)); // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // t2all = 1; // for n=2:4:100 // a = double(rand(n,2*n)); // [q,r] = qr(a); // err = abs(a-q*r); // err = max(err(:)); // bnd = 8*max(abs(diag(r)))*eps*(n^(0.5)); // t2 = (err < bnd); // if (~t2) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t2all = t2all & t2; // end // t3all = 1; // for n=2:4:100 // a = complex(rand(n,2*n)+i*rand(n,2*n)); // [q,r] = qr(a); // err = abs(a-q*r); // err = max(err(:)); // bnd = 8*max(abs(diag(r)))*feps*(n^(0.5)); // t3 = (err < bnd); // if (~t3) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t3all = t3all & t3; // end // t4all = 1; // for n=2:4:100 // a = dcomplex(rand(n,2*n)+i*rand(n,2*n)); // [q,r] = qr(a); // err = abs(a-q*r); // err = max(err(:)); // bnd = 8*max(abs(diag(r)))*eps*(n^(0.5)); // t4 = (err < bnd); // if (~t4) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t4all = t4all & t4; // end // test_val = t1all & t2all & t3all & t4all; //@} //@{ test_qr10.m //% Test the QR decomposition (compact, without pivoting) //function test_val = test_qr10 // t1all = 1; // for n=2:4:100 // a = float(rand(2*n,n)); // [q,r] = qr(a,0); // err = abs(a-q*r); // err = max(err(:)); // bnd = 10*max(abs(diag(r)))*feps*(n^(0.5)); // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // t2all = 1; // for n=2:4:100 // a = double(rand(2*n,n)); // [q,r] = qr(a,0); // err = abs(a-q*r); // err = max(err(:)); // bnd = 10*max(abs(diag(r)))*eps*(n^(0.5)); // t2 = (err < bnd); // if (~t2) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t2all = t2all & t2; // end // t3all = 1; // for n=2:4:100 // a = complex(rand(2*n,n)+i*rand(2*n,n)); // [q,r] = qr(a,0); // err = abs(a-q*r); // err = max(err(:)); // bnd = 10*max(abs(diag(r)))*feps*(n^(0.5)); // t3 = (err < bnd); // if (~t3) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t3all = t3all & t3; // end // t4all = 1; // for n=2:4:100 // a = dcomplex(rand(2*n,n)+i*rand(2*n,n)); // [q,r] = qr(a,0); // err = abs(a-q*r); // err = max(err(:)); // bnd = 10*max(abs(diag(r)))*eps*(n^(0.5)); // t4 = (err < bnd); // if (~t4) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t4all = t4all & t4; // end // test_val = t1all & t2all & t3all & t4all; //@} //@{ test_qr11.m //% Test the QR decomposition (compact, pivoting) //function test_val = test_qr11 // t1all = 1; // for n=2:4:100 // a = float(rand(2*n,n)); // [q,r,ec] = qr(a,0); // e = zeros(n,n); // p = 0:(n-1); // mdx = ec + p*n; // e(mdx) = 1; // err = abs(a-q*r*e'); // err = max(err(:)); // bnd = 10*max(abs(diag(r)))*feps*(n^(0.5)); // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // t2all = 1; // for n=2:4:100 // a = double(rand(2*n,n)); // [q,r,ec] = qr(a,0); // e = zeros(n,n); // p = 0:(n-1); // mdx = ec + p*n; // e(mdx) = 1; // err = abs(a-q*r*e'); // err = max(err(:)); // bnd = 10*max(abs(diag(r)))*eps*(n^(0.5)); // t2 = (err < bnd); // if (~t2) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t2all = t2all & t2; // end // t3all = 1; // for n=2:4:100 // a = complex(rand(2*n,n)+i*rand(2*n,n)); // [q,r,ec] = qr(a,0); // e = zeros(n,n); // p = 0:(n-1); // mdx = ec + p*n; // e(mdx) = 1; // err = abs(a-q*r*e'); // err = max(err(:)); // bnd = 10*max(abs(diag(r)))*feps*(n^(0.5)); // t3 = (err < bnd); // if (~t3) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t3all = t3all & t3; // end // t4all = 1; // for n=2:4:100 // a = dcomplex(rand(2*n,n)+i*rand(2*n,n)); // [q,r,ec] = qr(a,0); // e = zeros(n,n); // p = 0:(n-1); // mdx = ec + p*n; // e(mdx) = 1; // err = abs(a-q*r*e'); // err = max(err(:)); // bnd = 10*max(abs(diag(r)))*eps*(n^(0.5)); // t4 = (err < bnd); // if (~t4) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t4all = t4all & t4; // end // test_val = t1all & t2all & t3all & t4all; //@} //@{ test_qr12.m //% Test the QR decomposition (full, pivoting) //function test_val = test_qr12 // t1all = 1; // for n=2:4:100 // a = float(rand(n,n)); // [q,r,e] = qr(a); // err = abs(a-q*r*e'); // err = max(err(:)); // bnd = 10*max(abs(diag(r)))*feps*(n^(0.5)); // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // t2all = 1; // for n=2:4:100 // a = double(rand(n,n)); // [q,r,e] = qr(a); // err = abs(a-q*r*e'); // err = max(err(:)); // bnd = 10*max(abs(diag(r)))*eps*(n^(0.5)); // t2 = (err < bnd); // if (~t2) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t2all = t2all & t2; // end // t3all = 1; // for n=2:4:100 // a = complex(rand(n,n)+i*rand(n,n)); // [q,r,e] = qr(a); // err = abs(a-q*r*e'); // err = max(err(:)); // bnd = 10*max(abs(diag(r)))*feps*(n^(0.5)); // t3 = (err < bnd); // if (~t3) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t3all = t3all & t3; // end // t4all = 1; // for n=2:4:100 // a = dcomplex(rand(n,n)+i*rand(n,n)); // [q,r,e] = qr(a); // err = abs(a-q*r*e'); // err = max(err(:)); // bnd = 10*max(abs(diag(r)))*eps*(n^(0.5)); // t4 = (err < bnd); // if (~t4) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t4all = t4all & t4; // end // test_val = t1all & t2all & t3all & t4all; //@} //! ArrayVector QRDFunction(int nargout, const ArrayVector& arg) { if (arg.size() < 1) throw Exception("qr function requires at least one argument - the matrix to decompose."); Array A(arg[0]); // Test for numeric if (A.isReferenceType()) throw Exception("Cannot apply qr decomposition to reference types."); if (!A.is2D()) throw Exception("Cannot apply matrix operations to N-Dimensional arrays."); if (A.anyNotFinite()) throw Exception("QR Decomposition only defined for matrices with finite entries."); int nrows = A.getDimensionLength(0); int ncols = A.getDimensionLength(1); bool compactDecomposition = false; if (arg.size() == 2) { Array cflag(arg[1]); int cflag_int = cflag.getContentsAsIntegerScalar(); if (cflag_int == 0) compactDecomposition = true; } if (nargout == 3) return QRDPivotFunction(compactDecomposition, A); else return QRDNoPivotFunction(compactDecomposition, A); } //! //@Module SVD Singular Value Decomposition of a Matrix //@@Section TRANSFORMS //@@Usage //Computes the singular value decomposition (SVD) of a matrix. The //@|svd| function has three forms. The first returns only the singular //values of the matrix: //@[ // s = svd(A) //@] //The second form returns both the singular values in a diagonal //matrix @|S|, as well as the left and right eigenvectors. //@[ // [U,S,V] = svd(A) //@] //The third form returns a more compact decomposition, with the //left and right singular vectors corresponding to zero singular //values being eliminated. The syntax is //@[ // [U,S,V] = svd(A,0) //@] //@@Function Internals //Recall that @|sigma_i| is a singular value of an @|M x N| //matrix @|A| if there exists two vectors @|u_i, v_i| where @|u_i| is //of length @|M|, and @|v_i| is of length @|u_i| and //\[ // A v_i = \sigma_i u_i //\] //and generally //\[ // A = \sum_{i=1}^{K} \sigma_i u_i*v_i', //\] //where @|K| is the rank of @|A|. In matrix form, the left singular //vectors @|u_i| are stored in the matrix @|U| as //\[ // U = [u_1,\ldots,u_m], V = [v_1,\ldots,v_n] //\] //The matrix @|S| is then of size @|M x N| with the singular //values along the diagonal. The SVD is computed using the //@|LAPACK| class of functions @|GESDD|. //@@Examples //Here is an example of a partial and complete singular value //decomposition. //@< //A = float(randn(2,3)) //[U,S,V] = svd(A) //U*S*V' //svd(A) //@> //@@Tests //@{ test_svd1.m //% Test the svd with finite and infinite args (functional only) //function test_val = test_svd1 // a = [1,3;4,2]; // [u,s,v] = svd(a); // res1 = 1; // res2 = 0; // res3 = 0; // res4 = 0; // try // c = a; // c(2,2) = inf; // svd(c); // catch // res2 = 1; // end // try // c = a; // c(2,2) = -inf; // svd(c); // catch // res3 = 1; // end // try // c = a; // c(2,2) = nan; // svd(c); // catch // res4 = 1; // end // // test_val = res1 & res2 & res3 & res4; //@} //@{ test_svd2.m //% Test the svd with finite and infinite args (functional only) //function test_val = test_svd2 // a = double([1,3;4,2]); // [u,s,v] = svd(a); // res1 = 1; // res2 = 0; // res3 = 0; // res4 = 0; // try // c = a; // c(2,2) = inf; // svd(c); // catch // res2 = 1; // end // try // c = a; // c(2,2) = -inf; // svd(c); // catch // res3 = 1; // end // try // c = a; // c(2,2) = nan; // svd(c); // catch // res4 = 1; // end // // test_val = res1 & res2 & res3 & res4; //@} //@{ test_svd3.m //% Test the svd with finite and infinite args (functional only) //function test_val = test_svd3 // a = complex([1,3;4,2]); // [u,s,v] = svd(a); // res1 = 1; // res2 = 0; // res3 = 0; // res4 = 0; // try // c = a; // c(2,2) = inf; // svd(c); // catch // res2 = 1; // end // try // c = a; // c(2,2) = -inf; // svd(c); // catch // res3 = 1; // end // try // c = a; // c(2,2) = nan; // svd(c); // catch // res4 = 1; // end // // test_val = res1 & res2 & res3 & res4; //@} //@{ test_svd4.m //% Test the svd with finite and infinite args (functional only) //function test_val = test_svd4 // a = dcomplex([1,3;4,2]); // [u,s,v] = svd(a); // res1 = 1; // res2 = 0; // res3 = 0; // res4 = 0; // try // c = a; // c(2,2) = inf; // svd(c); // catch // res2 = 1; // end // try // c = a; // c(2,2) = -inf; // svd(c); // catch // res3 = 1; // end // try // c = a; // c(2,2) = nan; // svd(c); // catch // res4 = 1; // end // // test_val = res1 & res2 & res3 & res4; //@} //@{ test_svd5.m //% Test the full version of the svd with float vectors //function test_val = test_svd5 // t1all = 1; // p = []; // for n=2:4:100 // a = float(rand(n,2*n)); // [u,s,v] = svd(a); // emat = abs(a-u*s*v'); // err = max(emat(:)); // p(n) = err; // bnd = 10*max(abs(diag(s)))*feps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:4:100 // a = float(rand(2*n,n)); // [u,s,v] = svd(a); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*feps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:4:100 // a = float(rand(n,n)); // [u,s,v] = svd(a); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*feps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // test_val = t1all; //@} //@{ test_svd6.m //% Test the full version of the svd with complex vectors //function test_val = test_svd6 // t1all = 1; // p = []; // for n=2:4:100 // a = float(rand(n,2*n))+i*float(rand(n,2*n)); // [u,s,v] = svd(a); // emat = abs(a-u*s*v'); // err = max(emat(:)); // p(n) = err; // bnd = 10*max(abs(diag(s)))*feps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:4:100 // a = float(rand(2*n,n))+i*float(rand(2*n,n)); // [u,s,v] = svd(a); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*feps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:4:100 // a = float(rand(n,n))+i*float(rand(n,n)); // [u,s,v] = svd(a); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*feps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // test_val = t1all; //@} //@{ test_svd7.m //% Test the full version of the svd with double vectors //function test_val = test_svd7 // t1all = 1; // p = []; // for n=2:4:100 // a = double(rand(n,2*n)); // [u,s,v] = svd(a); // emat = abs(a-u*s*v'); // err = max(emat(:)); // p(n) = err; // bnd = 10*max(abs(diag(s)))*eps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:4:100 // a = double(rand(2*n,n)); // [u,s,v] = svd(a); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*eps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:4:100 // a = double(rand(n,n)); // [u,s,v] = svd(a); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*eps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // test_val = t1all; //@} //@{ test_svd8.m //% Test the full version of the svd with dcomplex vectors //function test_val = test_svd8 // t1all = 1; // p = []; // for n=2:4:100 // a = double(rand(n,2*n))+i*double(rand(n,2*n)); // [u,s,v] = svd(a); // emat = abs(a-u*s*v'); // err = max(emat(:)); // p(n) = err; // bnd = 10*max(abs(diag(s)))*eps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:4:100 // a = double(rand(2*n,n))+i*double(rand(2*n,n)); // [u,s,v] = svd(a); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*eps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:4:100 // a = double(rand(n,n))+i*double(rand(n,n)); // [u,s,v] = svd(a); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*eps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // test_val = t1all; //@} //@{ test_svd9.m //% Test the compact version of the svd with float vectors //function test_val = test_svd9 // t1all = 1; // p = []; // for n=2:4:100 // a = float(rand(n,2*n)); // [u,s,v] = svd(a,0); // emat = abs(a-u*s*v'); // err = max(emat(:)); // p(n) = err; // bnd = 10*max(abs(diag(s)))*feps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:4:100 // a = float(rand(2*n,n)); // [u,s,v] = svd(a,0); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*feps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:4:100 // a = float(rand(n,n)); // [u,s,v] = svd(a,0); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*feps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // test_val = t1all; //@} //@{ test_svd10.m //% Test the compact version of the svd with complex vectors //function test_val = test_svd10 // t1all = 1; // p = []; // for n=2:4:100 // a = float(rand(n,2*n))+i*float(rand(n,2*n)); // [u,s,v] = svd(a,0); // emat = abs(a-u*s*v'); // err = max(emat(:)); // p(n) = err; // bnd = 10*max(abs(diag(s)))*feps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:4:100 // a = float(rand(2*n,n))+i*float(rand(2*n,n)); // [u,s,v] = svd(a,0); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*feps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:4:100 // a = float(rand(n,n))+i*float(rand(n,n)); // [u,s,v] = svd(a,0); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*feps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // test_val = t1all; //@} //@{ test_svd11.m //% Test the compact version of the svd with double vectors //function test_val = test_svd11 // t1all = 1; // p = []; // for n=2:100 // a = double(rand(n,2*n)); // [u,s,v] = svd(a,0); // emat = abs(a-u*s*v'); // err = max(emat(:)); // p(n) = err; // bnd = 10*max(abs(diag(s)))*eps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:100 // a = double(rand(2*n,n)); // [u,s,v] = svd(a,0); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*eps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:100 // a = double(rand(n,n)); // [u,s,v] = svd(a,0); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*eps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // test_val = t1all; //@} //@{ test_svd12.m //% Test the compact version of the svd with dcomplex vectors //function test_val = test_svd12 // t1all = 1; // p = []; // for n=2:100 // a = double(rand(n,2*n))+i*double(rand(n,2*n)); // [u,s,v] = svd(a,0); // emat = abs(a-u*s*v'); // err = max(emat(:)); // p(n) = err; // bnd = 10*max(abs(diag(s)))*eps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:100 // a = double(rand(2*n,n))+i*double(rand(2*n,n)); // [u,s,v] = svd(a,0); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*eps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // for n=2:100 // a = double(rand(n,n))+i*double(rand(n,n)); // [u,s,v] = svd(a,0); // emat = abs(a-u*s*v'); // err = max(emat(:)); // bnd = 10*max(abs(diag(s)))*eps*n; // t1 = (err < bnd); // if (~t1) printf('test failed: er = %e bnd = %e (num %d)\n',err,bnd,n); end; // t1all = t1all & t1; // end // test_val = t1all; //@} //! ArrayVector SVDFunction(int nargout, const ArrayVector& arg) { if (arg.size() > 2) throw Exception("svd function takes at most two arguments"); if (arg.size() < 1) throw Exception("svd function requries at least one argument - the matrix to decompose"); Array A(arg[0]); bool compactform = false; if (arg.size() > 1) { Array flag(arg[1]); if (flag.getContentsAsIntegerScalar() == 0) compactform = true; } // Test for numeric if (A.isReferenceType()) throw Exception("Cannot apply svd to reference types."); if (!A.is2D()) throw Exception("Cannot apply matrix operations to N-Dimensional arrays."); if (A.anyNotFinite()) throw Exception("SVD only defined for matrices with finite entries."); int nrows = A.getDimensionLength(0); int ncols = A.getDimensionLength(1); Class Aclass(A.dataClass()); if (Aclass < FM_FLOAT) { A.promoteType(FM_DOUBLE); Aclass = FM_DOUBLE; } bool computevectors; computevectors = (nargout>1); ArrayVector retval; switch (Aclass) { case FM_FLOAT: { int mindim; mindim = (nrows < ncols) ? nrows : ncols; // A temporary vector to store the singular values float *svals = (float*) Malloc(mindim*sizeof(float)); // A temporary vector to store the left singular vectors float *umat = NULL; // A temporary vector to store the right singular vectors float *vtmat = NULL; if (computevectors) if (!compactform) { umat = (float*) Malloc(nrows*nrows*sizeof(float)); vtmat = (float*) Malloc(ncols*ncols*sizeof(float)); } else { umat = (float*) Malloc(nrows*mindim*sizeof(float)); vtmat = (float*) Malloc(ncols*mindim*sizeof(float)); } floatSVD(nrows,ncols,umat,vtmat,svals, (float*) A.getReadWriteDataPointer(), compactform, computevectors); // Always transfer the singular values into an Array Dimensions dim; if (!computevectors) { dim.set(0,mindim); dim.set(1,1); retval.push_back(Array(FM_FLOAT,dim,svals)); } else { if (!compactform) { dim.set(0,nrows); dim.set(1,nrows); retval.push_back(Array(FM_FLOAT,dim,umat)); dim.set(0,nrows); dim.set(1,ncols); float *smat = (float*) Malloc(nrows*ncols*sizeof(float)); for (int i=0;i //Or equivalently, using the second form: //@< //lasterr('Test message'); //lasterr //@> //! ArrayVector LasterrFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { ArrayVector retval; if (arg.size() == 0) { Array A = Array::stringConstructor(eval->getLastErrorString()); retval.push_back(A); } else { Array tmp(arg[0]); eval->setLastErrorString(tmp.getContentsAsString()); } return retval; } //! //@Module SIMKEYS Simulate Keypresses from the User //@@Section FREEMAT //@@Usage //This routine simulates keystrokes from the user on FreeMat. //The general syntax for its use is //@[ // otext = simkeys(text) //@] //where @|text| is a string to simulate as input to the console. //The output of the commands are captured and returned in the //string @|otext|. This is primarily used by the testing //infrastructure. //! ArrayVector SimKeysFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { if (arg.size() == 0) throw Exception("simkeys requires at least one argument (the cell array of strings to simulate)"); eval->clearCaptureString(); eval->setCaptureState(true); if (arg[0].dataClass() != FM_CELL_ARRAY) throw Exception("simkeys requires a cell array of strings"); const Array *dp = (const Array *) arg[0].getDataPointer(); for (int i=0;iExecuteLine(ArrayToString(dp[i])); eval->ExecuteLine("quit\n"); try { while(1) eval->evalCLI(); } catch (InterpreterContinueException& e) { } catch (InterpreterBreakException& e) { } catch (InterpreterReturnException& e) { } catch (InterpreterRetallException& e) { } catch (InterpreterQuitException& e) { } eval->setCaptureState(false); return ArrayVector() << Array::stringConstructor(eval->getCaptureString()); } //! //@Module ERRORCOUNT Retrieve the Error Counter for the Interpreter //@@Section FREEMAT //@@Usage //This routine retrieves the internal counter for the interpreter, //and resets it to zero. The general syntax for its use is //@[ // count = errorcount //@] //! ArrayVector ErrorCountFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { return ArrayVector() << Array::int32Constructor(eval->getErrorCount()); } //! //@Module DIAG Diagonal Matrix Construction/Extraction //@@Section ARRAY //@@Usage //The @|diag| function is used to either construct a //diagonal matrix from a vector, or return the diagonal //elements of a matrix as a vector. The general syntax //for its use is //@[ // y = diag(x,n) //@] //If @|x| is a matrix, then @|y| returns the @|n|-th //diagonal. If @|n| is omitted, it is assumed to be //zero. Conversely, if @|x| is a vector, then @|y| //is a matrix with @|x| set to the @|n|-th diagonal. //@@Examples //Here is an example of @|diag| being used to extract //a diagonal from a matrix. //@< //A = int32(10*rand(4,5)) //diag(A) //diag(A,1) //@> //Here is an example of the second form of @|diag|, being //used to construct a diagonal matrix. //@< //x = int32(10*rand(1,3)) //diag(x) //diag(x,-1) //@> //@@Tests //@{ test_diag1.m //% Test the diagonal extraction function //function test_val = test_diag1 //a = [1,2,3,4;5,6,7,8;9,10,11,12]; //b = diag(a); //test_val = test(b == [1;6;11]); //@} //@{ test_diag2.m //% Test the diagonal extraction function with a non-zero diagonal //function test_val = test_diag2 //a = [1,2,3,4;5,6,7,8;9,10,11,12]; //b = diag(a,1); //test_val = test(b == [2;7;12]); //@} //@{ test_diag3.m //% Test the diagonal creation function //function test_val = test_diag3 //a = [2,3]; //b = diag(a); //test_val = test(b == [2,0;0,3]); //@} //@{ test_diag4.m //% Test the diagonal creation function with a non-zero diagonal //function test_val = test_diag4 //a = [2,3]; //b = diag(a,-1); //test_val = test(b == [0,0,0;2,0,0;0,3,0]); //@} //@{ test_diag5.m //% Test the diagonal creation function with no arguments (bug 1620051) //function test_val = test_diag5 //test_val = 1; //try // b = diag; //catch // test_val = 1; //end //@} //@@Tests //@{ test_sparse74.m //% Test sparse matrix array diagonal extraction //function x = test_sparse74 //[yi1,zi1] = sparse_test_mat('int32',300,400); //[yf1,zf1] = sparse_test_mat('float',300,400); //[yd1,zd1] = sparse_test_mat('double',300,400); //[yc1,zc1] = sparse_test_mat('complex',300,400); //[yz1,zz1] = sparse_test_mat('dcomplex',300,400); //x = testeq(diag(yi1,30),diag(zi1,30)) & testeq(diag(yf1,30),diag(zf1,30)) & testeq(diag(yd1,30),diag(zd1,30)) & testeq(diag(yc1,30),diag(zc1,30)) & testeq(diag(yz1,30),diag(zz1,30)); //@} //! ArrayVector DiagFunction(int nargout, const ArrayVector& arg) { Array a; Array b; Array c; ArrayVector retval; int32 *dp; int diagonalOrder; // First, get the diagonal order, and synthesize it if it was // not supplied if (arg.size() == 0) throw Exception("diag requires at least one argument.\n"); if (arg.size() == 1) diagonalOrder = 0; else { b = arg[1]; if (!b.isScalar()) throw Exception("second argument must be a scalar.\n"); b.promoteType(FM_INT32); dp = (int32 *) b.getDataPointer(); diagonalOrder = dp[0]; } // Next, make sure the first argument is 2D a = arg[0]; if (!a.is2D()) throw Exception("First argument to 'diag' function must be 2D.\n"); // Case 1 - if the number of columns in a is 1, then this is a diagonal // constructor call. if ((a.getDimensionLength(1) == 1) || (a.getDimensionLength(0) == 1)) c = Array::diagonalConstructor(a,diagonalOrder); else c = a.getDiagonal(diagonalOrder); retval.push_back(c); return retval; } //! //@Module ISEMPTY Test For Variable Empty //@@Section INSPECTION //@@Usage //The @|isempty| function returns a boolean that indicates //if the argument variable is empty or not. The general //syntax for its use is //@[ // y = isempty(x). //@] //@@Examples //Here are some examples of the @|isempty| function //@< //a = [] //isempty(a) //b = 1:3 //isempty(b) //@> //Note that if the variable is not defined, @|isempty| //does not return true. //@<1 //clear x //isempty(x) //@> //@@Tests //@{ test_empty.m //% Test the arithmetic operators with empty arguments //function test_val = test_empty //test_val = isempty([]+[]); //test_val = test_val & isempty([]-[]); //test_val = test_val & isempty(-[]); //test_val = test_val & isempty([]*[]); //test_val = test_val & isempty([]/[]); //test_val = test_val & isempty([]\[]); //test_val = test_val & isempty([].*[]); //test_val = test_val & isempty([]./[]); //test_val = test_val & isempty([].\[]); //test_val = test_val & isempty([]^[]); //test_val = test_val & isempty([].^[]); //test_val = test_val & isempty([]>[]); //test_val = test_val & isempty([]>=[]); //test_val = test_val & isempty([]<[]); //test_val = test_val & isempty([]<=[]); //@} //! ArrayVector IsEmptyFunction(int nargout, const ArrayVector& arg) { if (arg.size() < 1) throw Exception("isempty function requires at least input argument"); ArrayVector retval; retval.push_back(Array::logicalConstructor(arg[0].isEmpty())); return retval; } //! //@Module SPONES Sparse Ones Function //@@Section SPARSE //@@Usage //Returns a sparse @|float| matrix with ones where the argument //matrix has nonzero values. The general syntax for it is //@[ // y = spones(x) //@] //where @|x| is a matrix (it may be full or sparse). The output //matrix @|y| is the same size as @|x|, has type @|float|, and contains //ones in the nonzero positions of @|x|. //@@Examples //Here are some examples of the @|spones| function //@< //a = [1,0,3,0,5;0,0,2,3,0;1,0,0,0,1] //b = spones(a) //full(b) //@> //! ArrayVector SponesFunction(int nargout, const ArrayVector& arg) { if (arg.size() < 1) throw Exception("spones function requires a sparse matrix template argument"); Array tmp(arg[0]); if (tmp.isEmpty()) return singleArrayVector(Array::emptyConstructor()); if(tmp.isReferenceType()) throw Exception("spones function requires a numeric sparse matrix argument"); tmp.makeSparse(); if (!tmp.sparse()) throw Exception("spones function requires a sparse matrix template argument"); return singleArrayVector(Array::Array(FM_FLOAT,Dimensions(tmp.getDimensionLength(0),tmp.getDimensionLength(1)),SparseOnesFunc(tmp.dataClass(),tmp.getDimensionLength(0),tmp.getDimensionLength(1),tmp.getSparseDataPointer()),true)); } //! //@Module WARNING Emits a Warning Message //@@Section FLOW //@@Usage //The @|warning| function causes a warning message to be //sent to the user. The general syntax for its use is //@[ // warning(s) //@] //where @|s| is the string message containing the warning. //! ArrayVector WarningFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { if (arg.size() == 0) throw Exception("Not enough inputs to warning function"); if (!(arg[0].isString())) throw Exception("Input to error function must be a string"); eval->warningMessage(arg[0].getContentsAsString()); return ArrayVector(); } //! //@Module VERSION The Current Version Number //@@Section FREEMAT //@@Usage //The @|version| function returns the current version number for //FreeMat (as a string). The general syntax for its use is //@[ // v = version //@] //@@Example //The current version of FreeMat is //@< //version //@> //! ArrayVector VersionFunction(int nargout, const ArrayVector& arg) { return ArrayVector() << Array::stringConstructor(VERSION); } //! //@Module VERSTRING The Current Version String //@@Section FREEMAT //@@Usage //The @|verstring| function returns the current version string for //FreeMat. The general syntax for its use is //@[ // version = verstring //@] //@@Example //The current version of FreeMat is //@< //verstring //@> //! ArrayVector VerStringFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { return ArrayVector() << Array::stringConstructor(Interpreter::getVersionString()); } //! //@Module ERROR Causes an Error Condition Raised //@@Section FLOW //@@Usage //The @|error| function causes an error condition (exception //to be raised). The general syntax for its use is //@[ // error(s), //@] //where @|s| is the string message describing the error. The //@|error| function is usually used in conjunction with @|try| //and @|catch| to provide error handling. If the string @|s|, //then (to conform to the MATLAB API), @|error| does nothing. //@@Example //Here is a simple example of an @|error| being issued by a function //@|evenoddtest|: //@{ evenoddtest.m //function evenoddtest(n) // if (n==0) // error('zero is neither even nor odd'); // elseif (~isa(n,'int32')) // error('expecting integer argument'); // end; // if (n==int32(n/2)*2) // printf('%d is even\n',n); // else // printf('%d is odd\n',n); // end //@} //The normal command line prompt @|-->| simply prints the error //that occured. //@<2 //evenoddtest(4) //evenoddtest(5) //evenoddtest(0) //evenoddtest(pi) //@> //! ArrayVector ErrorFunction(int nargout, const ArrayVector& arg) { if (arg.size() == 0) return ArrayVector(); string etxt(ArrayToString(arg[0])); if (!etxt.empty()) throw Exception(etxt); return ArrayVector(); } //! //@Module EVAL Evaluate a String //@@Section FREEMAT //@@Usage //The @|eval| function evaluates a string. The general syntax //for its use is //@[ // eval(s) //@] //where @|s| is the string to evaluate. If @|s| is an expression //(instead of a set of statements), you can assign the output //of the @|eval| call to one or more variables, via //@[ // x = eval(s) // [x,y,z] = eval(s) //@] // //Another form of @|eval| allows you to specify an expression or //set of statements to execute if an error occurs. In this //form, the syntax for @|eval| is //@[ // eval(try_clause,catch_clause), //@] //or with return values, //@[ // x = eval(try_clause,catch_clause) // [x,y,z] = eval(try_clause,catch_clause) //@] //These later forms are useful for specifying defaults. Note that //both the @|try_clause| and @|catch_clause| must be expressions, //as the equivalent code is //@[ // try // [x,y,z] = try_clause // catch // [x,y,z] = catch_clause // end //@] //so that the assignment must make sense in both cases. //@@Example //Here are some examples of @|eval| being used. //@< //eval('a = 32') //b = eval('a') //@> //The primary use of the @|eval| statement is to enable construction //of expressions at run time. //@< //s = ['b = a' ' + 2'] //eval(s) //@> //Here we demonstrate the use of the catch-clause to provide a //default value //@< //a = 32 //b = eval('a','1') //b = eval('z','a+1') //@> //Note that in the second case, @|b| takes the value of 33, indicating //that the evaluation of the first expression failed (because @|z| is //not defined). //! static string PrePendCallVars(string line, int nargout) { char buf[4096]; char *gp = buf; if (nargout > 1) *gp++ = '['; for (int i=0;i 1) sprintf(gp,"] = %s;\n",line.c_str()); else sprintf(gp," = %s;\n",line.c_str()); return buf; } // //static - having a static here seems to break cross compiling // ArrayVector RetrieveCallVars(Interpreter *eval, int nargout) { ArrayVector retval; for (int i=0;igetContext()->lookupVariable(tname); if (!ptr.valid()) tval = Array::emptyConstructor(); else tval = *ptr; eval->getContext()->deleteVariable(tname); retval.push_back(tval); } return retval; } ArrayVector EvalTryFunction(int nargout, const ArrayVector& arg, Interpreter* eval, int popSpec) { if (nargout > 0) { string try_line = arg[0].getContentsAsString(); string try_buf = PrePendCallVars(try_line,nargout); string catch_line = arg[1].getContentsAsString(); string catch_buf = PrePendCallVars(catch_line,nargout); ArrayVector retval; bool autostop; autostop = eval->AutoStop(); eval->AutoStop(false); try { eval->getContext()->bypassScope(popSpec); eval->evaluateString(try_buf,true); retval = RetrieveCallVars(eval,nargout); eval->getContext()->restoreBypassedScopes(); } catch (Exception &e) { eval->getContext()->restoreBypassedScopes(); eval->evaluateString(catch_buf,false); retval = RetrieveCallVars(eval,nargout); } eval->AutoStop(autostop); return retval; } else { string try_line = arg[0].getContentsAsString(); string catch_line = arg[1].getContentsAsString(); string try_buf = try_line + "\n"; string catch_buf = catch_line + "\n"; bool autostop; autostop = eval->AutoStop(); eval->AutoStop(false); try { eval->getContext()->bypassScope(popSpec); eval->evaluateString(try_buf,true); eval->getContext()->restoreBypassedScopes(); } catch (Exception &e) { eval->getContext()->restoreBypassedScopes(); eval->evaluateString(catch_buf,false); } eval->AutoStop(autostop); return ArrayVector(); } } ArrayVector EvalNoTryFunction(int nargout, const ArrayVector& arg, Interpreter* eval, int popSpec) { if (nargout > 0) { string line = arg[0].getContentsAsString(); string buf = PrePendCallVars(line,nargout); eval->getContext()->bypassScope(popSpec); eval->evaluateString(buf); ArrayVector retval(RetrieveCallVars(eval,nargout)); eval->getContext()->restoreBypassedScopes(); return retval; } else { string line = arg[0].getContentsAsString(); string buf = line + "\n"; eval->getContext()->bypassScope(popSpec); eval->evaluateString(buf); eval->getContext()->restoreBypassedScopes(); return ArrayVector(); } } ArrayVector EvalFunction(int nargout, const ArrayVector& arg,Interpreter* eval){ if (arg.size() == 0) throw Exception("eval function takes at least one argument - the string to execute"); if (arg.size() == 2) return EvalTryFunction(nargout, arg, eval, 0); return EvalNoTryFunction(nargout, arg, eval, 0); } //! //@Module DIARY Create a Log File of Console //@@Section FREEMAT //@@Usage //The @|diary| function controls the creation of a log file that duplicates //the text that would normally appear on the console. //The simplest syntax for the command is simply: //@[ // diary //@] //which toggles the current state of the diary command. You can also explicitly //set the state of the diary command via the syntax //@[ // diary off //@] //or //@[ // diary on //@] //To specify a filename for the log (other than the default of @|diary|), you //can use the form: //@[ // diary filename //@] //or //@[ // diary('filename') //@] //which activates the diary with an output filename of @|filename|. Note that the //@|diary| command is thread specific, but that the output is appended to a given //file. That means that if you call @|diary| with the same filename on multiple //threads, their outputs will be intermingled in the log file (just as on the console). //Because the @|diary| state is tied to individual threads, you cannot retrieve the //current diary state using the @|get(0,'Diary')| syntax from MATLAB. Instead, you //must call the @|diary| function with no inputs and one output: //@[ // state = diary //@] //which returns a logical @|1| if the output of the current thread is currently going to //a diary, and a logical @|0| if not. //! ArrayVector DiaryFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { if (nargout == 1) { if (arg.size() > 0) throw Exception("diary function with an assigned return value (i.e, 'x=diary') does not support any arguments"); return ArrayVector() << Array::logicalConstructor(eval->getDiaryState()); } if (arg.size() == 0) { eval->setDiaryState(!eval->getDiaryState()); return ArrayVector(); } string diaryString(ArrayToString(arg[0])); if (diaryString == "on") eval->setDiaryState(true); else if (diaryString == "off") eval->setDiaryState(false); else { eval->setDiaryFilename(diaryString); eval->setDiaryState(true); } return ArrayVector(); } //! //@Module EVALIN Evaluate a String in Workspace //@@Section FREEMAT //@@Usage //The @|evalin| function is similar to the @|eval| function, with an additional //argument up front that indicates the workspace that the expressions are to //be evaluated in. The various syntaxes for @|evalin| are: //@[ // evalin(workspace,expression) // x = evalin(workspace,expression) // [x,y,z] = evalin(workspace,expression) // evalin(workspace,try_clause,catch_clause) // x = evalin(workspace,try_clause,catch_clause) // [x,y,z] = evalin(workspace,try_clause,catch_clause) //@] //The argument @|workspace| must be either 'caller' or 'base'. If it is //'caller', then the expression is evaluated in the caller's work space. //That does not mean the caller of @|evalin|, but the caller of the current //function or script. On the other hand if the argument is 'base', then //the expression is evaluated in the base work space. See @|eval| for //details on the use of each variation. //! ArrayVector EvalInFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { if (arg.size() < 2) throw Exception("evalin function requires a workspace (scope) specifier (either 'caller' or 'base') and an expression to evaluate"); Array spec(arg[0]); string spec_str = spec.getContentsAsString(); int popspec = 0; if (spec_str=="base") popspec = -1; else if (spec_str=="caller") popspec = 1; else throw Exception("evalin function requires the first argument to be either 'caller' or 'base'"); ArrayVector argcopy(arg); argcopy.pop_front(); if (arg.size() == 3) return EvalTryFunction(nargout,argcopy,eval,popspec); else return EvalNoTryFunction(nargout,argcopy,eval,popspec); } //! //@Module ASSIGNIN Assign Variable in Workspace //@@Section FREEMAT //@@Usage //The @|assignin| function allows you to assign a value to a variable //in either the callers work space or the base work space. The syntax //for @|assignin| is //@[ // assignin(workspace,variablename,value) //@] //The argument @|workspace| must be either 'caller' or 'base'. If it is //'caller' then the variable is assigned in the caller's work space. //That does not mean the caller of @|assignin|, but the caller of the //current function or script. On the other hand if the argument is 'base', //then the assignment is done in the base work space. Note that the //variable is created if it does not already exist. //! ArrayVector AssignInFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { if (arg.size() < 3) throw Exception("assignin function requires a workspace (scope) specifier (either 'caller' or 'base') a variable name and a value to assign"); Array spec(arg[0]); string spec_str = spec.getContentsAsString(); int popspec = 0; if (spec_str=="base") popspec = -1; else if (spec_str=="caller") popspec = 1; else throw Exception("evalin function requires the first argument to be either 'caller' or 'base'"); string varname = ArrayToString(arg[1]); Array varvalue = arg[2]; eval->getContext()->bypassScope(popspec); eval->getContext()->insertVariable(varname,varvalue); eval->getContext()->restoreBypassedScopes(); return ArrayVector(); } //! //@Module QUIET Control the Verbosity of the Interpreter //@@Section FREEMAT //@@Usage //The @|quiet| function controls how verbose the interpreter //is when executing code. The syntax for the function is //@[ // quiet flag //@] //where @|flag| is one of //\begin{itemize} //\item @|'normal'| - normal output from the interpreter //\item @|'quiet'| - only intentional output (e.g. @|printf| calls and //@|disp| calls) is printed. The output of expressions that are not //terminated in semicolons are not printed. //\item @|'silent'| - nothing is printed to the output. //\end{itemize} //The @|quiet| command also returns the current quiet flag. //! ArrayVector QuietFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { if (arg.size() > 0) { string qtype(arg[0].getContentsAsStringUpper()); if (qtype == "NORMAL") eval->setQuietLevel(0); else if (qtype == "QUIET") eval->setQuietLevel(1); else if (qtype == "SILENT") eval->setQuietLevel(2); else throw Exception("quiet function takes one argument - the quiet level (normal, quiet, or silent) as a string"); } string rtype; if (eval->getQuietLevel() == 0) rtype = "normal"; else if (eval->getQuietLevel() == 1) rtype = "quiet"; else if (eval->getQuietLevel() == 2) rtype = "silent"; return ArrayVector() << Array::stringConstructor(rtype); } //! //@Module SOURCE Execute an Arbitrary File //@@Section FREEMAT //@@Usage //The @|source| function executes the contents of the given //filename one line at a time (as if it had been typed at //the @|-->| prompt). The @|source| function syntax is //@[ // source(filename) //@] //where @|filename| is a @|string| containing the name of //the file to process. //@@Example //First, we write some commands to a file (note that it does //not end in the usual @|.m| extension): //@{ source_test //a = 32; //b = a; //printf('a is %d and b is %d\n',a,b); //@} //Now we source the resulting file. //@< //clear a b //source source_test //@> //@@Tests //@{ source_test_script.m //n = 1; //n = n + 1; //@} //@{ test_source.m //% Check that the source function does not double-execute the last line of the script //function test_val = test_source //myloc = which('test_source'); //[path,name,sfx] = fileparts(myloc); //source([path '/source_test_script.m']) //test_val = test(n == 2); //@} //! ArrayVector SourceFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { if (arg.size() != 1) throw Exception("source function takes exactly one argument - the filename of the script to execute"); string filename = arg[0].getContentsAsString(); QFile fp(QString::fromStdString(filename)); if (!fp.open(QFile::ReadOnly)) throw Exception(std::string("unable to open file ") + filename + " for reading"); QTextStream fstr(&fp); QString scriptText(fstr.readAll()); Scanner S(scriptText.toStdString(),filename); Parser P(S); tree pcode = P.Process(); if (pcode.is(TOK_FUNCTION_DEFS)) throw Exception("only scripts can be source-ed, not functions"); tree code = pcode.first(); eval->block(code); return ArrayVector(); } //! //@Module DBDELETE Delete a Breakpoint //@@Section DEBUG //@@Usage //The @|dbdelete| function deletes a breakpoint. The syntax //for the @|dbdelete| function is //@[ // dbdelete(num) //@] //where @|num| is the number of the breakpoint to delete. //! ArrayVector DbDeleteFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { if (arg.size() < 1) throw Exception("dbdelete requires an argument (number of breakpoint to delete)"); Array tmp(arg[0]); int bpnum; bpnum = tmp.getContentsAsIntegerScalar(); eval->deleteBreakpoint(bpnum); return ArrayVector(); } //! //@Module DBLIST List Breakpoints //@@Section DEBUG //@@Usage //List the current set of breakpoints. The syntax for the //@|dblist| is simply //@[ // dblist //@] //! ArrayVector DbListFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { eval->listBreakpoints(); return ArrayVector(); } //! //@Module DBSTEP Step N Statements //@@Section DEBUG //@@Usage //Step @|N| statements during debug mode. The synax for this is //either //@[ // dbstep(N) //@] //to step @|N| statements, or //@[ // dbstep //@] //to step one statement. //! // ArrayVector DbStepFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { // int linesToSkip; // if (arg.size() == 0) // linesToSkip = 1; // else { // Array tmp(arg[0]); // linesToSkip = tmp.getContentsAsIntegerScalar(); // } // eval->dbstep(linesToSkip); // return ArrayVector(); // } //! //@Module DBSTOP //@@Section DEBUG //@@Usage //Set a breakpoint. The syntax for this is: //@[ // dbstop(funcname,linenumber) //@] //where @|funcname| is the name of the function where we want //to set the breakpoint, and @|linenumber| is the line number. //! ArrayVector DbStopFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { if (arg.size() < 2) throw Exception("dbstop function requires at least two arguments"); if (!(arg[0].isString())) throw Exception("first argument to dbstop must be the name of routine where to stop"); string cname = ArrayToString(arg[0]); int line = ArrayToInt32(arg[1]); eval->addBreakpoint(cname,line); return ArrayVector(); } ArrayVector FdumpFunction(int nargout, const ArrayVector& arg,Interpreter* eval){ if (arg.size() == 0) throw Exception("fdump function requires at least one argument"); if (!(arg[0].isString())) throw Exception("first argument to fdump must be the name of a function (i.e., a string)"); string fname = arg[0].getContentsAsString(); Context *context = eval->getContext(); FuncPtr funcDef; if (!context->lookupFunction(fname,funcDef)) throw Exception(std::string("function ") + fname + " undefined!"); funcDef->updateCode(eval); funcDef->printMe(eval); return ArrayVector(); } //! //@Module BUILTIN Evaulate Builtin Function //@@Section FREEMAT //@@Usage //The @|builtin| function evaluates a built in function //with the given name, bypassing any overloaded functions. //The syntax of @|builtin| is //@[ // [y1,y2,...,yn] = builtin(fname,x1,x2,...,xm) //@] //where @|fname| is the name of the function to call. Apart //from the fact that @|fname| must be a string, and that @|builtin| //always calls the non-overloaded method, it operates exactly like //@|feval|. Note that unlike MATLAB, @|builtin| does not force //evaluation to an actual compiled function. It simply subverts //the activation of overloaded method calls. //! ArrayVector BuiltinFunction(int nargout, const ArrayVector& arg,Interpreter* eval){ if (arg.size() == 0) throw Exception("builtin function requires at least one argument"); if (!(arg[0].isString())) throw Exception("first argument to builtin must be the name of a function (i.e., a string)"); FuncPtr funcDef; string fname = arg[0].getContentsAsString(); Context *context = eval->getContext(); if (!context->lookupFunction(fname,funcDef)) throw Exception(std::string("function ") + fname + " undefined!"); funcDef->updateCode(eval); if (funcDef->scriptFlag) throw Exception("cannot use feval on a script"); ArrayVector newarg(arg); newarg.pop_front(); bool flagsave = eval->getStopOverload(); eval->setStopOverload(true); ArrayVector tmp(funcDef->evaluateFunction(eval,newarg,nargout)); eval->setStopOverload(flagsave); return tmp; } //! //@Module FEVAL Evaluate a Function //@@Section FREEMAT //@@Usage //The @|feval| function executes a function using its name. //The syntax of @|feval| is //@[ // [y1,y2,...,yn] = feval(f,x1,x2,...,xm) //@] //where @|f| is the name of the function to evaluate, and //@|xi| are the arguments to the function, and @|yi| are the //return values. // //Alternately, @|f| can be a function handle to a function //(see the section on @|function handles| for more information). // //Finally, FreeMat also supports @|f| being a user defined class //in which case it will atttempt to invoke the @|subsref| method //of the class. //@@Example //Here is an example of using @|feval| to call the @|cos| //function indirectly. //@< //feval('cos',pi/4) //@> //Now, we call it through a function handle //@< //c = @cos //feval(c,pi/4) //@> //Here we construct an inline object (which is a user-defined class) //and use @|feval| to call it //@< //afunc = inline('cos(t)+sin(t)','t') //feval(afunc,pi) //afunc(pi) //@> //In both cases, (the @|feval| call and the direct invokation), FreeMat //calls the @|subsref| method of the class, which computes the requested //function. //@@Tests //@$"y = feval(@cos,pi)","-1","close" //@$"y = feval('cos',pi)","-1","close" //@$"y = feval(inline('cos(t)'),pi)","-1","close" //! ArrayVector FevalFunction(int nargout, const ArrayVector& arg,Interpreter* eval){ if (arg.size() == 0) throw Exception("feval function requires at least one argument"); if (!(arg[0].isString()) && (arg[0].dataClass() != FM_FUNCPTR_ARRAY)) throw Exception("first argument to feval must be the name of a function (i.e., a string) a function handle, or a user defined class"); FuncPtr funcDef; if (arg[0].isString()) { string fname = arg[0].getContentsAsString(); Context *context = eval->getContext(); if (!context->lookupFunction(fname,funcDef)) throw Exception(std::string("function ") + fname + " undefined!"); } else { if (!arg[0].isScalar()) throw Exception("function handle argument to feval must be a scalar"); const FuncPtr *fp = (const FuncPtr *) arg[0].getDataPointer(); funcDef = (FuncPtr) fp[0]; if (!funcDef) return ArrayVector(); } funcDef->updateCode(eval); if (funcDef->scriptFlag) throw Exception("cannot use feval on a script"); ArrayVector newarg(arg); newarg.pop_front(); return(funcDef->evaluateFunction(eval,newarg,nargout)); } //! //@Module DOCLI Start a Command Line Interface //@@Section FREEMAT //@@Usage //The @|docli| function is the main function that you interact with //when you run FreeMat. I am not sure why you would want to use //it, but hey - its there if you want to use it. //! //! //@Module STARTUP Startup Script //@@Section FREEMAT //@@Usage //Upon starting, FreeMat searches for a script names @|startup.m|, and //if it finds it, it executes it. This script can be in the current //directory, or on the FreeMat path (set using @|setpath|). The contents //of startup.m must be a valid script (not a function). //! ArrayVector DoCLIFunction(int nargout, const ArrayVector& arg, Interpreter* eval) { Context *context = eval->getContext(); FuncPtr funcDef; if (context->lookupFunction("startup",funcDef)) { funcDef->updateCode(eval); if (funcDef->scriptFlag) { try { eval->block(((MFunctionDef*)funcDef)->code); } catch (Exception& e) { eval->errorMessage("Startup script error:\n" + e.getMessageCopy()); } } else { eval->outputMessage(string("startup.m must be a script")); } } eval->doCLI(); return ArrayVector(); } //! //@Module REPMAT Array Replication Function //@@Section ARRAY //@@Usage //The @|repmat| function replicates an array the specified //number of times. The source and destination arrays may //be multidimensional. There are three distinct syntaxes for //the @|repmap| function. The first form: //@[ // y = repmat(x,n) //@] //replicates the array @|x| on an @|n-times-n| tiling, to create //a matrix @|y| that has @|n| times as many rows and columns //as @|x|. The output @|y| will match @|x| in all remaining //dimensions. The second form is //@[ // y = repmat(x,m,n) //@] //And creates a tiling of @|x| with @|m| copies of @|x| in the //row direction, and @|n| copies of @|x| in the column direction. //The final form is the most general //@[ // y = repmat(x,[m n p...]) //@] //where the supplied vector indicates the replication factor in //each dimension. //@@Example //Here is an example of using the @|repmat| function to replicate //a row 5 times. Note that the same effect can be accomplished //(although somewhat less efficiently) by a multiplication. //@< //x = [1 2 3 4] //y = repmat(x,[5,1]) //@> //The @|repmat| function can also be used to create a matrix of scalars //or to provide replication in arbitrary dimensions. Here we use it to //replicate a 2D matrix into a 3D volume. //@< //x = [1 2;3 4] //y = repmat(x,[1,1,3]) //@> //! #define MAX(a,b) ((a) > (b) ? (a) : (b)) ArrayVector RepMatFunction(int nargout, const ArrayVector& arg) { int i, j, k; if (arg.size() < 2) throw Exception("repmat function requires at least two arguments"); Array x(arg[0]); if (x.isEmpty()) return singleArrayVector(Array::emptyConstructor()); Dimensions repcount; // Case 1, look for a scalar second argument if ((arg.size() == 2) && (arg[1].isScalar())) { Array t(arg[1]); repcount.set(0,t.getContentsAsIntegerScalar()); repcount.set(1,t.getContentsAsIntegerScalar()); } // Case 2, look for two scalar arguments else if ((arg.size() == 3) && (arg[1].isScalar()) && (arg[2].isScalar())) { Array t(arg[1]); repcount.set(0,t.getContentsAsIntegerScalar()); t = arg[2]; repcount.set(1,t.getContentsAsIntegerScalar()); } // Case 3, look for a vector second argument else { if (arg.size() > 2) throw Exception("unrecognized form of arguments for repmat function"); Array t(arg[1]); t.promoteType(FM_INT32); if (t.getLength() > maxDims) throw Exception("replication request exceeds maxDims constant - either rebuild FreeMat with a higher maxDims constant, or shorten the requested replication array"); int32 *dp = (int32*) t.getDataPointer(); for (i=0;i //! ArrayVector SystemFunction(int nargout, const ArrayVector& arg) { if (arg.size() != 1) throw Exception("System function takes one string argument"); string systemArg = arg[0].getContentsAsString(); ArrayVector retval; if (systemArg.size() == 0) return retval; stringVector cp(DoSystemCallCaptured(systemArg)); Array* np = new Array[cp.size()]; for (int i=0;i //Now we permute a larger n-dimensional array: //@< //A = randn(13,5,7,2); //size(A) //B = permute(A,[3,4,2,1]); //size(B) //@> //@@Tests //@$"y=permute([1,2;3,4],[2,1])","[1,3;2,4]","exact" //@$"y=size(permute(randn(13,5,7,2),[3,4,2,1]))","[7,2,5,13]","exact" //@{ test_permute1.m //function test_val = test_permute1 //z = rand(3,5,2,4,7); //perm = [3,5,1,4,2]; //sizez = size(z); //y = permute(z,perm); //sizey = size(y); //test_val = all(sizey == sizez(perm)); //@} //! ArrayVector PermuteFunction(int nargout, const ArrayVector& arg) { if (arg.size() < 2) throw Exception("permute requires 2 inputs, the array to permute, and the permutation vector"); Array A(arg[0]); Array permutation(arg[1]); permutation.promoteType(FM_INT32); int Adims = A.dimensions().getLength(); if (permutation.getLength() != A.dimensions().getLength()) throw Exception("permutation vector must contain as many elements as the array to permute has dimensions"); // Check that it is, in fact a permutation MemBlock p(Adims); bool *d = &p; for (int i=0;i Adims)) throw Exception("permutation vector elements are limited to 1..ndims(A), where A is the array to permute"); d[dp[i]-1] = true; } // Check that all are covered for (int i=0;i=ym| and @|xn>=yn|. Otherwise //@|conv2| returns an empty matrix. //\end{itemize} //@@Function Internals //The convolution is computed explicitly using the definition: //\[ // Z(m,n) = \sum_{k} \sum_{j} X(k,j) Y(m-k,n-j) //\] //If the full output is requested, then @|m| ranges over @|0 <= m < xm+ym-1| //and @|n| ranges over @|0 <= n < xn+yn-1|. For the case where @|shape| //is @|'same'|, the output ranges over @|(ym-1)/2 <= m < xm + (ym-1)/2| //and @|(yn-1)/2 <= n < xn + (yn-1)/2|. //! // Summation limits... // sum a(i,j) b(m-i,n-j) // Need // 0 <= m-i <= Bm-1 // Can rewrite this as: // -m <= -i <= Bm-1-m // m >= i >= m+1-Bm // m+1-Bm <= i <= m // 0 <= n-j <= Bn-1 // n >= j >= n+1-Bn // n+1-Bn <= j <= n // And // 0 <= i <= Am-1 // 0 <= j <= An-1 // So // m+1-Bm <= i <= m // 0 <= i <= Am-1 // max(0,m+1-Bm) <= i <= min(m,Am-1) // // n+1-Bn <= j <= n // 0 <= j <= An-1 // max(0,n+1-Bn) <= j <= min(n,An-1) // // // max(0,m+1-Bm) <= i <= min(m,Am-1) // max(0,n+1-Bn) <= j <= min(n,An-1) template static void Conv2MainReal(T* C, const T* A, const T*B, int Am, int An, int Bm, int Bn, int Cm, int Cn, int Cm_offset, int Cn_offset) { for (int n=0;n static void Conv2MainComplex(T* C, const T* A, const T*B, int Am, int An, int Bm, int Bn, int Cm, int Cn, int Cm_offset, int Cn_offset) { for (int n=0;n(cp, (const float*) X.getDataPointer(), (const float*) Y.getDataPointer(), X.getDimensionLength(0), X.getDimensionLength(1), Y.getDimensionLength(0), Y.getDimensionLength(1), Cm, Cn, Cm_offset, Cn_offset); return Array(FM_FLOAT,Dimensions(Cm,Cn),cp); } case FM_DOUBLE: { double *cp = (double*) Array::allocateArray(FM_DOUBLE,Cm*Cn); Conv2MainReal(cp, (const double*) X.getDataPointer(), (const double*) Y.getDataPointer(), X.getDimensionLength(0), X.getDimensionLength(1), Y.getDimensionLength(0), Y.getDimensionLength(1), Cm, Cn, Cm_offset, Cn_offset); return Array(FM_DOUBLE,Dimensions(Cm,Cn),cp); } case FM_INT32: { int32 *cp = (int32*) Array::allocateArray(FM_INT32,Cm*Cn); Conv2MainReal(cp, (const int32*) X.getDataPointer(), (const int32*) Y.getDataPointer(), X.getDimensionLength(0), X.getDimensionLength(1), Y.getDimensionLength(0), Y.getDimensionLength(1), Cm, Cn, Cm_offset, Cn_offset); return Array(FM_INT32,Dimensions(Cm,Cn),cp); } case FM_INT64: { int64 *cp = (int64*) Array::allocateArray(FM_INT64,Cm*Cn); Conv2MainReal(cp, (const int64*) X.getDataPointer(), (const int64*) Y.getDataPointer(), X.getDimensionLength(0), X.getDimensionLength(1), Y.getDimensionLength(0), Y.getDimensionLength(1), Cm, Cn, Cm_offset, Cn_offset); return Array(FM_INT64,Dimensions(Cm,Cn),cp); } case FM_COMPLEX: { float *cp = (float*) Array::allocateArray(FM_COMPLEX,Cm*Cn); Conv2MainComplex(cp, (const float*) X.getDataPointer(), (const float*) Y.getDataPointer(), X.getDimensionLength(0), X.getDimensionLength(1), Y.getDimensionLength(0), Y.getDimensionLength(1), Cm, Cn, Cm_offset, Cn_offset); return Array(FM_COMPLEX,Dimensions(Cm,Cn),cp); } case FM_DCOMPLEX: { double *cp = (double*) Array::allocateArray(FM_DCOMPLEX,Cm*Cn); Conv2MainComplex(cp, (const double*) X.getDataPointer(), (const double*) Y.getDataPointer(), X.getDimensionLength(0), X.getDimensionLength(1), Y.getDimensionLength(0), Y.getDimensionLength(1), Cm, Cn, Cm_offset, Cn_offset); return Array(FM_DCOMPLEX,Dimensions(Cm,Cn),cp); } } } static ArrayVector Conv2FunctionFullXY(Array X, Array Y) { int Cm, Cn, Cm_offset, Cn_offset; Cm = X.getDimensionLength(0) + Y.getDimensionLength(0) - 1; Cn = X.getDimensionLength(1) + Y.getDimensionLength(1) - 1; Cm_offset = 0; Cn_offset = 0; return singleArrayVector(Conv2FunctionDispatch(X,Y,Cm,Cn,Cm_offset,Cn_offset)); } static ArrayVector Conv2FunctionSameXY(Array X, Array Y) { int Cm, Cn, Cm_offset, Cn_offset; Cm = X.getDimensionLength(0); Cn = X.getDimensionLength(1); Cm_offset = (int) floor((double)((Y.getDimensionLength(0)-1)/2)); Cn_offset = (int) floor((double)((Y.getDimensionLength(1)-1)/2)); return singleArrayVector(Conv2FunctionDispatch(X,Y,Cm,Cn,Cm_offset,Cn_offset)); } static ArrayVector Conv2FunctionValidXY(Array X, Array Y) { int Cm, Cn, Cm_offset, Cn_offset; Cm = X.getDimensionLength(0)-Y.getDimensionLength(0)+1; Cn = X.getDimensionLength(1)-Y.getDimensionLength(1)+1; if ((Cm <= 0) || (Cn <= 0)) return singleArrayVector(Array::emptyConstructor()); Cm_offset = Y.getDimensionLength(0)-1; Cn_offset = Y.getDimensionLength(1)-1; return singleArrayVector(Conv2FunctionDispatch(X,Y,Cm,Cn,Cm_offset,Cn_offset)); } ArrayVector Conv2FunctionXY(Array X, Array Y, string type) { // Check the arguments if (X.isReferenceType() || Y.isReferenceType()) throw Exception("cannot apply conv2 to reference types."); if (!X.is2D() || !Y.is2D()) throw Exception("arguments must be matrices, not n-dimensional arrays."); TypeCheck(X,Y,false); if (type == "FULL") return Conv2FunctionFullXY(X,Y); if (type == "SAME") return Conv2FunctionSameXY(X,Y); if (type == "VALID") return Conv2FunctionValidXY(X,Y); throw Exception("could not recognize the arguments to conv2"); } ArrayVector Conv2FunctionRCX(Array hcol, Array hrow, Array X, string type) { if (hcol.isReferenceType() || hrow.isReferenceType() || X.isReferenceType()) throw Exception("cannot apply conv2 to reference types."); if (!hcol.is2D() || !hrow.is2D() || !X.is2D()) throw Exception("arguments must be matrices, not n-dimensional arrays."); Dimensions t(hcol.getLength(),1); hcol.reshape(t); t = Dimensions(1,hrow.getLength()); hrow.reshape(t); ArrayVector rvec; rvec = Conv2FunctionXY(X,hcol,type); rvec = Conv2FunctionXY(rvec.back(),hrow,type); return rvec; } ArrayVector Conv2Function(int nargout, const ArrayVector& arg) { // Call the right function based on the arguments if (arg.size() < 2) throw Exception("conv2 requires at least 2 arguments"); if (arg.size() == 2) return Conv2FunctionXY(arg[0],arg[1],"FULL"); if ((arg.size() == 3) && (arg[2].isString())) return Conv2FunctionXY(arg[0],arg[1],arg[2].getContentsAsStringUpper()); if (arg.size() == 3) return Conv2FunctionRCX(arg[0],arg[1],arg[2],"FULL"); if ((arg.size() == 4) && (arg[3].isString())) return Conv2FunctionRCX(arg[0],arg[1],arg[2], arg[3].getContentsAsStringUpper()); throw Exception("could not recognize which form of conv2 was requested - check help conv2 for details"); }