Solving Sparse Linear Systems in Maple

roman_pearce's picture

What is the largest linear system that Maple can solve? You might be surprised to find out. In this article we present strategies for solving sparse linear systems over the rationals. An example implementation is provided, but first we present a bit of background.

Sparse linear systems arise naturally from problems in mathematics, science, and engineering. Typically many quantities are related, but because of an underlying structure only a small subset of the elements appear in most equations. Consider networks, finite element models, structural analysis problems, and linear programming problems. In all of these cases each element is connected to only a handful of neighbours making the resulting linear system sparse. Many mathematical algorithms also produce sparse linear algebra problems. For example, Berlekamp's algorithm for factoring polynomials, the quadratic seive algorithm for integer factorization, and the index calculus algorithm for computing discrete logarithms all use sparse linear algebra mod p or p^k-1. We could modify the code below to run mod p, but that's another article.

Algorithms

This article focuses on direct methods for solving linear systems, ie: Gaussian elimination. Iterative methods are not considered, although we could implement them - see this post. The main problem encountered when using a direct method is "fill in". A poor choice of pivot will increase the number of non-zero entries in each row and subsequently the amount of work required to solve the rest of the system. Repeatly choosing bad pivots will fill in the matrix entirely, producing a dense system that can be very hard to solve. Good pivot choices preserve sparsity and keep the elimination process from getting bogged down.

Structured Gaussian Elimination

This algorithm was developed to solve very large systems coming from integer factorization and discrete logarithm computations. You can read about it in more detail here. The algorithm assumes that the non-zero elements are not evenly distributed among the columns. It first solves for variables that appear in only one equation, repeatedly, until all triangular blocks are removed from the system. Next it declares a small number of the densest columns "heavy" and uses equations with one "light" variable for elimination. This ensures that the light part of the matrix never fills in. At some point one can also drop a few of the densest rows and recurse on the rest of the system to quickly solve for most of the variables. Afterwards the dense equations are evaluated at the solutions to obtain a smaller system in the unsolved variables. Experience shows that extremely sparse systems will often collapse.

Markowitz Pivoting

