/** @file matrix.cpp * * Implementation of symbolic matrices */ /* * GiNaC Copyright (C) 1999 Johannes Gutenberg University Mainz, Germany * * 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 "matrix.h" #include "debugmsg.h" namespace GiNaC { ////////// // default constructor, destructor, copy constructor, assignment operator // and helpers: ////////// // public /** Default ctor. Initializes to 1 x 1-dimensional zero-matrix. */ matrix::matrix() : basic(TINFO_matrix), row(1), col(1) { debugmsg("matrix default constructor",LOGLEVEL_CONSTRUCT); m.push_back(exZERO()); } matrix::~matrix() { debugmsg("matrix destructor",LOGLEVEL_DESTRUCT); } matrix::matrix(matrix const & other) { debugmsg("matrix copy constructor",LOGLEVEL_CONSTRUCT); copy(other); } matrix const & matrix::operator=(matrix const & other) { debugmsg("matrix operator=",LOGLEVEL_ASSIGNMENT); if (this != &other) { destroy(1); copy(other); } return *this; } // protected void matrix::copy(matrix const & other) { basic::copy(other); row=other.row; col=other.col; m=other.m; // use STL's vector copying } void matrix::destroy(bool call_parent) { if (call_parent) basic::destroy(call_parent); } ////////// // other constructors ////////// // public /** Very common ctor. Initializes to r x c-dimensional zero-matrix. * * @param r number of rows * @param c number of cols */ matrix::matrix(int r, int c) : basic(TINFO_matrix), row(r), col(c) { debugmsg("matrix constructor from int,int",LOGLEVEL_CONSTRUCT); m.resize(r*c, exZERO()); } // protected /** Ctor from representation, for internal use only. */ matrix::matrix(int r, int c, vector const & m2) : basic(TINFO_matrix), row(r), col(c), m(m2) { debugmsg("matrix constructor from int,int,vector",LOGLEVEL_CONSTRUCT); } ////////// // functions overriding virtual functions from bases classes ////////// // public basic * matrix::duplicate() const { debugmsg("matrix duplicate",LOGLEVEL_DUPLICATE); return new matrix(*this); } /** nops is defined to be rows x columns. */ int matrix::nops() const { return row*col; } /** returns matrix entry at position (i/col, i%col). */ ex & matrix::let_op(int const i) { return m[i]; } /** expands the elements of a matrix entry by entry. */ ex matrix::expand(unsigned options) const { vector tmp(row*col); for (int i=0; i::const_iterator r=m.begin(); r!=m.end(); ++r) { if ((*r).has(other)) return true; } return false; } /** evaluate matrix entry by entry. */ ex matrix::eval(int level) const { debugmsg("matrix eval",LOGLEVEL_MEMBER_FUNCTION); // check if we have to do anything at all if ((level==1)&&(flags & status_flags::evaluated)) { return *this; } // emergency break if (level == -max_recursion_level) { throw (std::runtime_error("matrix::eval(): recursion limit exceeded")); } // eval() entry by entry vector m2(row*col); --level; for (int r=0; rsetflag(status_flags::dynallocated | status_flags::evaluated ); } /** evaluate matrix numerically entry by entry. */ ex matrix::evalf(int level) const { debugmsg("matrix evalf",LOGLEVEL_MEMBER_FUNCTION); // check if we have to do anything at all if (level==1) { return *this; } // emergency break if (level == -max_recursion_level) { throw (std::runtime_error("matrix::evalf(): recursion limit exceeded")); } // evalf() entry by entry vector m2(row*col); --level; for (int r=0; r(const_cast(other)); // compare number of rows if (row != o.rows()) { return row < o.rows() ? -1 : 1; } // compare number of columns if (col != o.cols()) { return col < o.cols() ? -1 : 1; } // equal number of rows and columns, compare individual elements int cmpval; for (int r=0; r matrices are equal; return 0; } ////////// // non-virtual functions in this class ////////// // public /** Sum of matrices. * * @exception logic_error (incompatible matrices) */ matrix matrix::add(matrix const & other) const { if (col != other.col || row != other.row) { throw (std::logic_error("matrix::add(): incompatible matrices")); } vector sum(this->m); vector::iterator i; vector::const_iterator ci; for (i=sum.begin(), ci=other.m.begin(); i!=sum.end(); ++i, ++ci) { (*i) += (*ci); } return matrix(row,col,sum); } /** Difference of matrices. * * @exception logic_error (incompatible matrices) */ matrix matrix::sub(matrix const & other) const { if (col != other.col || row != other.row) { throw (std::logic_error("matrix::sub(): incompatible matrices")); } vector dif(this->m); vector::iterator i; vector::const_iterator ci; for (i=dif.begin(), ci=other.m.begin(); i!=dif.end(); ++i, ++ci) { (*i) -= (*ci); } return matrix(row,col,dif); } /** Product of matrices. * * @exception logic_error (incompatible matrices) */ matrix matrix::mul(matrix const & other) const { if (col != other.row) { throw (std::logic_error("matrix::mul(): incompatible matrices")); } vector prod(row*other.col); for (int i=0; i=row || co<0 || co>=col) { throw (std::range_error("matrix::operator(): index out of range")); } return m[ro*col+co]; } /** Set individual elements manually. * * @exception range_error (index out of range) */ matrix & matrix::set(int ro, int co, ex value) { if (ro<0 || ro>=row || co<0 || co>=col) { throw (std::range_error("matrix::set(): index out of range")); } ensure_if_modifiable(); m[ro*col+co]=value; return *this; } /** Transposed of an m x n matrix, producing a new n x m matrix object that * represents the transposed. */ matrix matrix::transpose(void) const { vector trans(col*row); for (int r=0; r int permutation_sign(vector s) { if (s.size() < 2) return 0; int sigma=1; for (typename vector::iterator i=s.begin(); i!=s.end()-1; ++i) { for (typename vector::iterator j=i+1; j!=s.end(); ++j) { if (*i == *j) return 0; if (*i > *j) { iter_swap(i,j); sigma = -sigma; } } } return sigma; } /** Determinant built by application of the full permutation group. This * routine is only called internally by matrix::determinant(). */ ex determinant_symbolic_perm(const matrix & M) { GINAC_ASSERT(M.rows()==M.cols()); // cannot happen, just in case... if (M.rows()==1) { // speed things up return M(0,0); } ex det; ex term; vector sigma(M.cols()); for (int i=0; i::const_iterator r=m.begin(); r!=m.end(); ++r) { if (!(*r).info(info_flags::numeric)) { if (normalized) { return determinant_symbolic_minor(*this).normal(); } else { return determinant_symbolic_perm(*this); } } } // if it turns out that all elements are numeric return determinant_numeric(*this); } /** Trace of a matrix. * * @return the sum of diagonal elements * @exception logic_error (matrix not square) */ ex matrix::trace(void) const { if (row != col) { throw (std::logic_error("matrix::trace(): matrix not square")); } ex tr; for (int r=0; rzero_in_last_row)||(zero_in_this_row=n)); zero_in_last_row=zero_in_this_row; } #endif // def DO_GINAC_ASSERT // assemble solution matrix sol(n,1); int last_assigned_sol=n+1; for (int r=m; r>0; --r) { int first_non_zero=1; while ((first_non_zero<=n)&&(a.ffe_get(r,first_non_zero).is_zero())) { first_non_zero++; } if (first_non_zero>n) { // row consists only of zeroes, corresponding rhs must be 0 as well if (!b.ffe_get(r,1).is_zero()) { throw (std::runtime_error("matrix::fraction_free_elim(): singular matrix")); } } else { // assign solutions for vars between first_non_zero+1 and // last_assigned_sol-1: free parameters for (int c=first_non_zero+1; c<=last_assigned_sol-1; ++c) { sol.ffe_set(c,1,vars.ffe_get(c,1)); } ex e=b.ffe_get(r,1); for (int c=first_non_zero+1; c<=n; ++c) { e=e-a.ffe_get(r,c)*sol.ffe_get(c,1); } sol.ffe_set(first_non_zero,1, (e/a.ffe_get(r,first_non_zero)).normal()); last_assigned_sol=first_non_zero; } } // assign solutions for vars between 1 and // last_assigned_sol-1: free parameters for (int c=1; c<=last_assigned_sol-1; ++c) { sol.ffe_set(c,1,vars.ffe_get(c,1)); } /* for (int c=1; c<=n; ++c) { cout << vars.ffe_get(c,1) << "->" << sol.ffe_get(c,1) << endl; } */ #ifdef DO_GINAC_ASSERT // test solution with echelon matrix for (int r=1; r<=m; ++r) { ex e=0; for (int c=1; c<=n; ++c) { e=e+a.ffe_get(r,c)*sol.ffe_get(c,1); } if (!(e-b.ffe_get(r,1)).normal().is_zero()) { cout << "e=" << e; cout << "b.ffe_get(" << r<<",1)=" << b.ffe_get(r,1) << endl; cout << "diff=" << (e-b.ffe_get(r,1)).normal() << endl; } GINAC_ASSERT((e-b.ffe_get(r,1)).normal().is_zero()); } // test solution with original matrix for (int r=1; r<=m; ++r) { ex e=0; for (int c=1; c<=n; ++c) { e=e+ffe_get(r,c)*sol.ffe_get(c,1); } try { if (!(e-rhs.ffe_get(r,1)).normal().is_zero()) { cout << "e=" << e << endl; e.printtree(cout); ex en=e.normal(); cout << "e.normal()=" << en << endl; en.printtree(cout); cout << "rhs.ffe_get(" << r<<",1)=" << rhs.ffe_get(r,1) << endl; cout << "diff=" << (e-rhs.ffe_get(r,1)).normal() << endl; } } catch (...) { ex xxx=e-rhs.ffe_get(r,1); cerr << "xxx=" << xxx << endl << endl; } GINAC_ASSERT((e-rhs.ffe_get(r,1)).normal().is_zero()); } #endif // def DO_GINAC_ASSERT return sol; } /** Solve simultaneous set of equations. */ matrix matrix::solve(matrix const & v) const { if (!(row == col && col == v.row)) { throw (std::logic_error("matrix::solve(): incompatible matrices")); } // build the extended matrix of *this with v attached to the right matrix tmp(row,col+v.col); for (int r=0; r sol(v.row*v.col); for (int c=0; c=0; --r) { sol[r*v.col+c] = tmp[r*tmp.col+c]; for (int i=r+1; i