moonlight-linalg-0.1.0.0: src-sparse/Moonlight/LinAlg/Pure/Sparse/Solver/Preconditioner.hs
module Moonlight.LinAlg.Pure.Sparse.Solver.Preconditioner
( SparsePreconditioner (..),
applySparsePreconditionerMutable,
applySparsePreconditionerAndDotMutable,
preconditionerDimension,
compileSparsePreconditioner,
)
where
import Control.Monad.ST (ST)
import Data.Kind (Type)
import Data.Vector.Unboxed qualified as U
import Moonlight.Core (fieldValueValid)
import Moonlight.LinAlg.Pure.Sparse.Solver.Common
( shiftedDiagonalValue,
solverEpsilon,
sparseDiagonal,
)
import Moonlight.LinAlg.Pure.Sparse.Solver.IncompleteCholesky0
( IC0Factor,
applyIC0FactorAndDotMutable,
applyIC0FactorMutable,
ic0FactorDimension,
incompleteCholesky0Factor,
)
import Moonlight.LinAlg.Pure.Sparse.Solver.Mutable
( MutableDoubleVector,
copyMutableVector,
divideByDiagonalAndDotIntoMutable,
divideByDiagonalIntoMutable,
dotMutableVector,
lowerTriangularSolveIntoMutable,
multiplyByDiagonalIntoMutable,
scaleMutableVector,
upperTriangularSolveIntoMutable,
)
import Moonlight.LinAlg.Pure.Sparse.Solver.Types
( SparseIterativeFailure (..),
SparsePreconditionerFamily (..),
)
import Moonlight.LinAlg.Pure.Sparse.Types (SparseCSR, csrRows)
import Prelude
type SparsePreconditioner :: Type
data SparsePreconditioner
= IdentitySparsePreconditioner !Int
| DiagonalSparsePreconditioner !(U.Vector Double)
| SsorSparsePreconditioner !Double !(U.Vector Double) !(U.Vector Double) !(SparseCSR Double)
| IncompleteCholesky0SparsePreconditioner !IC0Factor
preconditionerDimension :: SparsePreconditioner -> Int
preconditionerDimension preconditionerValue =
case preconditionerValue of
IdentitySparsePreconditioner dimension -> dimension
DiagonalSparsePreconditioner diagonalValues -> U.length diagonalValues
SsorSparsePreconditioner _ diagonalValues _ _ -> U.length diagonalValues
IncompleteCholesky0SparsePreconditioner factorValue -> ic0FactorDimension factorValue
applySparsePreconditionerMutable ::
SparsePreconditioner ->
MutableDoubleVector s ->
MutableDoubleVector s ->
MutableDoubleVector s ->
MutableDoubleVector s ->
ST s ()
applySparsePreconditionerMutable preconditionerValue sourceVector scratchA scratchB targetVector =
case preconditionerValue of
IdentitySparsePreconditioner _ ->
copyMutableVector sourceVector targetVector
DiagonalSparsePreconditioner diagonalValues ->
divideByDiagonalIntoMutable diagonalValues sourceVector targetVector
SsorSparsePreconditioner omegaValue diagonalValues scaledDiagonalValues sparseMatrix -> do
lowerTriangularSolveIntoMutable scaledDiagonalValues sparseMatrix sourceVector scratchA
multiplyByDiagonalIntoMutable diagonalValues scratchA scratchB
upperTriangularSolveIntoMutable scaledDiagonalValues sparseMatrix scratchB targetVector
scaleMutableVector ((2.0 - omegaValue) / omegaValue) targetVector
IncompleteCholesky0SparsePreconditioner factorValue ->
applyIC0FactorMutable factorValue sourceVector scratchA targetVector
applySparsePreconditionerAndDotMutable ::
SparsePreconditioner ->
MutableDoubleVector s ->
MutableDoubleVector s ->
MutableDoubleVector s ->
MutableDoubleVector s ->
ST s Double
applySparsePreconditionerAndDotMutable
preconditionerValue
sourceVector
scratchA
scratchB
targetVector =
case preconditionerValue of
IdentitySparsePreconditioner _ -> do
copyMutableVector sourceVector targetVector
dotMutableVector sourceVector sourceVector
DiagonalSparsePreconditioner diagonalValues ->
divideByDiagonalAndDotIntoMutable
diagonalValues
sourceVector
targetVector
SsorSparsePreconditioner
omegaValue
diagonalValues
scaledDiagonalValues
sparseMatrix -> do
lowerTriangularSolveIntoMutable
scaledDiagonalValues
sparseMatrix
sourceVector
scratchA
multiplyByDiagonalIntoMutable
diagonalValues
scratchA
scratchB
upperTriangularSolveIntoMutable
scaledDiagonalValues
sparseMatrix
scratchB
targetVector
scaleMutableVector
((2.0 - omegaValue) / omegaValue)
targetVector
dotMutableVector sourceVector targetVector
IncompleteCholesky0SparsePreconditioner factorValue ->
applyIC0FactorAndDotMutable
factorValue
sourceVector
scratchA
targetVector
{-# INLINE applySparsePreconditionerAndDotMutable #-}
compileSparsePreconditioner :: SparsePreconditionerFamily -> SparseCSR Double -> Either SparseIterativeFailure SparsePreconditioner
compileSparsePreconditioner preconditionerFamily sparseMatrix =
case preconditionerFamily of
IdentitySparsePreconditionerFamily ->
Right (IdentitySparsePreconditioner (csrRows sparseMatrix))
DiagonalJacobiSparsePreconditionerFamily ->
diagonalPreconditioner sparseMatrix
ShiftedDiagonalJacobiSparsePreconditionerFamily shiftValue ->
shiftedDiagonalPreconditioner shiftValue sparseMatrix
SsorSparsePreconditionerFamily omegaValue ->
ssorPreconditioner omegaValue sparseMatrix
IncompleteCholesky0SparsePreconditionerFamily configValue ->
IncompleteCholesky0SparsePreconditioner
<$> incompleteCholesky0Factor configValue sparseMatrix
diagonalPreconditioner :: SparseCSR Double -> Either SparseIterativeFailure SparsePreconditioner
diagonalPreconditioner sparseMatrix = do
diagonalValues <- sparseDiagonal sparseMatrix
if U.all strictlyNonZero diagonalValues
then Right (DiagonalSparsePreconditioner diagonalValues)
else Left (SparseInvalidInput "diagonal preconditioner requires a strictly non-zero diagonal")
shiftedDiagonalPreconditioner :: Double -> SparseCSR Double -> Either SparseIterativeFailure SparsePreconditioner
shiftedDiagonalPreconditioner shiftValue sparseMatrix
| shiftValue <= solverEpsilon =
Left (SparseInvalidInput "shifted diagonal preconditioner requires a strictly positive shift")
| otherwise = do
diagonalValues <- sparseDiagonal sparseMatrix
let shiftedDiagonalValues = U.map (shiftedDiagonalValue shiftValue) diagonalValues
if U.all strictlyNonZero shiftedDiagonalValues
then Right (DiagonalSparsePreconditioner shiftedDiagonalValues)
else Left (SparseInvalidInput "shifted diagonal preconditioner requires a non-degenerate shifted diagonal")
ssorPreconditioner :: Double -> SparseCSR Double -> Either SparseIterativeFailure SparsePreconditioner
ssorPreconditioner omegaValue sparseMatrix
| omegaValue <= solverEpsilon || omegaValue >= 2.0 - solverEpsilon =
Left (SparseInvalidInput "SSOR preconditioner requires a relaxation parameter strictly between 0 and 2")
| otherwise = do
diagonalValues <- sparseDiagonal sparseMatrix
let scaledDiagonalValues = U.map (/ omegaValue) diagonalValues
if U.all strictlyNonZero scaledDiagonalValues
then Right (SsorSparsePreconditioner omegaValue diagonalValues scaledDiagonalValues sparseMatrix)
else Left (SparseInvalidInput "SSOR preconditioner requires a strictly non-zero diagonal")
strictlyNonZero :: Double -> Bool
strictlyNonZero value =
fieldValueValid value && abs value > solverEpsilon