Sparse linear algebra
Introduction
This chapter deals with sparse linear algebra over commutative rings and fields.
Sparse linear algebra, that is, linear algebra with sparse matrices, plays an important role in various algorithms in algebraic number theory. For example, it is one of the key ingredients in the computation of class groups and discrete logarithms using index calculus methods.
Sparse rows
Building blocks for sparse matrices are sparse rows, which are modelled by objects of type SRow. More precisely, the type is of parametrized form SRow{T}, where T is the element type of the base ring $R$. For example, SRow{ZZRingElem} is the type for sparse rows over the integers.
It is important to note that sparse rows do not have a fixed number of columns, that is, they represent elements of $\{ (x_i)_i \in R^{\mathbb{N}} \mid x_i = 0 \text{ for almost all }i\}$. In particular any two sparse rows over the same base ring can be added.
Creation
sparse_row — Method
sparse_row(R::Ring, J::Vector{Tuple{Int, T}}) -> SRow{T}Constructs the sparse row $(a_i)_i$ with $a_{i_j} = x_j$, where $J = (i_j, x_j)_j$. The elements $x_i$ must belong to the ring $R$.
sourcesparse_row — Method
sparse_row(R::Ring, J::Vector{Tuple{Int, Int}}) -> SRowConstructs the sparse row $(a_i)_i$ over $R$ with $a_{i_j} = x_j$, where $J = (i_j, x_j)_j$.
sourcesparse_row — Method
sparse_row(R::NCRing, J::Vector{Int}, V::Vector{T}) -> SRow{T}Constructs the sparse row $(a_i)_i$ over $R$ with $a_{i_j} = x_j$, where $J = (i_j)_j$ and $V = (x_j)_j$.
sourceBasic operations
Rows support the usual operations:
+,-,==- multiplication by scalars
div,divexact
add_scaled_row — Method
add_scaled_row — Method
transform_row — Method
transform_row(A::SRow{T}, B::SRow{T}, i::Int, j::Int, a::T, b::T, c::T, d::T)Returns the tuple $(aA + bB, cA + dB)$.
sourceChange of base ring
change_base_ring — Method
change_base_ring(R::Ring, A::SRow) -> SRowCreate a new sparse row by coercing all elements into the ring $R$.
sourceMaximum, minimum and 2-norm
Functionality for integral sparse rows
Conversion to/from julia and AbstractAlgebra types
sparse_row — Method
Sparse matrices
Let $R$ be a commutative ring. Sparse matrices with base ring $R$ are modelled by objects of type SMat. More precisely, the type is of parametrized form SRow{T}, where T is the element type of the base ring. For example, SMat{ZZRingElem} is the type for sparse matrices over the integers.
In contrast to sparse rows, sparse matrices have a fixed number of rows and columns, that is, they represent elements of the matrices space $\mathrm{Mat}_{n\times m}(R)$. Internally, sparse matrices are implemented as an array of sparse rows. As a consequence, unlike their dense counterparts, sparse matrices have a mutable number of rows and it is very performant to add additional rows.
Construction
sparse_matrix — Method
sparse_matrix — Method
sparse_matrix(R::Ring, n::Int, m::Int) -> SMatReturn a sparse $n$ times $m$ zero matrix over $R$.
sourceSparse matrices can also be created from dense matrices as well as from julia arrays:
sparse_matrix — Method
sparse_matrix(A::MatElem; keepzrows::Bool = true)Constructs the sparse matrix corresponding to the dense matrix $A$. If keepzrows is false, then the constructor will drop any zero row of $A$.
sparse_matrix — Method
sparse_matrix(R::Ring, A::Matrix{T}) -> SMatConstructs the sparse matrix over $R$ corresponding to $A$.
sourcesparse_matrix — Method
sparse_matrix(R::Ring, A::Matrix{T}) -> SMatConstructs the sparse matrix over $R$ corresponding to $A$.
sourceThe normal way however, is to add rows:
Sparse matrices can also be concatenated to form larger ones:
(Normal julia $cat$ is also supported)
There are special constructors:
identity_matrix — Method
identity_matrix(::Type{SMat}, R::Ring, n::Int)Return a sparse $n$ times $n$ identity matrix over $R$.
sourcezero_matrix — Method
zero_matrix — Method
zero_matrix(::Type{SMat}, R::Ring, n::Int, m::Int)Return a sparse $n$ times $m$ zero matrix over $R$.
sourceblock_diagonal_matrix — Method
block_diagonal_matrix(xs::Vector{SMat})Return the block diagonal matrix with the matrices in xs on the diagonal. Requires all blocks to have the same base ring.
Slices:
Transpose:
Elementary Properties
number_of_rows — Method
number_of_columns — Method
is_upper_triangular — Method
elementary_divisors — Method
elementary_divisors(A::SMat{ZZRingElem}) -> Vector{ZZRingElem}The elementary divisors of $A$, i.e. the diagonal elements of the Smith normal form of $A$.
sourcesolve_dixon_sf — Method
solve_dixon_sf(A::SMat{ZZRingElem}, b::SRow{ZZRingElem}, is_int::Bool = false) -> SRow{ZZRingElem}, ZZRingElem
solve_dixon_sf(A::SMat{ZZRingElem}, B::SMat{ZZRingElem}, is_int::Bool = false) -> SMat{ZZRingElem}, ZZRingElemFor a sparse square matrix $A$ of full rank and a sparse matrix (row), find a sparse matrix (row) $x$ and an integer $d$ s.th. $x A = bd$ holds. The algorithm is a Dixon-based linear p-adic lifting method. If \code{is_int} is given, then $d$ is assumed to be $1$. In this case rational reconstruction is avoided.
sourcehadamard_bound2 — Method
echelon_with_transform — Method
echelon_with_transform(A::SMat{zzModRingElem}) -> SMat, SMatFind a unimodular matrix $T$ and an upper-triangular $E$ s.th. $TA = E$ holds.
sourcereduce_full — Method
reduce_full(A::SMat{ZZRingElem}, g::SRow{ZZRingElem},
with_transform = Val(false)) -> SRow{ZZRingElem}, Vector{Int}Reduces $g$ modulo $A$ and assumes that $A$ is upper triangular.
The second return value is the array of pivot elements of $A$ that changed.
If with_transform is set to Val(true), then additionally an array of transformations is returned.
hnf — Method
hnf(A::SMat{ZZRingElem}) -> SMat{ZZRingElem}Return the upper right Hermite normal form of $A$.
\[truncate\]
indicates if zero rows should be returned or disgarded, when $full_hnf$ is set to $false$ only an upper triangular matrix is computed, ie. the upwards reduction is not performed.
\[auto\]
if set to true selects a different elemination strategy - here the upwards reduction is temporarily switched off.
\[limit\]
marks the last column where size reduction happens, so calling $hnf$ on $hcat(A, identity_matrix(SMat, ZZ, nrows(A)))$ with $limit$ set to $ncols(A)$ should produce a non-reduced transformation matrix in the right. If $limit$ is not set, the transformation matrix will also be partly triangular.
sourcehnf_extend! — Method
hnf_extend!(A::SMat{ZZRingElem}, b::SMat{ZZRingElem}, offset::Int = 0) -> SMat{ZZRingElem}Given a matrix $A$ in HNF, extend this to get the HNF of the concatenation with $b$.
sourceis_diagonal — Method
valence_mc — Method
valence_mc{T}(A::SMat{T}; extra_prime = 2, trans = Vector{SMatSLP_add_row{T}}()) -> TUses a Monte-Carlo algorithm to compute the valence of $A$. The valence is the valence of the minimal polynomial $f$ of $transpose(A)*A$, thus the last non-zero coefficient, typically $f(0)$.
The valence is computed modulo various primes until the computation stabilises for extra_prime many.
trans, if given, is a SLP (straight-line-program) in GL(n, Z). Then the valence of trans * $A$ is computed instead.
saturate — Method
saturate(A::SMat{ZZRingElem}) -> SMat{ZZRingElem}Computes the saturation of $A$, that is, a basis for $\mathbf{Q}\otimes M \cap \mathbf{Z}^n$, where $M$ is the row span of $A$ and $n$ the number of rows of $A$.
Equivalently, return $TA$ for an invertible rational matrix $T$, such that $TA$ is integral and the elementary divisors of $TA$ are all trivial.
sourcehnf_kannan_bachem — Method
hnf_kannan_bachem(A::SMat{ZZRingElem}) -> SMat{ZZRingElem}Compute the Hermite normal form of $A$ using the Kannan-Bachem algorithm.
sourcediagonal_form — Method
diagonal_form(A::SMat{ZZRingElem}) -> SMat{ZZRingElem}A matrix $D$ that is diagonal and obtained via unimodular row and column operations. Like a snf without the divisibility condition.
sourceManipulation/ Access
swap_rows! — Method
swap_cols! — Method
scale_row! — Method
add_scaled_col! — Method
add_scaled_col!(A::SMat{T}, i::Int, j::Int, c::T)Add $c$ times the $i$-th column to the $j$-th column of $A$ inplace, that is, $A_j \rightarrow A_j + c \cdot A_i$, where $(A_i)_i$ denote the columns of $A$.
sourceadd_scaled_row! — Method
add_scaled_row!(A::SMat{T}, i::Int, j::Int, c::T)Add $c$ times the $i$-th row to the $j$-th row of $A$ inplace, that is, $A_j \rightarrow A_j + c \cdot A_i$, where $(A_i)_i$ denote the rows of $A$.
sourcetransform_row! — Method
transform_row!(A::SMat{T}, i::Int, j::Int, a::T, b::T, c::T, d::T)Applies the transformation $(A_i, A_j) \rightarrow (aA_i + bA_j, cA_i + dA_j)$ to $A$, where $(A_i)_i$ are the rows of $A$.
sourcereverse_rows! — Method
find_row_starting_with — Method
find_row_starting_with(A::SMat, p::Int) -> IntTries to find the index $i$ such that $A_{i,p} \neq 0$ and $A_{i, p-j} = 0$ for all $j > 1$. It is assumed that $A$ is upper triangular. If such an index does not exist, find the smallest index larger.
sourceChanging of the ring:
map_entries — Method
map_entries(f, A::SMat) -> SMatGiven a sparse matrix $A$ and a callable object $f$, this function will construct a new sparse matrix by applying $f$ to all elements of $A$.
sourcechange_base_ring — Method
change_base_ring(R::Ring, A::SMat)Create a new sparse matrix by coercing all elements into the ring $R$.
sourceArithmetic
Matrices support the usual operations as well
+,-,==div,divexactby scalars- multiplication by scalars
Various products:
mul_sparse — Method
mul_sparse(A::SMat{T}, B::SMat{T}) -> SMat{T}Return the product $A\cdot B$ as a sparse matrix.
The product of two sparse matrices is, in general, not sparse, so depending on the context, it might be more efficient to use mul_dense(::SMat{T}, ::SMat{T}) where {T} instead.
Other: