X-Git-Url: https://www.ginac.de/ginac.git//ginac.git?p=ginac.git;a=blobdiff_plain;f=ginac%2Fmatrix.cpp;h=66f3999a4e37574d142f57d4dc68d486c572864c;hp=f92a7c56a481ec61f935f692d0a5690cad5a78ff;hb=4bce949dac6328bc9fcfcd3f875fd422055745f4;hpb=e7cc6a764ff67b5885d6633385fac23ccc1dc9a7 diff --git a/ginac/matrix.cpp b/ginac/matrix.cpp index f92a7c56..66f3999a 100644 --- a/ginac/matrix.cpp +++ b/ginac/matrix.cpp @@ -3,7 +3,7 @@ * Implementation of symbolic matrices */ /* - * GiNaC Copyright (C) 1999-2002 Johannes Gutenberg University Mainz, Germany + * GiNaC Copyright (C) 1999-2010 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 @@ -17,52 +17,49 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include -#include -#include -#include - #include "matrix.h" #include "numeric.h" #include "lst.h" #include "idx.h" #include "indexed.h" +#include "add.h" #include "power.h" #include "symbol.h" +#include "operators.h" #include "normal.h" -#include "print.h" #include "archive.h" #include "utils.h" +#include +#include +#include +#include +#include +#include + namespace GiNaC { -GINAC_IMPLEMENT_REGISTERED_CLASS(matrix, basic) +GINAC_IMPLEMENT_REGISTERED_CLASS_OPT(matrix, basic, + print_func(&matrix::do_print). + print_func(&matrix::do_print_latex). + print_func(&matrix::do_print_tree). + print_func(&matrix::do_print_python_repr)) ////////// -// default ctor, dtor, copy ctor, assignment operator and helpers: +// default constructor ////////// /** Default ctor. Initializes to 1 x 1-dimensional zero-matrix. */ -matrix::matrix() : inherited(TINFO_matrix), row(1), col(1) -{ - m.push_back(_ex0); -} - -void matrix::copy(const matrix & other) +matrix::matrix() : row(1), col(1), m(1, _ex0) { - inherited::copy(other); - row = other.row; - col = other.col; - m = other.m; // STL's vector copying invoked here + setflag(status_flags::not_shareable); } -DEFAULT_DESTROY(matrix) - ////////// -// other ctors +// other constructors ////////// // public @@ -71,33 +68,36 @@ DEFAULT_DESTROY(matrix) * * @param r number of rows * @param c number of cols */ -matrix::matrix(unsigned r, unsigned c) - : inherited(TINFO_matrix), row(r), col(c) +matrix::matrix(unsigned r, unsigned c) : row(r), col(c), m(r*c, _ex0) { - m.resize(r*c, _ex0); + setflag(status_flags::not_shareable); } // 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) {} + : row(r), col(c), m(m2) +{ + setflag(status_flags::not_shareable); +} /** Construct matrix from (flat) list of elements. If the list has fewer * elements than the matrix, the remaining matrix elements are set to zero. * If the list has more elements than the matrix, the excessive elements are * thrown away. */ matrix::matrix(unsigned r, unsigned c, const lst & l) - : inherited(TINFO_matrix), row(r), col(c) + : row(r), col(c), m(r*c, _ex0) { - m.resize(r*c, _ex0); + setflag(status_flags::not_shareable); - for (unsigned i=0; i= r) break; // matrix smaller than list: throw away excessive elements - m[y*c+x] = l.op(i); + m[y*c+x] = *it; } } @@ -105,19 +105,25 @@ matrix::matrix(unsigned r, unsigned c, const lst & l) // archiving ////////// -matrix::matrix(const archive_node &n, const lst &sym_lst) : inherited(n, sym_lst) +void matrix::read_archive(const archive_node &n, lst &sym_lst) { + inherited::read_archive(n, sym_lst); + 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++) { + // XXX: default ctor inserts a zero element, we need to erase it here. + m.pop_back(); + archive_node::archive_node_cit first = n.find_first("m"); + archive_node::archive_node_cit last = n.find_last("m"); + ++last; + for (archive_node::archive_node_cit i=first; i != last; ++i) { ex e; - if (n.find_ex("m", e, sym_lst, i)) - m.push_back(e); - else - break; + n.find_ex_by_loc(i, e, sym_lst); + m.push_back(e); } } +GINAC_BIND_UNARCHIVER(matrix); void matrix::archive(archive_node &n) const { @@ -131,67 +137,69 @@ void matrix::archive(archive_node &n) const } } -DEFAULT_UNARCHIVE(matrix) - ////////// // functions overriding virtual functions from base classes ////////// // public -void matrix::print(const print_context & c, unsigned level) const +void matrix::print_elements(const print_context & c, const char *row_start, const char *row_end, const char *row_sep, const char *col_sep) const { - if (is_a(c)) { - - inherited::print(c, level); - - } else { - - if (is_a(c)) - c.s << class_name() << '('; - - c.s << "["; - for (unsigned y=0; y(c)) - c.s << ')'; +void matrix::do_print(const print_context & c, unsigned level) const +{ + c.s << "["; + print_elements(c, "[", "]", ",", ","); + c.s << "]"; +} - } +void matrix::do_print_latex(const print_latex & c, unsigned level) const +{ + c.s << "\\left(\\begin{array}{" << std::string(col,'c') << "}"; + print_elements(c, "", "", "\\\\", "&"); + c.s << "\\end{array}\\right)"; +} + +void matrix::do_print_python_repr(const print_python_repr & c, unsigned level) const +{ + c.s << class_name() << '('; + print_elements(c, "[", "]", ",", ","); + c.s << ')'; } /** nops is defined to be rows x columns. */ -unsigned matrix::nops() const +size_t matrix::nops() const { - return row*col; + return static_cast(row) * static_cast(col); } /** returns matrix entry at position (i/col, i%col). */ -ex matrix::op(int i) const +ex matrix::op(size_t i) const { + GINAC_ASSERT(i=0); GINAC_ASSERT(isetflag(status_flags::dynallocated | - status_flags::evaluated ); + status_flags::evaluated); } -ex matrix::subs(const lst & ls, const lst & lr, bool no_pattern) const +ex matrix::subs(const exmap & mp, unsigned options) const { exvector m2(row * col); for (unsigned r=0; rconjugate(); + if (ev) { + ev->push_back(x); + continue; + } + if (are_ex_trivially_equal(x, *i)) { + continue; + } + ev = new exvector; + ev->reserve(m.size()); + for (exvector::const_iterator j=m.begin(); j!=i; ++j) { + ev->push_back(*j); + } + ev->push_back(x); + } + if (ev) { + ex result = matrix(row, col, *ev); + delete ev; + return result; + } + return *this; +} + +ex matrix::real_part() const +{ + exvector v; + v.reserve(m.size()); + for (exvector::const_iterator i=m.begin(); i!=m.end(); ++i) + v.push_back(i->real_part()); + return matrix(row, col, v); +} + +ex matrix::imag_part() const +{ + exvector v; + v.reserve(m.size()); + for (exvector::const_iterator i=m.begin(); i!=m.end(); ++i) + v.push_back(i->imag_part()); + return matrix(row, col, v); } // protected @@ -350,7 +404,7 @@ ex matrix::add_indexed(const ex & self, const ex & other) const GINAC_ASSERT(self.nops() == 2 || self.nops() == 3); // Only add two matrices - if (is_ex_of_type(other.op(0), matrix)) { + if (is_a(other.op(0))) { GINAC_ASSERT(other.nops() == 2 || other.nops() == 3); const matrix &self_matrix = ex_to(self.op(0)); @@ -401,7 +455,7 @@ bool matrix::contract_with(exvector::iterator self, exvector::iterator other, ex GINAC_ASSERT(is_a(self->op(0))); // Only contract with other matrices - if (!is_ex_of_type(other->op(0), matrix)) + if (!is_a(other->op(0))) return false; GINAC_ASSERT(other->nops() == 2 || other->nops() == 3); @@ -545,10 +599,11 @@ matrix matrix::mul(const matrix & other) const for (unsigned r1=0; r1rows(); ++r1) { for (unsigned c=0; ccols(); ++c) { + // Quick test: can we shortcut? if (m[r1*col+c].is_zero()) continue; for (unsigned r2=0; r2(expn)) { // Integer cases are computed by successive multiplication, using the // obvious shortcut of storing temporaries, like A^4 == (A*A)*(A*A). if (expn.info(info_flags::integer)) { @@ -605,17 +660,19 @@ matrix matrix::pow(const ex & expn) const matrix C(row,col); for (unsigned r=0; rcols()*this->rows()); @@ -693,12 +750,12 @@ ex matrix::determinant(unsigned algo) const unsigned sparse_count = 0; // counts non-zero elements exvector::const_iterator r = m.begin(), rend = m.end(); while (r != rend) { - lst srl; // symbol replacement list + if (!r->info(info_flags::numeric)) + numeric_flag = false; + exmap srl; // symbol replacement list ex rtest = r->to_rational(srl); if (!rtest.is_zero()) ++sparse_count; - if (!rtest.info(info_flags::numeric)) - numeric_flag = false; if (!rtest.info(info_flags::crational_polynomial) && rtest.info(info_flags::rational_function)) normal_flag = true; @@ -727,7 +784,7 @@ ex matrix::determinant(unsigned algo) const else return m[0].expand(); } - + // Compute the determinant switch(algo) { case determinant_algo::gauss: { @@ -813,7 +870,7 @@ ex matrix::determinant(unsigned algo) const * * @return the sum of diagonal elements * @exception logic_error (matrix not square) */ -ex matrix::trace(void) const +ex matrix::trace() const { if (row != col) throw (std::logic_error("matrix::trace(): matrix not square")); @@ -823,7 +880,7 @@ ex matrix::trace(void) const tr += m[r*col+r]; if (tr.info(info_flags::rational_function) && - !tr.info(info_flags::crational_polynomial)) + !tr.info(info_flags::crational_polynomial)) return tr.normal(); else return tr.expand(); @@ -841,7 +898,7 @@ ex matrix::trace(void) const * @return characteristic polynomial as new expression * @exception logic_error (matrix not square) * @see matrix::determinant() */ -ex matrix::charpoly(const symbol & lambda) const +ex matrix::charpoly(const ex & lambda) const { if (row != col) throw (std::logic_error("matrix::charpoly(): matrix not square")); @@ -858,27 +915,30 @@ ex matrix::charpoly(const symbol & lambda) const // trapped and we use Leverrier's algorithm which goes as row^3 for // every coefficient. The expensive part is the matrix multiplication. if (numeric_flag) { + matrix B(*this); ex c = B.trace(); - ex poly = power(lambda,row)-c*power(lambda,row-1); + ex poly = power(lambda, row) - c*power(lambda, row-1); for (unsigned i=1; imul(B); - c = B.trace()/ex(i+1); - poly -= c*power(lambda,row-i-1); + c = B.trace() / ex(i+1); + poly -= c*power(lambda, row-i-1); } if (row%2) return -poly; else return poly; - } + + } else { - matrix M(*this); - for (unsigned r=0; rrows(); const unsigned n = this->cols(); @@ -1026,6 +1087,29 @@ matrix matrix::solve(const matrix & vars, } +/** Compute the rank of this matrix. */ +unsigned matrix::rank() const +{ + // Method: + // Transform this matrix into upper echelon form and then count the + // number of non-zero rows. + + GINAC_ASSERT(row*col==m.capacity()); + + // Actually, any elimination scheme will do since we are only + // interested in the echelon matrix' zeros. + matrix to_eliminate = *this; + to_eliminate.fraction_free_elimination(); + + unsigned r = row*col; // index of last non-zero element + while (r--) { + if (!to_eliminate.m[r].is_zero()) + return 1+r/col; + } + return 0; +} + + // protected /** Recursive determinant for small matrices having at least one symbolic @@ -1038,7 +1122,7 @@ matrix matrix::solve(const matrix & vars, * * @return the determinant as a new expression (in expanded form) * @see matrix::determinant() */ -ex matrix::determinant_minor(void) const +ex matrix::determinant_minor() const { // for small matrices the algorithm does not make any sense: const unsigned n = this->cols(); @@ -1141,7 +1225,7 @@ ex matrix::determinant_minor(void) const Pkey[j] = Pkey[j-1]+1; } while(fc); // next column, so change the role of A and B: - A = B; + A.swap(B); B.clear(); } @@ -1167,8 +1251,8 @@ int matrix::gauss_elimination(const bool det) int sign = 1; unsigned r0 = 0; - for (unsigned r1=0; (r1 0) sign = -sign; for (unsigned r2=r0+1; r2m[r2*n+r1].is_zero()) { + if (!this->m[r2*n+c0].is_zero()) { // yes, there is something to do in this row - ex piv = this->m[r2*n+r1] / this->m[r0*n+r1]; - for (unsigned c=r1+1; cm[r2*n+c0] / this->m[r0*n+c0]; + for (unsigned c=c0+1; cm[r2*n+c] -= piv * this->m[r0*n+c]; if (!this->m[r2*n+c].info(info_flags::numeric)) this->m[r2*n+c] = this->m[r2*n+c].normal(); } } // fill up left hand side with zeros - for (unsigned c=0; c<=r1; ++c) + for (unsigned c=r0; c<=c0; ++c) this->m[r2*n+c] = _ex0; } if (det) { @@ -1199,7 +1283,12 @@ int matrix::gauss_elimination(const bool det) ++r0; } } - + // clear remaining rows + for (unsigned r=r0+1; rm[r*n+c] = _ex0; + } + return sign; } @@ -1221,8 +1310,8 @@ int matrix::division_free_elimination(const bool det) int sign = 1; unsigned r0 = 0; - for (unsigned r1=0; (r10) sign = -sign; for (unsigned r2=r0+1; r2m[r2*n+c] = (this->m[r0*n+r1]*this->m[r2*n+c] - this->m[r2*n+r1]*this->m[r0*n+c]).expand(); + for (unsigned c=c0+1; cm[r2*n+c] = (this->m[r0*n+c0]*this->m[r2*n+c] - this->m[r2*n+c0]*this->m[r0*n+c]).expand(); // fill up left hand side with zeros - for (unsigned c=0; c<=r1; ++c) + for (unsigned c=r0; c<=c0; ++c) this->m[r2*n+c] = _ex0; } if (det) { @@ -1246,7 +1335,12 @@ int matrix::division_free_elimination(const bool det) ++r0; } } - + // clear remaining rows + for (unsigned r=r0+1; rm[r*n+c] = _ex0; + } + return sign; } @@ -1271,7 +1365,7 @@ int matrix::fraction_free_elimination(const bool det) // // Bareiss (fraction-free) elimination in addition divides that element // by m[k-1](k-1,k-1) for k>1, where it can be shown by means of the - // Sylvester determinant that this really divides m[k+1](r,c). + // Sylvester identity that this really divides m[k+1](r,c). // // We also allow rational functions where the original prove still holds. // However, we must care for numerator and denominator separately and @@ -1308,7 +1402,7 @@ int matrix::fraction_free_elimination(const bool det) // makes things more complicated than they need to be. matrix tmp_n(*this); matrix tmp_d(m,n); // for denominators, if needed - lst srl; // symbol replacement list + exmap srl; // symbol replacement list exvector::const_iterator cit = this->m.begin(), citend = this->m.end(); exvector::iterator tmp_n_it = tmp_n.m.begin(), tmp_d_it = tmp_d.m.begin(); while (cit != citend) { @@ -1319,28 +1413,37 @@ int matrix::fraction_free_elimination(const bool det) } unsigned r0 = 0; - for (unsigned r1=0; (r1=0) { - if (indx>0) { + } else { + if (indx>r0) { + // Matrix needs pivoting, swap rows r0 and indx of tmp_n and tmp_d. sign = -sign; - // tmp_n's rows r0 and indx were swapped, do the same in tmp_d: - for (unsigned c=r1; cm.begin(), itend = this->m.end(); tmp_n_it = tmp_n.m.begin(); tmp_d_it = tmp_d.m.begin(); while (it != itend) - *it++ = ((*tmp_n_it++)/(*tmp_d_it++)).subs(srl); + *it++ = ((*tmp_n_it++)/(*tmp_d_it++)).subs(srl, subs_options::no_pattern); return sign; } @@ -1399,11 +1508,11 @@ int matrix::pivot(unsigned ro, unsigned co, bool symbolic) ++k; } else { // search largest element in column co beginning at row ro - GINAC_ASSERT(is_a(this->m[k*col+co])); + GINAC_ASSERT(is_exactly_a(this->m[k*col+co])); unsigned kmax = k+1; numeric mmax = abs(ex_to(m[kmax*col+co])); while (kmax(this->m[kmax*col+co])); + GINAC_ASSERT(is_exactly_a(this->m[kmax*col+co])); numeric tmp = ex_to(this->m[kmax*col+co]); if (abs(tmp) > mmax) { mmax = tmp; @@ -1428,36 +1537,152 @@ int matrix::pivot(unsigned ro, unsigned co, bool symbolic) return k; } +/** Function to check that all elements of the matrix are zero. + */ +bool matrix::is_zero_matrix() const +{ + for (exvector::const_iterator i=m.begin(); i!=m.end(); ++i) + if(!(i->is_zero())) + return false; + return true; +} + ex lst_to_matrix(const lst & l) { + lst::const_iterator itr, itc; + // Find number of rows and columns - unsigned rows = l.nops(), cols = 0, i, j; - for (i=0; i cols) - cols = l.op(i).nops(); + size_t rows = l.nops(), cols = 0; + for (itr = l.begin(); itr != l.end(); ++itr) { + if (!is_a(*itr)) + throw (std::invalid_argument("lst_to_matrix: argument must be a list of lists")); + if (itr->nops() > cols) + cols = itr->nops(); + } // Allocate and fill matrix - matrix &m = *new matrix(rows, cols); - m.setflag(status_flags::dynallocated); - for (i=0; i j) - m(i, j) = l.op(i).op(j); - else - m(i, j) = _ex0; - return m; + matrix &M = *new matrix(rows, cols); + M.setflag(status_flags::dynallocated); + + unsigned i; + for (itr = l.begin(), i = 0; itr != l.end(); ++itr, ++i) { + unsigned j; + for (itc = ex_to(*itr).begin(), j = 0; itc != ex_to(*itr).end(); ++itc, ++j) + M(i, j) = *itc; + } + + return M; } ex diag_matrix(const lst & l) { - unsigned dim = l.nops(); + lst::const_iterator it; + size_t dim = l.nops(); + + // Allocate and fill matrix + matrix &M = *new matrix(dim, dim); + M.setflag(status_flags::dynallocated); - matrix &m = *new matrix(dim, dim); - m.setflag(status_flags::dynallocated); - for (unsigned i=0; i 10 || c > 10); + bool single_row = (r == 1 || c == 1); + + for (unsigned i=0; im.rows() || c+1>m.cols() || m.cols()<2 || m.rows()<2) + throw std::runtime_error("minor_matrix(): index out of bounds"); + + const unsigned rows = m.rows()-1; + const unsigned cols = m.cols()-1; + matrix &M = *new matrix(rows, cols); + M.setflag(status_flags::dynallocated | status_flags::evaluated); + + unsigned ro = 0; + unsigned ro2 = 0; + while (ro2m.rows() || c+nc>m.cols()) + throw std::runtime_error("sub_matrix(): index out of bounds"); + + matrix &M = *new matrix(nr, nc); + M.setflag(status_flags::dynallocated | status_flags::evaluated); + + for (unsigned ro=0; ro