[GiNaC-list] [patch] Improve automatic algorithm selection in matrix::solve()

Vitaly Magerya vmagerya at gmail.com
Thu Jun 14 15:32:16 CEST 2018


Hi, folks. Since we've recently added solve_algo::markowitz, I
thought it would be a good idea to update the automatic elimination
algorithm selection logic we have for matrix::solve() (currently
living in matrix::echelon_form()).

To this end I've constructed a benchmark that generates random-ish
matrices and measures how fast matrix::solve() can solve them.
The matrix generation works by starting with a known simple
solution and then permuting and mixing rows and columns randomly.
You can find the code for this procedure at [1].

With that code I've generated a bunch of data points and run a
quick analysis on it. Overall, the fastest (on average) algorithm
seems to be:

    For numeric matrices:
        If ncells > 200 && density < 0.5: Markowitz
        Otherwise: Gauss

    For symbolic matrices:
        If density > 0.6 && ncells <= 12: Divfree
        If density > 0.6 && ncells < 120: Bareiss
        Otherwise: Markowitz

You can find the details (and some pretty charts) at [2].

The attached patch implements this recommendation.

Note that matrix::determinant() also has a similar automatic
algorithm selection logic, but I didn't touch it for now.

[1] https://github.com/magv/ginac-matrix-solve-bench/blob/master/mx-solve-bench.cpp
[2] https://github.com/magv/ginac-matrix-solve-bench/blob/master/analysis.ipynb
-------------- next part --------------
From 7c3c5284314b4c0edc4d23ca3ee4db11aa0839c9 Mon Sep 17 00:00:00 2001
From: Vitaly Magerya <magv at tx97.net>
Date: Thu, 14 Jun 2018 14:31:28 +0200
Subject: [PATCH] Improve automatic algorithm selection in matrix::solve()

---
 ginac/matrix.cpp | 35 +++++++++++++++++++++++++----------
 1 file changed, 25 insertions(+), 10 deletions(-)

diff --git a/ginac/matrix.cpp b/ginac/matrix.cpp
index 3a01da2d..1bb33e0c 100644
--- a/ginac/matrix.cpp
+++ b/ginac/matrix.cpp
@@ -1211,21 +1211,36 @@ matrix::echelon_form(unsigned algo, int n)
 	if (algo == solve_algo::automatic) {
         // Gather some statistical information about the augmented matrix:
 		bool numeric_flag = true;
-		for (auto & r : m) {
+		unsigned ncells = col*row;
+		unsigned density = 0;
+		for (auto && r : m) {
 			if (!r.info(info_flags::numeric)) {
 				numeric_flag = false;
 				break;
 			}
+			density += !r.is_zero();
+		}
+		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;
+			}
 		}
-		// Bareiss (fraction-free) elimination is generally a good guess:
-		algo = solve_algo::bareiss;
-		// For row<3, Bareiss elimination is equivalent to division free
-		// elimination but has more logistic overhead
-		if (row<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(col);
-- 
2.13.6



More information about the GiNaC-list mailing list