Markowitz pivoting is a simple and popular heuristic for reducing fill in. It selects a pivot to minimize the product of the number of non-zero row and column elements. That is, each non-zero A[i,j] is given weight (#row[i] - 1)(#column[j] - 1) and a pivot with minimal weight is selected. We subtract one from the row and column counts to force the selection of single elements in a row or column if they exist, since their choice produces no fill in at all. A common problem with the strategy is that the overhead required to select a pivot and update the row and column weights after each elimination step is O(|A|). To make our implementation efficient we select blocks of pivots at a time. The equations containing the pivots are triangularized and a block of variables is eliminated from the system in each step of the algorithm.

Dense Modular Methods

Eventually after running these methods one obtains a small dense linear system. To solve these systems we use LinearAlgebra:-Modular:-IntegerLinearSolve command, which uses hardware arithmetic to solve the system modulo primes and Chinese remaindering and rational reconstruction to recover the solutions. One advantage to using this routine is that it is output sensitive - the amount of computation it does depends mostly on the size of the result instead of the size of the input. The tradeoff is a very small probability of error, but the benefits are huge for structured systems where the solutions are often small.

Maple Code and Examples

Download some preliminary Maple code from my homepage here. You may also want to download some example systems: small (900 x 924), medium (3211 x 3211), random (21952 x 21929), and large (99779 x 99619). I have intentionally made these systems easy so that you can see how the algorithms work on a slow computer. The code requires Maple 11. Here is an example:

> read "sge.mpl":		# load code
> read "medsys":		# assigns the system to 'eqns'
> sol := RationalSparse(eqns):	# solves system
> subs(sol, eqns);		# check solution (if small!)
> map(lprint, sort_by_length(sol)): # print solution

I'm hoping that you'll want to try hard problems though. The University of Florida Sparse Matrix Collection is the best place to start. They have a lot of matrices there, and you can sort them by type to find ones with integer or binary entries (sorry, no floats or complex numbers in this code - that's another article). Once you've found a matrix you like, download it in MatrixMarket format (abbreviated MM) and decompress the file. I have included a MatrixMarket import command for you to use, with two examples below.

# bcspwr06 with randomly generated rhs
> eqns := MatrixMarket("bcspwr06.mtx"):
> r := rand(-10..10):	# random number generator
> eqns := eqns - [seq(r(), i=1..nops(eqns))]:
> sol := RationalSparse(eqns):

# lpi_ceria3d with included rhs
> eqns := MatrixMarket("lpi_ceria3d.mtx", rhs="lpi_ceria3d_b.mtx"):
> eqns := convert(eqns, 'rational'):	# important
> sol := RationalSparse(eqns):

You can also get a sparse Maple Matrix out of the MatrixMarket command by specifying output=matrix, however you should be careful with those since many routines in Maple are not optimized for sparse matrices. For example, a large sparse matrix (50000 x 50000) will hang the matrix browser. If you find any bugs or if you think the code above performs poorly on some systems please let me know - I'm trying to tweak it. Either a private message here or an email to the address on the bottom of my homepage would be appreciated.

Comments

acer's picture

options for these sparse rational methods?

In the floating-point case, solving large sparse linear systems can be difficult. The existing solvers for such floating-point systems (hooked into LinearSolve) accept some options -- to control things like the choice of method, amount of fill-in, tolerances, etc. See ?IterativeSolver .

What sort of options do you expect that your rational solver implementation might need, if any?

ps. The documentation makes it appear that only symmetric/hermitian floating-point sparse systems can be solved with iterative methods. Yet trying it with real nonsymmetric float[8] sparse Matrices shows via userinfo that there are also distinct nonsymmetric solvers. All the sparse floating-point methods show NAG function names through userinfo, ie. with infolevel[LinearAlgebra] > 0 .

acer

roman_pearce's picture

options for sparse rational methods

I don't really want a bunch of options, but there are some magic numbers at the top of the file:
- the cutoff between SGE and Markowitz pivoting (30k rows or 12% dense)
- the cutoff between Markowitz and LAModular (95% dense)
- the size of the blocks in the Markowitz solver (n/3)
- the max number of heavy columns in SGE (4/3*sqrt(|A|))
- when to stop light block elimination in SGE (< sqrt(max_heavy) eliminated)
- number of light rows to recurse on in SGE (n-sqrt(n))

Right now I am trying to choose good defaults. I think the SGE values work well, but the Markowitz block size seems insane. It tries to solve n/3 equations in every step, and n/2 is often faster! Now if multiple equations pivot on the same variable only one equation is selected and the others are not replaced, so as the system gets smaller there are more collisions and less equations are solved. But still, this seems very counter-intuitive. It's probably just a side effect of Maple's design, which always makes a copy when things are modified.

As for the NAG solvers, it's true that most iterative methods require a symmetric (and sometimes even positive-definite) matrix for efficiency and/or convergence. Often there are modifications that can be made (like the biconjugate gradient method which slow the algorithm down but allow non-symmetric matrices to be solved. Overall I am not that familiar with iterative methods, which is why I didn't try to implement one this time around. If I have time or if there is demand for it I may try in the future. There are some systems where I think it could really help.

acer's picture

sparse LU

Thanks for those details. Do you suspect that, even if you find reasonably good defaults for those magic numbers, some user-based control via options may be necessary for some problems to be solved?

Will there be some mechanism for repeated solving using different RHS's (but *not* all done at once)? I ask because depending on the method this can require saving the factorization or preconditioning.

There is also a real floating-point sparse direct solver available from LinearAlgebra[LinearSolve] with the method=SparseLU option.

The userinfo messages indicate that this uses NAG routine f01brf to factorize and routine f04axf to do the subsequent solving for a given RHS.

There is an indication that the NAG f01brf approach is based upon the somewhat well-known MA28 FORTRAN code. I don't have much experience with either SuperLU or MA48 (the successor to MA28?).

acer

roman_pearce's picture

sparse LU

Do you suspect that, even if you find reasonably good defaults for those magic numbers, some user-based control via options may be necessary for some problems to be solved?

No. I will pick slightly conservative defaults, sacrificing some performance to guarantee that if the routines can solve the problem then they always do. The current magic numbers are actually pretty good. I'm hoping people will test a few systems and report back if there is a problem.

Will there be some mechanism for repeated solving using different RHS's (but *not* all done at once)?

No, and this was a difficult decision. The plan is to replace the code in solve, which solves linear systems with one right hand side. I have made some optimizations for that case. I wanted to focus on this one specific instance because:
1) the general problem of fixing sparse linear algebra is a megaproject
2) some people are skeptical of whether it can or should be done
3) it would be massively faster if we wrote it in C

Basically, I decided not to try to do too much. I want to show the potential for solving these types of problems and provide a useful routine to attract support. I know these computations are important, which is why I am even more concerned that they are done right if they are done at all.

Numeric sparse iterative solvers for nonsymmetric matrices

acer writes:

ps. The documentation makes it appear that only symmetric/hermitian floating-point sparse systems can be solved with iterative methods. Yet trying it with real nonsymmetric float[8] sparse Matrices shows via userinfo that there are also distinct nonsymmetric solvers. All the sparse floating-point methods show NAG function names through userinfo, ie. with infolevel[LinearAlgebra] > 0 .

Yes, there is a conjugate-gradient-squared (CGS) method for nonsymmetric floating-point sparse linear systems. Unfortunately, mention of this algorithm somehow got left out of the help pages but we'll try to remedy this in an upcoming release.

Paulina Chin
Maplesoft

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
}