X-Git-Url: https://www.ginac.de/ginac.git//ginac.git?p=ginac.git;a=blobdiff_plain;f=ginac%2Fmatrix.cpp;h=2c191ff4f69c04d993b2724b16f13e13d445156e;hp=f219f27de0c3dcb1432bcc7d944b4ef8bcd63dcc;hb=c1285bb62f3a86454ca26260cf8b4352238a1fc5;hpb=956a3ad3779759028bfd742456ed9eafc3e85063 diff --git a/ginac/matrix.cpp b/ginac/matrix.cpp index f219f27d..2c191ff4 100644 --- a/ginac/matrix.cpp +++ b/ginac/matrix.cpp @@ -3,7 +3,7 @@ * Implementation of symbolic matrices */ /* - * GiNaC Copyright (C) 1999-2000 Johannes Gutenberg University Mainz, Germany + * GiNaC Copyright (C) 1999-2003 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 @@ -20,72 +20,42 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include +#include +#include #include #include #include #include "matrix.h" -#include "archive.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 "archive.h" #include "utils.h" -#include "debugmsg.h" -#ifndef NO_NAMESPACE_GINAC namespace GiNaC { -#endif // ndef NO_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(&basic::do_print_tree). + print_func(&matrix::do_print_python_repr)) ////////// -// default constructor, destructor, copy constructor, assignment operator -// and helpers: +// default constructor ////////// -// 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) +matrix::matrix() : inherited(TINFO_matrix), row(1), col(1), m(1, _ex0) { - 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); + setflag(status_flags::not_shareable); } ////////// @@ -99,230 +69,435 @@ void matrix::destroy(bool call_parent) * @param r number of rows * @param c number of cols */ matrix::matrix(unsigned r, unsigned c) - : inherited(TINFO_matrix), row(r), col(c) + : inherited(TINFO_matrix), row(r), col(c), m(r*c, _ex0) { - debugmsg("matrix constructor from unsigned,unsigned",LOGLEVEL_CONSTRUCT); - 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) + : inherited(TINFO_matrix), 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), m(r*c, _ex0) { - debugmsg("matrix constructor from unsigned,unsigned,exvector",LOGLEVEL_CONSTRUCT); + setflag(status_flags::not_shareable); + + size_t i = 0; + for (lst::const_iterator it = l.begin(); it != l.end(); ++it, ++i) { + size_t x = i % c; + size_t y = i / c; + if (y >= r) + break; // matrix smaller than list: throw away excessive elements + m[y*c+x] = *it; + } } ////////// // archiving ////////// -/** Construct object from archive_node. */ -matrix::matrix(const archive_node &n, const lst &sym_lst) : inherited(n, sym_lst) +matrix::matrix(const archive_node &n, 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; - } + setflag(status_flags::not_shareable); + + 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++; - } + 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; + } } +DEFAULT_UNARCHIVE(matrix) + ////////// -// functions overriding virtual functions from bases classes +// functions overriding virtual functions from base classes ////////// // public -basic * matrix::duplicate() 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 { - debugmsg("matrix duplicate",LOGLEVEL_DUPLICATE); - return new matrix(*this); + for (unsigned ro=0; ro(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 { - return m[i]; + GINAC_ASSERT(isetflag(status_flags::dynallocated | + status_flags::evaluated); } -/** Search ocurrences. A matrix 'has' an expression if it is the expression - * itself or one of the elements 'has' it. */ -bool matrix::has(const ex & other) const +ex matrix::subs(const exmap & mp, unsigned options) const { - GINAC_ASSERT(other.bp!=0); - - // tautology: it is the expression itself - if (is_equal(*other.bp)) return true; - - // search all the elements - for (exvector::const_iterator r=m.begin(); r!=m.end(); ++r) { - if ((*r).has(other)) return true; - } - return false; + exvector m2(row * col); + for (unsigned r=0; rsetflag(status_flags::dynallocated | - status_flags::evaluated ); + GINAC_ASSERT(is_exactly_a(other)); + const matrix &o = static_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; } -/** evaluate matrix numerically entry by entry. */ -ex matrix::evalf(int level) const +bool matrix::match_same_type(const basic & other) 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(other)); + const matrix & o = static_cast(other); + + // The number of rows and columns must be the same. This is necessary to + // prevent a 2x3 matrix from matching a 3x2 one. + return row == o.rows() && col == o.cols(); } -// protected +/** Automatic symbolic evaluation of an indexed matrix. */ +ex matrix::eval_indexed(const basic & i) const +{ + GINAC_ASSERT(is_a(i)); + GINAC_ASSERT(is_a(i.op(0))); + + bool all_indices_unsigned = static_cast(i).all_index_values_are(info_flags::nonnegint); + + // Check indices + if (i.nops() == 2) { + + // One index, must be one-dimensional vector + if (row != 1 && col != 1) + throw (std::runtime_error("matrix::eval_indexed(): vector must have exactly 1 index")); + + const idx & i1 = ex_to(i.op(1)); + + if (col == 1) { + + // Column vector + if (!i1.get_dim().is_equal(row)) + throw (std::runtime_error("matrix::eval_indexed(): dimension of index must match number of vector elements")); + + // Index numeric -> return vector element + if (all_indices_unsigned) { + unsigned n1 = ex_to(i1.get_value()).to_int(); + if (n1 >= row) + throw (std::runtime_error("matrix::eval_indexed(): value of index exceeds number of vector elements")); + return (*this)(n1, 0); + } + + } else { + + // Row vector + if (!i1.get_dim().is_equal(col)) + throw (std::runtime_error("matrix::eval_indexed(): dimension of index must match number of vector elements")); + + // Index numeric -> return vector element + if (all_indices_unsigned) { + unsigned n1 = ex_to(i1.get_value()).to_int(); + if (n1 >= col) + throw (std::runtime_error("matrix::eval_indexed(): value of index exceeds number of vector elements")); + return (*this)(0, n1); + } + } + + } else if (i.nops() == 3) { + + // Two indices + const idx & i1 = ex_to(i.op(1)); + const idx & i2 = ex_to(i.op(2)); + + if (!i1.get_dim().is_equal(row)) + throw (std::runtime_error("matrix::eval_indexed(): dimension of first index must match number of rows")); + if (!i2.get_dim().is_equal(col)) + throw (std::runtime_error("matrix::eval_indexed(): dimension of second index must match number of columns")); + + // Pair of dummy indices -> compute trace + if (is_dummy_pair(i1, i2)) + return trace(); + + // Both indices numeric -> return matrix element + if (all_indices_unsigned) { + unsigned n1 = ex_to(i1.get_value()).to_int(), n2 = ex_to(i2.get_value()).to_int(); + if (n1 >= row) + throw (std::runtime_error("matrix::eval_indexed(): value of first index exceeds number of rows")); + if (n2 >= col) + throw (std::runtime_error("matrix::eval_indexed(): value of second index exceeds number of columns")); + return (*this)(n1, n2); + } + + } else + throw (std::runtime_error("matrix::eval_indexed(): matrix must have exactly 2 indices")); + + return i.hold(); +} -int matrix::compare_same_type(const basic & other) const +/** Sum of two indexed matrices. */ +ex matrix::add_indexed(const ex & self, const ex & other) const +{ + GINAC_ASSERT(is_a(self)); + GINAC_ASSERT(is_a(self.op(0))); + GINAC_ASSERT(is_a(other)); + GINAC_ASSERT(self.nops() == 2 || self.nops() == 3); + + // Only add two matrices + if (is_a(other.op(0))) { + GINAC_ASSERT(other.nops() == 2 || other.nops() == 3); + + const matrix &self_matrix = ex_to(self.op(0)); + const matrix &other_matrix = ex_to(other.op(0)); + + if (self.nops() == 2 && other.nops() == 2) { // vector + vector + + if (self_matrix.row == other_matrix.row) + return indexed(self_matrix.add(other_matrix), self.op(1)); + else if (self_matrix.row == other_matrix.col) + return indexed(self_matrix.add(other_matrix.transpose()), self.op(1)); + + } else if (self.nops() == 3 && other.nops() == 3) { // matrix + matrix + + if (self.op(1).is_equal(other.op(1)) && self.op(2).is_equal(other.op(2))) + return indexed(self_matrix.add(other_matrix), self.op(1), self.op(2)); + else if (self.op(1).is_equal(other.op(2)) && self.op(2).is_equal(other.op(1))) + return indexed(self_matrix.add(other_matrix.transpose()), self.op(1), self.op(2)); + + } + } + + // Don't know what to do, return unevaluated sum + return self + other; +} + +/** Product of an indexed matrix with a number. */ +ex matrix::scalar_mul_indexed(const ex & self, const numeric & other) const +{ + GINAC_ASSERT(is_a(self)); + GINAC_ASSERT(is_a(self.op(0))); + GINAC_ASSERT(self.nops() == 2 || self.nops() == 3); + + const matrix &self_matrix = ex_to(self.op(0)); + + if (self.nops() == 2) + return indexed(self_matrix.mul(other), self.op(1)); + else // self.nops() == 3 + return indexed(self_matrix.mul(other), self.op(1), self.op(2)); +} + +/** Contraction of an indexed matrix with something else. */ +bool matrix::contract_with(exvector::iterator self, exvector::iterator other, exvector & v) const { - GINAC_ASSERT(is_exactly_of_type(other, matrix)); - const matrix & o = static_cast(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; + GINAC_ASSERT(is_a(*self)); + GINAC_ASSERT(is_a(*other)); + GINAC_ASSERT(self->nops() == 2 || self->nops() == 3); + GINAC_ASSERT(is_a(self->op(0))); + + // Only contract with other matrices + if (!is_a(other->op(0))) + return false; + + GINAC_ASSERT(other->nops() == 2 || other->nops() == 3); + + const matrix &self_matrix = ex_to(self->op(0)); + const matrix &other_matrix = ex_to(other->op(0)); + + if (self->nops() == 2) { + + if (other->nops() == 2) { // vector * vector (scalar product) + + if (self_matrix.col == 1) { + if (other_matrix.col == 1) { + // Column vector * column vector, transpose first vector + *self = self_matrix.transpose().mul(other_matrix)(0, 0); + } else { + // Column vector * row vector, swap factors + *self = other_matrix.mul(self_matrix)(0, 0); + } + } else { + if (other_matrix.col == 1) { + // Row vector * column vector, perfect + *self = self_matrix.mul(other_matrix)(0, 0); + } else { + // Row vector * row vector, transpose second vector + *self = self_matrix.mul(other_matrix.transpose())(0, 0); + } + } + *other = _ex1; + return true; + + } else { // vector * matrix + + // B_i * A_ij = (B*A)_j (B is row vector) + if (is_dummy_pair(self->op(1), other->op(1))) { + if (self_matrix.row == 1) + *self = indexed(self_matrix.mul(other_matrix), other->op(2)); + else + *self = indexed(self_matrix.transpose().mul(other_matrix), other->op(2)); + *other = _ex1; + return true; + } + + // B_j * A_ij = (A*B)_i (B is column vector) + if (is_dummy_pair(self->op(1), other->op(2))) { + if (self_matrix.col == 1) + *self = indexed(other_matrix.mul(self_matrix), other->op(1)); + else + *self = indexed(other_matrix.mul(self_matrix.transpose()), other->op(1)); + *other = _ex1; + return true; + } + } + + } else if (other->nops() == 3) { // matrix * matrix + + // A_ij * B_jk = (A*B)_ik + if (is_dummy_pair(self->op(2), other->op(1))) { + *self = indexed(self_matrix.mul(other_matrix), self->op(1), other->op(2)); + *other = _ex1; + return true; + } + + // A_ij * B_kj = (A*Btrans)_ik + if (is_dummy_pair(self->op(2), other->op(2))) { + *self = indexed(self_matrix.mul(other_matrix.transpose()), self->op(1), other->op(1)); + *other = _ex1; + return true; + } + + // A_ji * B_jk = (Atrans*B)_ik + if (is_dummy_pair(self->op(1), other->op(1))) { + *self = indexed(self_matrix.transpose().mul(other_matrix), self->op(2), other->op(2)); + *other = _ex1; + return true; + } + + // A_ji * B_kj = (B*A)_ki + if (is_dummy_pair(self->op(1), other->op(2))) { + *self = indexed(other_matrix.mul(self_matrix), other->op(1), self->op(2)); + *other = _ex1; + return true; + } + } + + return false; } + ////////// // non-virtual functions in this class ////////// @@ -334,18 +509,16 @@ int matrix::compare_same_type(const basic & other) const * @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); + if (col != other.col || row != other.row) + throw std::logic_error("matrix::add(): incompatible matrices"); + + exvector sum(this->m); + exvector::iterator i = sum.begin(), end = sum.end(); + exvector::const_iterator ci = other.m.begin(); + while (i != end) + *i++ += *ci++; + + return matrix(row,col,sum); } @@ -354,18 +527,16 @@ matrix matrix::add(const matrix & other) const * @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); + if (col != other.col || row != other.row) + throw std::logic_error("matrix::sub(): incompatible matrices"); + + exvector dif(this->m); + exvector::iterator i = dif.begin(), end = dif.end(); + exvector::const_iterator ci = other.m.begin(); + while (i != end) + *i++ -= *ci++; + + return matrix(row,col,dif); } @@ -374,66 +545,140 @@ matrix matrix::sub(const matrix & other) const * @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; icols() != other.rows()) + throw std::logic_error("matrix::mul(): incompatible matrices"); + + exvector prod(this->rows()*other.cols()); + + for (unsigned r1=0; r1rows(); ++r1) { + for (unsigned c=0; ccols(); ++c) { + 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)) { + numeric b = ex_to(expn); + matrix A(row,col); + if (expn.info(info_flags::negative)) { + b *= -1; + A = this->inverse(); + } else { + A = *this; + } + matrix C(row,col); + for (unsigned r=0; r=row || co<0 || co>=col) - throw (std::range_error("matrix::operator(): index out of range")); - - return m[ro*col+co]; + if (ro>=row || co>=col) + throw (std::range_error("matrix::operator(): index out of range")); + + return m[ro*col+co]; } -/** Set individual elements manually. +/** operator() to access elements for writing. * + * @param ro row of element + * @param co column of element * @exception range_error (index out of range) */ -matrix & matrix::set(unsigned ro, unsigned co, ex value) +ex & matrix::operator() (unsigned ro, unsigned co) { - 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; + if (ro>=row || co>=col) + throw (std::range_error("matrix::operator(): index out of range")); + + ensure_if_modifiable(); + return m[ro*col+co]; } /** Transposed of an m x n matrix, producing a new n x m matrix object that * represents the transposed. */ -matrix matrix::transpose(void) const +matrix matrix::transpose() const { - exvector trans(col*row); - - for (unsigned r=0; rcols()*this->rows()); + + for (unsigned r=0; rcols(); ++r) + for (unsigned c=0; crows(); ++c) + trans[r*this->rows()+c] = m[c*this->cols()+r]; + + return matrix(this->cols(),this->rows(),trans); } - /** Determinant of square matrix. This routine doesn't actually calculate the * determinant, it only implements some heuristics about which algorithm to - * call. If all the elements of the matrix are elements of an integral domain + * run. If all the elements of the matrix are elements of an integral domain * the determinant is also in that integral domain and the result is expanded * only. If one or more elements are from a quotient field the determinant is * usually also in that quotient field and the result is normalized before it @@ -441,77 +686,133 @@ matrix matrix::transpose(void) const * [[a/(a-b),1],[b/(a-b),1]] is returned as unity. (In this respect, it * behaves like MapleV and unlike Mathematica.) * + * @param algo allows to chose an algorithm * @return the determinant as a new expression - * @exception logic_error (matrix not square) */ -ex matrix::determinant(void) const + * @exception logic_error (matrix not square) + * @see determinant_algo */ +ex matrix::determinant(unsigned algo) const { - if (row!=col) - throw (std::logic_error("matrix::determinant(): matrix not square")); - GINAC_ASSERT(row*col==m.capacity()); - if (this->row==1) // continuation would be pointless - return m[0]; - - bool numeric_flag = true; - bool normal_flag = false; - unsigned sparse_count = 0; // count non-zero elements - for (exvector::const_iterator r=m.begin(); r!=m.end(); ++r) { - if (!(*r).is_zero()) { - ++sparse_count; - } - if (!(*r).info(info_flags::numeric)) { - numeric_flag = false; - } - if ((*r).info(info_flags::rational_function) && - !(*r).info(info_flags::crational_polynomial)) { - normal_flag = true; - } - } - - if (numeric_flag) - return determinant_numeric(); - - if (5*sparse_count uintpair; // # of zeros, column - vector c_zeros; // number of zeros in column - for (unsigned c=0; c pre_sort; // unfortunately vector can't be used - // for permutation_sign. - for (vector::iterator i=c_zeros.begin(); i!=c_zeros.end(); ++i) - pre_sort.push_back(i->second); - int sign = permutation_sign(pre_sort); - exvector result(row*col); // represents sorted matrix - unsigned c = 0; - for (vector::iterator i=pre_sort.begin(); - i!=pre_sort.end(); - ++i,++c) { - for (unsigned r=0; rto_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; + ++r; + } + + // Here is the heuristics in case this routine has to decide: + if (algo == determinant_algo::automatic) { + // Minor expansion is generally a good guess: + algo = determinant_algo::laplace; + // Does anybody know when a matrix is really sparse? + // Maybe <~row/2.236 nonzero elements average in a row? + if (row>3 && 5*sparse_count<=row*col) + algo = determinant_algo::bareiss; + // Purely numeric matrix can be handled by Gauss elimination. + // This overrides any prior decisions. + if (numeric_flag) + algo = determinant_algo::gauss; + } + + // Trap the trivial case here, since some algorithms don't like it + if (this->row==1) { + // for consistency with non-trivial determinants... + if (normal_flag) + return m[0].normal(); + else + return m[0].expand(); + } + + // Compute the determinant + switch(algo) { + case determinant_algo::gauss: { + ex det = 1; + matrix tmp(*this); + int sign = tmp.gauss_elimination(true); + for (unsigned d=0; d uintpair; + std::vector c_zeros; // number of zeros in column + for (unsigned c=0; c pre_sort; + for (std::vector::const_iterator i=c_zeros.begin(); i!=c_zeros.end(); ++i) + pre_sort.push_back(i->second); + std::vector pre_sort_test(pre_sort); // permutation_sign() modifies the vector so we make a copy here + int sign = permutation_sign(pre_sort_test.begin(), pre_sort_test.end()); + exvector result(row*col); // represents sorted matrix + unsigned c = 0; + for (std::vector::const_iterator i=pre_sort.begin(); + i!=pre_sort.end(); + ++i,++c) { + for (unsigned r=0; rinfo(info_flags::numeric)) + numeric_flag = false; + ++r; + } + + // The pure numeric case is traditionally rather common. Hence, it is + // 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); + for (unsigned i=1; imul(B); + 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; rsolve(vars,identity); + } catch (const std::runtime_error & e) { + if (e.what()==std::string("matrix::solve(): inconsistent linear system")) + throw (std::runtime_error("matrix::inverse(): singular matrix")); + else + throw; + } + return sol; } -/** Solve a set of equations for an m x n matrix by fraction-free Gaussian - * elimination. Based on algorithm 9.1 from 'Algorithms for Computer Algebra' - * by Keith O. Geddes et al. - * - * @param vars n x p matrix - * @param rhs m x p matrix - * @exception logic_error (incompatible matrices) - * @exception runtime_error (singular matrix) */ -matrix matrix::fraction_free_elim(const matrix & vars, - const matrix & rhs) const -{ - // FIXME: implement a Sasaki-Murao scheme which avoids division at all! - if ((row != rhs.row) || (col != vars.row) || (rhs.col != vars.col)) - throw (std::logic_error("matrix::fraction_free_elim(): incompatible matrices")); - - matrix a(*this); // make a copy of the matrix - matrix b(rhs); // make a copy of the rhs vector - - // given an m x n matrix a, reduce it to upper echelon form - unsigned m = a.row; - unsigned n = a.col; - int sign = 1; - ex divisor = 1; - unsigned r = 1; - - // eliminate below row r, with pivot in column k - for (unsigned k=1; (k<=n)&&(r<=m); ++k) { - // find a nonzero pivot - unsigned p; - for (p=r; (p<=m)&&(a.ffe_get(p,k).is_equal(_ex0())); ++p) {} - // pivot is in row p - if (p<=m) { - if (p!=r) { - // switch rows p and r - for (unsigned j=k; j<=n; ++j) - a.ffe_swap(p,j,r,j); - b.ffe_swap(p,1,r,1); - // keep track of sign changes due to row exchange - sign = -sign; - } - for (unsigned i=r+1; i<=m; ++i) { - for (unsigned j=k+1; j<=n; ++j) { - a.ffe_set(i,j,(a.ffe_get(r,k)*a.ffe_get(i,j) - -a.ffe_get(r,j)*a.ffe_get(i,k))/divisor); - a.ffe_set(i,j,a.ffe_get(i,j).normal() /*.normal() */ ); - } - b.ffe_set(i,1,(a.ffe_get(r,k)*b.ffe_get(i,1) - -b.ffe_get(r,1)*a.ffe_get(i,k))/divisor); - b.ffe_set(i,1,b.ffe_get(i,1).normal() /*.normal() */ ); - a.ffe_set(i,k,0); - } - divisor = a.ffe_get(r,k); - r++; - } - } - // optionally compute the determinant for square or augmented matrices - // if (r==m+1) { det = sign*divisor; } else { det = 0; } - - /* - for (unsigned r=1; r<=m; ++r) { - for (unsigned c=1; c<=n; ++c) { - cout << a.ffe_get(r,c) << "\t"; - } - cout << " | " << b.ffe_get(r,1) << endl; - } - */ - -#ifdef DO_GINAC_ASSERT - // test if we really have an upper echelon matrix - int zero_in_last_row = -1; - for (unsigned r=1; r<=m; ++r) { - int zero_in_this_row=0; - for (unsigned c=1; c<=n; ++c) { - if (a.ffe_get(r,c).is_equal(_ex0())) - zero_in_this_row++; - else - break; - } - GINAC_ASSERT((zero_in_this_row>zero_in_last_row)||(zero_in_this_row=n)); - zero_in_last_row = zero_in_this_row; - } -#endif // def DO_GINAC_ASSERT - - /* - cout << "after" << endl; - cout << "a=" << a << endl; - cout << "b=" << b << endl; - */ - - // 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)); - -#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 a set of equations for an m x n matrix. +/** Solve a linear system consisting of a m x n matrix and a m x p right hand + * side by applying an elimination scheme to the augmented matrix. * - * @param vars n x p matrix + * @param vars n x p matrix, all elements must be symbols * @param rhs m x p matrix + * @param algo selects the solving algorithm + * @return n x p solution matrix * @exception logic_error (incompatible matrices) - * @exception runtime_error (singular matrix) */ + * @exception invalid_argument (1st argument must be matrix of symbols) + * @exception runtime_error (inconsistent linear system) + * @see solve_algo */ matrix matrix::solve(const matrix & vars, - const matrix & rhs) const + const matrix & rhs, + unsigned algo) const { - if ((row != rhs.row) || (col != vars.row) || (rhs.col != vars.col)) - throw (std::logic_error("matrix::solve(): incompatible matrices")); - - throw (std::runtime_error("FIXME: need implementation.")); -} - -/** Old and obsolete interface: */ -matrix matrix::old_solve(const matrix & v) const -{ - if ((v.row != col) || (col != v.row)) - throw (std::logic_error("matrix::solve(): incompatible matrices")); - - // build the augmented matrix of *this with v attached to the right - matrix tmp(row,col+v.col); - for (unsigned r=0; rm[r*col+c]; - for (unsigned c=0; c0; --r) { - for (unsigned i=r; irows(); + const unsigned n = this->cols(); + const unsigned p = rhs.cols(); + + // syntax checks + if ((rhs.rows() != m) || (vars.rows() != n) || (vars.col != p)) + throw (std::logic_error("matrix::solve(): incompatible matrices")); + for (unsigned ro=0; rom[r*n+c]; + for (unsigned c=0; cinfo(info_flags::numeric)) + numeric_flag = false; + ++r; + } + + // Here is the heuristics in case this routine has to decide: + if (algo == solve_algo::automatic) { + // Bareiss (fraction-free) elimination is generally a good guess: + algo = solve_algo::bareiss; + // For m<3, Bareiss elimination is equivalent to division free + // elimination but has more logistic overhead + if (m<3) + algo = solve_algo::divfree; + // This overrides any prior decisions. + if (numeric_flag) + algo = solve_algo::gauss; + } + + // Eliminate the augmented matrix: + switch(algo) { + case solve_algo::gauss: + aug.gauss_elimination(); + break; + case solve_algo::divfree: + aug.division_free_elimination(); + break; + case solve_algo::bareiss: + default: + aug.fraction_free_elimination(); + } + + // assemble the solution matrix: + matrix sol(n,p); + for (unsigned co=0; co=0; --r) { + unsigned fnz = 1; // first non-zero in row + while ((fnz<=n) && (aug.m[r*(n+p)+(fnz-1)].is_zero())) + ++fnz; + if (fnz>n) { + // row consists only of zeros, corresponding rhs must be 0, too + if (!aug.m[r*(n+p)+n+co].is_zero()) { + throw (std::runtime_error("matrix::solve(): inconsistent linear system")); + } + } else { + // assign solutions for vars between fnz+1 and + // last_assigned_sol-1: free parameters + for (unsigned c=fnz; crow==1) - return m[0]; - if (this->row==2) - return (m[0]*m[3]-m[2]*m[1]).expand(); - if (this->row==3) - return (m[0]*m[4]*m[8]-m[0]*m[5]*m[7]- - m[1]*m[3]*m[8]+m[2]*m[3]*m[7]+ - m[1]*m[5]*m[6]-m[2]*m[4]*m[6]).expand(); - - ex det; - matrix minorM(this->row-1,this->col-1); - for (unsigned r1=0; r1row; ++r1) { - // shortcut if element(r1,0) vanishes - if (m[r1*col].is_zero()) - continue; - // assemble the minor matrix - for (unsigned r=0; rrow==1) - return m[0]; - if (this->row==2) - return (m[0]*m[3]-m[2]*m[1]).expand(); - if (this->row==3) - return (m[0]*m[4]*m[8]-m[0]*m[5]*m[7]- - m[1]*m[3]*m[8]+m[2]*m[3]*m[7]+ - m[1]*m[5]*m[6]-m[2]*m[4]*m[6]).expand(); - - // This algorithm can best be understood by looking at a naive - // implementation of Laplace-expansion, like this one: - // ex det; - // matrix minorM(this->row-1,this->col-1); - // for (unsigned r1=0; r1row; ++r1) { - // // shortcut if element(r1,0) vanishes - // if (m[r1*col].is_zero()) - // continue; - // // assemble the minor matrix - // for (unsigned r=0; r Pkey; - Pkey.reserve(this->col); - // key for minor determinant (a subpartition of Pkey) - vector Mkey; - Mkey.reserve(this->col-1); - // we store our subminors in maps, keys being the rows they arise from - typedef map,class ex> Rmap; - typedef map,class ex>::value_type Rmap_value; - Rmap A; - Rmap B; - ex det; - // initialize A with last column: - for (unsigned r=0; rcol; ++r) { - Pkey.erase(Pkey.begin(),Pkey.end()); - Pkey.push_back(r); - A.insert(Rmap_value(Pkey,m[this->col*r+this->col-1])); - } - // proceed from right to left through matrix - for (int c=this->col-2; c>=0; --c) { - Pkey.erase(Pkey.begin(),Pkey.end()); // don't change capacity - Mkey.erase(Mkey.begin(),Mkey.end()); - for (unsigned i=0; icol-c; ++i) - Pkey.push_back(i); - unsigned fc = 0; // controls logic for our strange flipper counter - do { - det = _ex0(); - for (unsigned r=0; rcol-c; ++r) { - // maybe there is nothing to do? - if (m[Pkey[r]*this->col+c].is_zero()) - continue; - // create the sorted key for all possible minors - Mkey.erase(Mkey.begin(),Mkey.end()); - for (unsigned i=0; icol-c; ++i) - if (i!=r) - Mkey.push_back(Pkey[i]); - // Fetch the minors and compute the new determinant - if (r%2) - det -= m[Pkey[r]*this->col+c]*A[Mkey]; - else - det += m[Pkey[r]*this->col+c]*A[Mkey]; - } - // prevent build-up of deep nesting of expressions saves time: - det = det.expand(); - // store the new determinant at its place in B: - if (!det.is_zero()) - B.insert(Rmap_value(Pkey,det)); - // increment our strange flipper counter - for (fc=this->col-c; fc>0; --fc) { - ++Pkey[fc-1]; - if (Pkey[fc-1]col-c) - for (unsigned j=fc; jcol-c; ++j) - Pkey[j] = Pkey[j-1]+1; - } while(fc); - // next column, so change the role of A and B: - A = B; - B.clear(); - } - - return det; -} - - -/* Determinant using a simple Bareiss elimination scheme. Suited for - * sparse matrices. - * - * @return the determinant as a new expression (in expanded form) - * @see matrix::determinant() */ -ex matrix::determinant_bareiss(void) const +ex matrix::determinant_minor() const { - matrix M(*this); - int sign = M.fraction_free_elimination(); - if (sign) - return sign*M(row-1,col-1); - else - return _ex0(); + // for small matrices the algorithm does not make any sense: + const unsigned n = this->cols(); + if (n==1) + return m[0].expand(); + if (n==2) + return (m[0]*m[3]-m[2]*m[1]).expand(); + if (n==3) + return (m[0]*m[4]*m[8]-m[0]*m[5]*m[7]- + m[1]*m[3]*m[8]+m[2]*m[3]*m[7]+ + m[1]*m[5]*m[6]-m[2]*m[4]*m[6]).expand(); + + // This algorithm can best be understood by looking at a naive + // implementation of Laplace-expansion, like this one: + // ex det; + // matrix minorM(this->rows()-1,this->cols()-1); + // for (unsigned r1=0; r1rows(); ++r1) { + // // shortcut if element(r1,0) vanishes + // if (m[r1*col].is_zero()) + // continue; + // // assemble the minor matrix + // for (unsigned r=0; r Pkey; + Pkey.reserve(n); + // key for minor determinant (a subpartition of Pkey) + std::vector Mkey; + Mkey.reserve(n-1); + // we store our subminors in maps, keys being the rows they arise from + typedef std::map,class ex> Rmap; + typedef std::map,class ex>::value_type Rmap_value; + Rmap A; + Rmap B; + ex det; + // initialize A with last column: + for (unsigned r=0; r=0; --c) { + Pkey.erase(Pkey.begin(),Pkey.end()); // don't change capacity + Mkey.erase(Mkey.begin(),Mkey.end()); + for (unsigned i=0; i0; --fc) { + ++Pkey[fc-1]; + if (Pkey[fc-1]0) + for (unsigned j=fc; j sigma(col); - for (unsigned i=0; i 0) - sign = -sign; - for (unsigned r2=r1+1; r2m[r2*col+c] -= this->m[r2*col+r1]*this->m[r1*col+c]/this->m[r1*col+r1]; - for (unsigned c=0; c<=r1; ++c) - this->m[r2*col+c] = _ex0(); - } - } - - return sign; + ensure_if_modifiable(); + const unsigned m = this->rows(); + const unsigned n = this->cols(); + GINAC_ASSERT(!det || n==m); + int sign = 1; + + unsigned r0 = 0; + for (unsigned r1=0; (r1=0) { + if (indx > 0) + sign = -sign; + for (unsigned r2=r0+1; r2m[r2*n+r1].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+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) + this->m[r2*n+c] = _ex0; + } + if (det) { + // save space by deleting no longer needed elements + for (unsigned c=r0+1; cm[r0*n+c] = _ex0; + } + ++r0; + } + } + + return sign; } -/** Perform the steps of division free elimination to bring the matrix +/** Perform the steps of division free elimination to bring the m x n matrix * into an upper echelon form. * + * @param det may be set to true to save a lot of space if one is only + * interested in the diagonal elements (i.e. for calculating determinants). + * The others are set to zero in this case. * @return sign is 1 if an even number of rows was swapped, -1 if an odd * number of rows was swapped and 0 if the matrix is singular. */ -int matrix::division_free_elimination(void) +int matrix::division_free_elimination(const bool det) { - int sign = 1; - ensure_if_modifiable(); - for (unsigned r1=0; r10) - sign = -sign; - for (unsigned r2=r1+1; r2m[r2*col+c] = this->m[r1*col+r1]*this->m[r2*col+c] - this->m[r2*col+r1]*this->m[r1*col+c]; - for (unsigned c=0; c<=r1; ++c) - this->m[r2*col+c] = _ex0(); - } - } - - return sign; + ensure_if_modifiable(); + const unsigned m = this->rows(); + const unsigned n = this->cols(); + GINAC_ASSERT(!det || n==m); + int sign = 1; + + unsigned r0 = 0; + for (unsigned r1=0; (r1=0) { + if (indx>0) + 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(); + // fill up left hand side with zeros + for (unsigned c=0; c<=r1; ++c) + this->m[r2*n+c] = _ex0; + } + if (det) { + // save space by deleting no longer needed elements + for (unsigned c=r0+1; cm[r0*n+c] = _ex0; + } + ++r0; + } + } + + return sign; } /** Perform the steps of Bareiss' one-step fraction free elimination to bring - * the matrix into an upper echelon form. - * + * the matrix into an upper echelon form. Fraction free elimination means + * that divide is used straightforwardly, without computing GCDs first. This + * is possible, since we know the divisor at each step. + * + * @param det may be set to true to save a lot of space if one is only + * interested in the last element (i.e. for calculating determinants). The + * others are set to zero in this case. * @return sign is 1 if an even number of rows was swapped, -1 if an odd * number of rows was swapped and 0 if the matrix is singular. */ -int matrix::fraction_free_elimination(void) +int matrix::fraction_free_elimination(const bool det) { - int sign = 1; - ex divisor = 1; - - ensure_if_modifiable(); - for (unsigned r1=0; r10) - sign = -sign; - if (r1>0) - divisor = this->m[(r1-1)*col + (r1-1)]; - for (unsigned r2=r1+1; r2m[r2*col+c] = ((this->m[r1*col+r1]*this->m[r2*col+c] - this->m[r2*col+r1]*this->m[r1*col+c])/divisor).normal(); - for (unsigned c=0; c<=r1; ++c) - this->m[r2*col+c] = _ex0(); - } - } - - return sign; + // Method: + // (single-step fraction free elimination scheme, already known to Jordan) + // + // Usual division-free elimination sets m[0](r,c) = m(r,c) and then sets + // m[k+1](r,c) = m[k](k,k) * m[k](r,c) - m[k](r,k) * m[k](k,c). + // + // 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 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 + // "manually" work in the integral domains because of subtle cancellations + // (see below). This blows up the bookkeeping a bit and the formula has + // to be modified to expand like this (N{x} stands for numerator of x, + // D{x} for denominator of x): + // N{m[k+1](r,c)} = N{m[k](k,k)}*N{m[k](r,c)}*D{m[k](r,k)}*D{m[k](k,c)} + // -N{m[k](r,k)}*N{m[k](k,c)}*D{m[k](k,k)}*D{m[k](r,c)} + // D{m[k+1](r,c)} = D{m[k](k,k)}*D{m[k](r,c)}*D{m[k](r,k)}*D{m[k](k,c)} + // where for k>1 we now divide N{m[k+1](r,c)} by + // N{m[k-1](k-1,k-1)} + // and D{m[k+1](r,c)} by + // D{m[k-1](k-1,k-1)}. + + ensure_if_modifiable(); + const unsigned m = this->rows(); + const unsigned n = this->cols(); + GINAC_ASSERT(!det || n==m); + int sign = 1; + if (m==1) + return 1; + ex divisor_n = 1; + ex divisor_d = 1; + ex dividend_n; + ex dividend_d; + + // We populate temporary matrices to subsequently operate on. There is + // one holding numerators and another holding denominators of entries. + // This is a must since the evaluator (or even earlier mul's constructor) + // might cancel some trivial element which causes divide() to fail. The + // elements are normalized first (yes, even though this algorithm doesn't + // need GCDs) since the elements of *this might be unnormalized, which + // makes things more complicated than they need to be. + matrix tmp_n(*this); + matrix tmp_d(m,n); // for denominators, if needed + 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) { + ex nd = cit->normal().to_rational(srl).numer_denom(); + ++cit; + *tmp_n_it++ = nd.op(0); + *tmp_d_it++ = nd.op(1); + } + + unsigned r0 = 0; + for (unsigned r1=0; (r1=0) { + if (indx>0) { + 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, subs_options::no_pattern); + + return sign; } -/** Partial pivoting method. +/** Partial pivoting method for matrix elimination schemes. * Usual pivoting (symbolic==false) returns the index to the element with the * largest absolute value in column ro and swaps the current row with the one * where the element was found. With (symbolic==true) it does the same thing * with the first non-zero element. * - * @param ro is the row to be inspected + * @param ro is the row from where to begin + * @param co is the column to be inspected * @param symbolic signal if we want the first non-zero element to be pivoted * (true) or the one with the largest absolute value (false). * @return 0 if no interchange occured, -1 if all are zero (usually signaling * a degeneracy) and positive integer k means that rows ro and k were swapped. */ -int matrix::pivot(unsigned ro, bool symbolic) +int matrix::pivot(unsigned ro, unsigned co, bool symbolic) { - unsigned k = ro; - - if (symbolic) { // search first non-zero - for (unsigned r=ro; r maxn && - !tmp.is_zero()) { - maxn = tmp; - k = r; - } - } - } - if (m[k*col+ro].is_zero()) - return -1; - if (k!=ro) { // swap rows - ensure_if_modifiable(); - for (unsigned c=0; cm[k*col+co].expand().is_zero())) + ++k; + } else { + // search largest element in column co beginning at row ro + 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])); + numeric tmp = ex_to(this->m[kmax*col+co]); + if (abs(tmp) > mmax) { + mmax = tmp; + k = kmax; + } + ++kmax; + } + if (!mmax.is_zero()) + k = kmax; + } + if (k==row) + // all elements in column co below row ro vanish + return -1; + if (k==ro) + // matrix needs no pivoting + return 0; + // matrix needs pivoting, so swap rows k and ro + ensure_if_modifiable(); + for (unsigned c=0; cm[k*col+c].swap(this->m[ro*col+c]); + + return k; } -/** Convert list of lists to matrix. */ -ex lst_to_matrix(const ex &l) +ex lst_to_matrix(const lst & l) { - if (!is_ex_of_type(l, lst)) - throw(std::invalid_argument("argument to lst_to_matrix() must be a lst")); + 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); - for (i=0; i j) - m.set(i, j, l.op(i).op(j)); - else - m.set(i, j, ex(0)); - 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; } -////////// -// global constants -////////// +ex diag_matrix(const lst & l) +{ + lst::const_iterator it; + size_t dim = l.nops(); + + // Allocate and fill matrix + matrix &M = *new matrix(dim, dim); + M.setflag(status_flags::dynallocated); -const matrix some_matrix; -const type_info & typeid_matrix=typeid(some_matrix); + unsigned i; + for (it = l.begin(), i = 0; it != l.end(); ++it, ++i) + M(i, i) = *it; + + return M; +} + +ex unit_matrix(unsigned r, unsigned c) +{ + matrix &Id = *new matrix(r, c); + Id.setflag(status_flags::dynallocated); + for (unsigned i=0; i 10 || c > 10); + bool single_row = (r == 1 || c == 1); + + for (unsigned i=0; i