* Implementation of symbolic matrices */
/*
- * GiNaC Copyright (C) 1999-2018 Johannes Gutenberg University Mainz, Germany
+ * GiNaC Copyright (C) 1999-2019 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
/** 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, all elements must be symbols
+ * @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
const unsigned n = this->cols();
const unsigned p = rhs.cols();
- // syntax checks
+ // syntax checks
if ((rhs.rows() != m) || (vars.rows() != n) || (vars.cols() != p))
throw (std::logic_error("matrix::solve(): incompatible matrices"));
for (unsigned ro=0; ro<n; ++ro)
for (unsigned c=0; c<p; ++c)
aug.m[r*(n+p)+c+n] = rhs.m[r*p+c];
}
-
- // Gather some statistical information about the augmented matrix:
- bool numeric_flag = true;
- for (auto & r : aug.m) {
- if (!r.info(info_flags::numeric)) {
- numeric_flag = false;
- break;
- }
- }
-
- // 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:
- std::vector<unsigned> colid(aug.cols());
- for (unsigned c = 0; c < aug.cols(); c++) {
- colid[c] = c;
- }
- switch(algo) {
- case solve_algo::gauss:
- aug.gauss_elimination();
- break;
- case solve_algo::divfree:
- aug.division_free_elimination();
- break;
- case solve_algo::bareiss:
- aug.fraction_free_elimination();
- break;
- case solve_algo::markowitz:
- colid = aug.markowitz_elimination(n);
- break;
- default:
- throw std::invalid_argument("matrix::solve(): 'algo' is not one of the solve_algo enum");
- }
+ auto colid = aug.echelon_form(algo, n);
// assemble the solution matrix:
matrix sol(n,p);
return sol;
}
-
/** Compute the rank of this matrix. */
unsigned matrix::rank() const
+{
+ return rank(solve_algo::automatic);
+}
+
+/** Compute the rank of this matrix using the given algorithm,
+ * which should be a member of enum solve_algo. */
+unsigned matrix::rank(unsigned solve_algo) 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();
+ to_eliminate.echelon_form(solve_algo, col);
unsigned r = row*col; // index of last non-zero element
while (r--) {
return det;
}
+std::vector<unsigned>
+matrix::echelon_form(unsigned algo, int n)
+{
+ // Here is the heuristics in case this routine has to decide:
+ if (algo == solve_algo::automatic) {
+ // Gather some statistical information about the augmented matrix:
+ bool numeric_flag = true;
+ for (const auto & r : m) {
+ if (!r.info(info_flags::numeric)) {
+ numeric_flag = false;
+ break;
+ }
+ }
+ unsigned density = 0;
+ for (const auto & r : m) {
+ density += !r.is_zero();
+ }
+ unsigned ncells = col*row;
+ if (numeric_flag) {
+ // For numerical matrices Gauss is good, but Markowitz becomes
+ // better for large sparse matrices.
+ if ((ncells > 200) && (density < ncells/2)) {
+ algo = solve_algo::markowitz;
+ } else {
+ algo = solve_algo::gauss;
+ }
+ } else {
+ // For symbolic matrices Markowitz is good, but Bareiss/Divfree
+ // is better for small and dense matrices.
+ if ((ncells < 120) && (density*5 > ncells*3)) {
+ if (ncells <= 12) {
+ algo = solve_algo::divfree;
+ } else {
+ algo = solve_algo::bareiss;
+ }
+ } else {
+ algo = solve_algo::markowitz;
+ }
+ }
+ }
+ // Eliminate the augmented matrix:
+ std::vector<unsigned> colid(col);
+ for (unsigned c = 0; c < col; c++) {
+ colid[c] = c;
+ }
+ switch(algo) {
+ case solve_algo::gauss:
+ gauss_elimination();
+ break;
+ case solve_algo::divfree:
+ division_free_elimination();
+ break;
+ case solve_algo::bareiss:
+ fraction_free_elimination();
+ break;
+ case solve_algo::markowitz:
+ colid = markowitz_elimination(n);
+ break;
+ default:
+ throw std::invalid_argument("matrix::echelon_form(): 'algo' is not one of the solve_algo enum");
+ }
+ return colid;
+}
/** Perform the steps of an ordinary Gaussian elimination to bring the m x n
* matrix into an upper echelon form. The algorithm is ok for matrices
sign = -sign;
for (unsigned r2=r0+1; r2<m; ++r2) {
for (unsigned c=c0+1; c<n; ++c)
- this->m[r2*n+c] = (this->m[r0*n+c0]*this->m[r2*n+c] - this->m[r2*n+c0]*this->m[r0*n+c]).expand();
+ this->m[r2*n+c] = (this->m[r0*n+c0]*this->m[r2*n+c] - this->m[r2*n+c0]*this->m[r0*n+c]).normal();
// fill up left hand side with zeros
for (unsigned c=r0; c<=c0; ++c)
this->m[r2*n+c] = _ex0;