/** @file matrix.cpp * * Implementation of symbolic matrices */ /* * GiNaC Copyright (C) 1999-2000 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 "archive.h" #include "utils.h" #include "debugmsg.h" #ifndef NO_GINAC_NAMESPACE namespace GiNaC { #endif // ndef NO_GINAC_NAMESPACE GINAC_IMPLEMENT_REGISTERED_CLASS(matrix, basic) ////////// // default constructor, destructor, copy constructor, assignment operator // and helpers: ////////// // public /** Default ctor. Initializes to 1 x 1-dimensional zero-matrix. */ matrix::matrix() : inherited(TINFO_matrix), row(1), col(1) { debugmsg("matrix default constructor",LOGLEVEL_CONSTRUCT); m.push_back(_ex0()); } matrix::~matrix() { debugmsg("matrix destructor",LOGLEVEL_DESTRUCT); } matrix::matrix(const matrix & other) { debugmsg("matrix copy constructor",LOGLEVEL_CONSTRUCT); copy(other); } const matrix & matrix::operator=(const matrix & other) { debugmsg("matrix operator=",LOGLEVEL_ASSIGNMENT); if (this != &other) { destroy(1); copy(other); } return *this; } // protected void matrix::copy(const matrix & other) { inherited::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) inherited::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(unsigned r, unsigned c) : inherited(TINFO_matrix), row(r), col(c) { debugmsg("matrix constructor from unsigned,unsigned",LOGLEVEL_CONSTRUCT); m.resize(r*c, _ex0()); } // protected /** Ctor from representation, for internal use only. */ matrix::matrix(unsigned r, unsigned c, const exvector & m2) : inherited(TINFO_matrix), row(r), col(c), m(m2) { debugmsg("matrix constructor from unsigned,unsigned,exvector",LOGLEVEL_CONSTRUCT); } ////////// // archiving ////////// /** Construct object from archive_node. */ matrix::matrix(const archive_node &n, const lst &sym_lst) : inherited(n, sym_lst) { debugmsg("matrix constructor from archive_node", LOGLEVEL_CONSTRUCT); if (!(n.find_unsigned("row", row)) || !(n.find_unsigned("col", col))) throw (std::runtime_error("unknown matrix dimensions in archive")); m.reserve(row * col); for (unsigned int i=0; true; i++) { ex e; if (n.find_ex("m", e, sym_lst, i)) m.push_back(e); else break; } } /** Unarchive the object. */ ex matrix::unarchive(const archive_node &n, const lst &sym_lst) { return (new matrix(n, sym_lst))->setflag(status_flags::dynallocated); } /** Archive the object. */ void matrix::archive(archive_node &n) const { inherited::archive(n); n.add_unsigned("row", row); n.add_unsigned("col", col); exvector::const_iterator i = m.begin(), iend = m.end(); while (i != iend) { n.add_ex("m", *i); i++; } } ////////// // functions overriding virtual functions from bases classes ////////// // public basic * matrix::duplicate() const { debugmsg("matrix duplicate",LOGLEVEL_DUPLICATE); return new matrix(*this); } void matrix::print(ostream & os, unsigned upper_precedence) const { debugmsg("matrix print",LOGLEVEL_PRINT); os << "[[ "; for (unsigned 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 exvector m2(row*col); --level; for (unsigned 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 (unsigned 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(const matrix & other) const { if (col != other.col || row != other.row) { throw (std::logic_error("matrix::add(): incompatible matrices")); } exvector sum(this->m); exvector::iterator i; exvector::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(const matrix & other) const { if (col != other.col || row != other.row) { throw (std::logic_error("matrix::sub(): incompatible matrices")); } exvector dif(this->m); exvector::iterator i; exvector::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(const matrix & other) const { if (col != other.row) { throw (std::logic_error("matrix::mul(): incompatible matrices")); } exvector prod(row*other.col); for (unsigned 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(unsigned ro, unsigned 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 { exvector trans(col*row); for (unsigned 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 (unsigned i=0; izero_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); unsigned last_assigned_sol=n+1; for (unsigned r=m; r>0; --r) { unsigned 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 (unsigned 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 (unsigned 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 (unsigned c=1; c<=last_assigned_sol-1; ++c) { sol.ffe_set(c,1,vars.ffe_get(c,1)); } /* for (unsigned 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 (unsigned r=1; r<=m; ++r) { ex e=0; for (unsigned 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 (unsigned r=1; r<=m; ++r) { ex e=0; for (unsigned 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(const matrix & 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 (unsigned r=0; r=0; --r) { sol[r*v.col+c] = tmp[r*tmp.col+c]; for (unsigned i=r+1; i