X-Git-Url: https://www.ginac.de/ginac.git//ginac.git?p=ginac.git;a=blobdiff_plain;f=ginac%2Fmatrix.cpp;h=6240ce20e9e36a14557e38a9918841ecf63923c7;hp=bf490f046e95673c4b77644499508bd0843013f8;hb=db5765dc91202851739e196ba11bfccb0b3fe7bc;hpb=55d35dcf72dc411c8265628fcad2bd67d320a8c9 diff --git a/ginac/matrix.cpp b/ginac/matrix.cpp index bf490f04..6240ce20 100644 --- a/ginac/matrix.cpp +++ b/ginac/matrix.cpp @@ -108,7 +108,7 @@ matrix::matrix(unsigned r, unsigned c) m.resize(r*c, _ex0()); } - // protected +// protected /** Ctor from representation, for internal use only. */ matrix::matrix(unsigned r, unsigned c, const exvector & m2) @@ -369,17 +369,17 @@ matrix matrix::sub(const matrix & other) const * @exception logic_error (incompatible matrices) */ matrix matrix::mul(const matrix & other) const { - if (col != other.row) + if (this->cols() != other.rows()) throw (std::logic_error("matrix::mul(): incompatible matrices")); - exvector prod(row*other.col); + 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; r2cols()*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(col,row,trans); + return matrix(this->cols(),this->rows(),trans); } @@ -447,9 +447,7 @@ 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]; - + // Gather some statistical information about this matrix: bool numeric_flag = true; bool normal_flag = false; @@ -468,11 +466,11 @@ ex matrix::determinant(unsigned algo) const // Here is the heuristics in case this routine has to decide: if (algo == determinant_algo::automatic) { - // Minor expansion is generally a good starting point: + // 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 (5*sparse_count<=row*col) + 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. @@ -480,17 +478,27 @@ ex matrix::determinant(unsigned algo) const 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(); + int sign = tmp.gauss_elimination(true); for (unsigned d=0; drows(); + const unsigned n = this->cols(); + const unsigned p = rhs.cols(); - // 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 = 0; + // 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; czero_in_last_row)||(zero_in_this_row=n)); - zero_in_last_row = zero_in_this_row; + // Gather some statistical information about the augmented matrix: + bool numeric_flag = true; + for (exvector::const_iterator r=aug.m.begin(); r!=aug.m.end(); ++r) { + if (!(*r).info(info_flags::numeric)) + numeric_flag = false; } -#endif // def DO_GINAC_ASSERT - // assemble solution - matrix sol(n,1); - unsigned last_assigned_sol = n+1; - for (int r=m-1; r>=0; --r) { - unsigned first_non_zero = 1; - while ((first_non_zero<=n)&&(a(r,first_non_zero-1).is_zero())) - first_non_zero++; - if (first_non_zero>n) { - // row consists only of zeroes, corresponding rhs must be 0 as well - if (!b.m[r*b.cols()].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; cm[r*col+c]; - for (unsigned c=0; c0; --r) { - for (unsigned i=r; irow==1) - return m[0]; - if (this->row==2) + 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 (this->row==3) + 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(); @@ -875,8 +820,8 @@ ex matrix::determinant_minor(void) const // 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) { + // 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; @@ -906,10 +851,10 @@ ex matrix::determinant_minor(void) const // Unique flipper counter for partitioning into minors std::vector Pkey; - Pkey.reserve(this->col); + Pkey.reserve(n); // key for minor determinant (a subpartition of Pkey) std::vector Mkey; - Mkey.reserve(this->col-1); + 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; @@ -917,34 +862,34 @@ ex matrix::determinant_minor(void) const Rmap B; ex det; // initialize A with last column: - for (unsigned r=0; rcol; ++r) { + for (unsigned r=0; rcol*r+this->col-1])); + A.insert(Rmap_value(Pkey,m[n*(r+1)-1])); } // proceed from right to left through matrix - for (int c=this->col-2; c>=0; --c) { + for (int c=n-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) + for (unsigned i=0; icol-c; ++r) { + for (unsigned r=0; rcol+c].is_zero()) + if (m[Pkey[r]*n+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) + for (unsigned i=0; icol+c]*A[Mkey]; + det -= m[Pkey[r]*n+c]*A[Mkey]; else - det += m[Pkey[r]*this->col+c]*A[Mkey]; + det += m[Pkey[r]*n+c]*A[Mkey]; } // prevent build-up of deep nesting of expressions saves time: det = det.expand(); @@ -952,13 +897,13 @@ ex matrix::determinant_minor(void) const if (!det.is_zero()) B.insert(Rmap_value(Pkey,det)); // increment our strange flipper counter - for (fc=this->col-c; fc>0; --fc) { + for (fc=n-c; fc>0; --fc) { ++Pkey[fc-1]; if (Pkey[fc-1]col-c) - for (unsigned j=fc; jcol-c; ++j) + if (fcrows(); + const unsigned n = this->cols(); + GINAC_ASSERT(!det || n==m); int sign = 1; - ex piv; - for (unsigned r1=0; r1 0) - sign = -sign; - for (unsigned r2=r1+1; r2m[r2*col+r1] / this->m[r1*col+r1]; - for (unsigned c=r1+1; cm[r2*col+c] -= piv * this->m[r1*col+c]; - for (unsigned c=0; c<=r1; ++c) - this->m[r2*col+c] = _ex0(); + + unsigned r0 = 0; + for (unsigned r1=0; (r1=0) { + if (indx > 0) + sign = -sign; + for (unsigned r2=r0+1; r2m[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; } } @@ -999,26 +967,46 @@ int matrix::gauss_elimination(void) } -/** 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(); + 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; } } @@ -1032,11 +1020,11 @@ int matrix::division_free_elimination(void) * 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 + * 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(bool det) +int matrix::fraction_free_elimination(const bool det) { // Method: // (single-step fraction free elimination scheme, already known to Jordan) @@ -1062,12 +1050,13 @@ int matrix::fraction_free_elimination(bool det) // and D{m[k+1](r,c)} by // D{m[k-1](k-1,k-1)}. - GINAC_ASSERT(!det || row==col); ensure_if_modifiable(); - if (rows()==1) - return 1; - + 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; @@ -1081,62 +1070,70 @@ int matrix::fraction_free_elimination(bool det) // 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(row,col); // for denominators, if needed + matrix tmp_d(m,n); // for denominators, if needed lst srl; // symbol replacement list - exvector::iterator it = m.begin(); + exvector::iterator it = this->m.begin(); exvector::iterator tmp_n_it = tmp_n.m.begin(); exvector::iterator tmp_d_it = tmp_d.m.begin(); - for (; it!= m.end(); ++it, ++tmp_n_it, ++tmp_d_it) { + for (; it!= this->m.end(); ++it, ++tmp_n_it, ++tmp_d_it) { (*tmp_n_it) = (*it).normal().to_rational(srl); (*tmp_d_it) = (*tmp_n_it).denom(); (*tmp_n_it) = (*tmp_n_it).numer(); } - for (unsigned r1=0; r10) { - sign = -sign; - // rows r1 and indx were swapped, so pivot matrix tmp_d: - for (unsigned c=0; c0) { - divisor_n = tmp_n.m[(r1-1)*col+(r1-1)].expand(); - divisor_d = tmp_d.m[(r1-1)*col+(r1-1)].expand(); - // save space by deleting no longer needed elements: - if (det) { - for (unsigned c=0; c=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(); tmp_n_it = tmp_n.m.begin(); tmp_d_it = tmp_d.m.begin(); - for (; it!= m.end(); ++it, ++tmp_n_it, ++tmp_d_it) + for (; it!= this->m.end(); ++it, ++tmp_n_it, ++tmp_d_it) (*it) = ((*tmp_n_it)/(*tmp_d_it)).subs(srl); return sign; @@ -1149,68 +1146,72 @@ int matrix::fraction_free_elimination(bool det) * 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 (symbolic) { + // search first non-zero element in column co beginning at row ro + while ((km[k*col+co].expand().is_zero())) + ++k; + } else { + // search largest element in column co beginning at row ro + GINAC_ASSERT(is_ex_of_type(this->m[k*col+co],numeric)); + unsigned kmax = k+1; + numeric mmax = abs(ex_to_numeric(m[kmax*col+co])); + while (kmaxm[kmax*col+co],numeric)); + numeric tmp = ex_to_numeric(this->m[kmax*col+co]); + if (abs(tmp) > mmax) { + mmax = tmp; + k = kmax; } + ++kmax; } + if (!mmax.is_zero()) + k = kmax; } - if (m[k*col+ro].is_zero()) + if (k==row) + // all elements in column co below row ro vanish return -1; - if (k!=ro) { // swap rows - ensure_if_modifiable(); - for (unsigned c=0; c cols) - cols = l.op(i).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; + if (!is_ex_of_type(l, lst)) + throw(std::invalid_argument("argument to lst_to_matrix() must be a lst")); + + // Find number of rows and columns + unsigned rows = l.nops(), cols = 0, i, j; + for (i=0; i cols) + cols = l.op(i).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; } //////////