packages feed

moonlight-linalg-0.1.0.0: src-sparse/Moonlight/LinAlg/Pure/Sparse/Solver/Mutable.hs

{-# LANGUAGE BangPatterns #-}

module Moonlight.LinAlg.Pure.Sparse.Solver.Mutable
  ( MutableDoubleVector,
    newMutableDoubleVector,
    thawMutableDoubleVector,
    freezeMutableDoubleVector,
    copyImmutableIntoMutable,
    copyImmutableSquaredNormIntoMutable,
    copyMutableVector,
    dotMutableVector,
    normMutableVector,
    addScaledMutableVector,
    scaleMutableVector,
    scaledCopyMutableVector,
    addScaledPairIntoMutable,
    subtractMutableInto,
    csrMatVecIntoMutable,
    csrMatVecDotIntoMutable,
    residualIntoMutable,
    csrResidualSquaredIntoMutable,
    updateSolutionAndResidualSquaredMutable,
    updateDirectionMutable,
    initializeZeroJacobiMutable,
    divideByDiagonalDotAndCopyMutable,
    updateSolutionResidualJacobiMutable,
    divideByDiagonalAndDotIntoMutable,
    divideByDiagonalIntoMutable,
    multiplyByDiagonalIntoMutable,
    lowerTriangularSolveIntoMutable,
    upperTriangularSolveIntoMutable,
  )
where

import Control.Monad.ST (ST)
import Data.Kind (Type)
import Data.Primitive.ByteArray
  ( ByteArray,
    MutableByteArray,
    indexByteArray,
    readByteArray,
    writeByteArray,
  )
import Data.Vector.Primitive qualified as P
import Data.Vector.Primitive.Mutable qualified as PM
import Data.Vector.Unboxed.Base qualified as UB
import Data.Vector.Unboxed qualified as U
import Data.Vector.Unboxed.Mutable qualified as MU
import Moonlight.LinAlg.Pure.Sparse.Types
  ( CSRExecutionPlan (..),
    SparseCSR,
    csrColumnIndicesVector,
    csrRows,
    csrExecutionPlan,
    csrRowOffsetsVector,
    csrValuesVector,
  )
import Prelude

type MutableDoubleVector :: Type -> Type
type MutableDoubleVector s = MU.MVector s Double

newMutableDoubleVector :: Int -> ST s (MutableDoubleVector s)
newMutableDoubleVector !dimension =
  MU.replicate dimension 0.0
{-# INLINE newMutableDoubleVector #-}

thawMutableDoubleVector :: U.Vector Double -> ST s (MutableDoubleVector s)
thawMutableDoubleVector = U.thaw
{-# INLINE thawMutableDoubleVector #-}

freezeMutableDoubleVector :: MutableDoubleVector s -> ST s (U.Vector Double)
freezeMutableDoubleVector = U.freeze
{-# INLINE freezeMutableDoubleVector #-}

copyImmutableIntoMutable ::
  U.Vector Double ->
  MutableDoubleVector s ->
  ST s ()
copyImmutableIntoMutable sourceVector targetVector =
  go 0
  where
    !dimension = U.length sourceVector

    go !indexValue
      | indexValue >= dimension = pure ()
      | otherwise = do
          MU.unsafeWrite
            targetVector
            indexValue
            (sourceVector `U.unsafeIndex` indexValue)
          go (indexValue + 1)
{-# INLINE copyImmutableIntoMutable #-}

copyImmutableSquaredNormIntoMutable ::
  U.Vector Double ->
  MutableDoubleVector s ->
  ST s Double
copyImmutableSquaredNormIntoMutable sourceVector targetVector =
  go 0 0.0
  where
    !dimension = U.length sourceVector

    go !indexValue !sumSquares
      | indexValue >= dimension = pure sumSquares
      | otherwise = do
          let !entryValue = sourceVector `U.unsafeIndex` indexValue
          MU.unsafeWrite targetVector indexValue entryValue
          go
            (indexValue + 1)
            (sumSquares + entryValue * entryValue)
{-# INLINE copyImmutableSquaredNormIntoMutable #-}

copyMutableVector ::
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
copyMutableVector sourceVector targetVector =
  MU.unsafeCopy targetVector sourceVector
{-# INLINE copyMutableVector #-}

dotMutableVector ::
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s Double
dotMutableVector leftVector rightVector =
  go 0 0.0
  where
    !dimension = MU.length leftVector

    go !indexValue !accumulator
      | indexValue >= dimension = pure accumulator
      | otherwise = do
          leftValue <- MU.unsafeRead leftVector indexValue
          rightValue <- MU.unsafeRead rightVector indexValue
          go
            (indexValue + 1)
            (accumulator + leftValue * rightValue)
{-# INLINE dotMutableVector #-}

normMutableVector :: MutableDoubleVector s -> ST s Double
normMutableVector vectorValue =
  sqrt <$> dotMutableVector vectorValue vectorValue
{-# INLINE normMutableVector #-}

addScaledMutableVector ::
  Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
addScaledMutableVector !scaleValue sourceVector targetVector =
  go 0
  where
    !dimension = MU.length sourceVector

    go !indexValue
      | indexValue >= dimension = pure ()
      | otherwise = do
          sourceValue <- MU.unsafeRead sourceVector indexValue
          targetValue <- MU.unsafeRead targetVector indexValue
          MU.unsafeWrite
            targetVector
            indexValue
            (targetValue + scaleValue * sourceValue)
          go (indexValue + 1)
{-# INLINE addScaledMutableVector #-}

scaleMutableVector ::
  Double ->
  MutableDoubleVector s ->
  ST s ()
scaleMutableVector !scaleValue targetVector =
  go 0
  where
    !dimension = MU.length targetVector

    go !indexValue
      | indexValue >= dimension = pure ()
      | otherwise = do
          targetValue <- MU.unsafeRead targetVector indexValue
          MU.unsafeWrite
            targetVector
            indexValue
            (scaleValue * targetValue)
          go (indexValue + 1)
{-# INLINE scaleMutableVector #-}

scaledCopyMutableVector ::
  Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
scaledCopyMutableVector !scaleValue sourceVector targetVector =
  go 0
  where
    !dimension = MU.length sourceVector

    go !indexValue
      | indexValue >= dimension = pure ()
      | otherwise = do
          sourceValue <- MU.unsafeRead sourceVector indexValue
          MU.unsafeWrite
            targetVector
            indexValue
            (scaleValue * sourceValue)
          go (indexValue + 1)
{-# INLINE scaledCopyMutableVector #-}

addScaledPairIntoMutable ::
  Double ->
  MutableDoubleVector s ->
  Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
addScaledPairIntoMutable
  !leftScale
  leftVector
  !rightScale
  rightVector
  targetVector =
    go 0
  where
    !dimension = MU.length targetVector

    go !indexValue
      | indexValue >= dimension = pure ()
      | otherwise = do
          leftValue <- MU.unsafeRead leftVector indexValue
          rightValue <- MU.unsafeRead rightVector indexValue
          MU.unsafeWrite
            targetVector
            indexValue
            (leftScale * leftValue + rightScale * rightValue)
          go (indexValue + 1)
{-# INLINE addScaledPairIntoMutable #-}

subtractMutableInto ::
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
subtractMutableInto leftVector rightVector targetVector =
  addScaledPairIntoMutable
    1.0
    leftVector
    (-1.0)
    rightVector
    targetVector
{-# INLINE subtractMutableInto #-}

csrMatVecIntoMutable ::
  SparseCSR Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
csrMatVecIntoMutable sparseMatrix inputVector targetVector =
  case csrExecutionPlan sparseMatrix of
    CSRGeneral ->
      csrMatVecGeneralIntoMutable
        sparseMatrix
        inputVector
        targetVector
    CSRContiguousBand 2 2
      | csrRows sparseMatrix >= 5 ->
          pentadiagonalMatVecIntoMutable
            (csrRows sparseMatrix)
            (csrValuesVector sparseMatrix)
            inputVector
            targetVector
    CSRContiguousBand lowerBandwidth upperBandwidth ->
      contiguousBandMatVecIntoMutable
        (csrRows sparseMatrix)
        lowerBandwidth
        upperBandwidth
        (csrValuesVector sparseMatrix)
        inputVector
        targetVector
{-# INLINE csrMatVecIntoMutable #-}

csrMatVecGeneralIntoMutable ::
  SparseCSR Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
csrMatVecGeneralIntoMutable sparseMatrix inputVector targetVector =
  writeRows 0
  where
    !rowCount = csrRows sparseMatrix
    !rowOffsets = csrRowOffsetsVector sparseMatrix
    !columnIndices = csrColumnIndicesVector sparseMatrix
    !coefficients = csrValuesVector sparseMatrix

    writeRows !rowIndex
      | rowIndex >= rowCount = pure ()
      | otherwise = do
          rowValue <-
            csrRowDotMutable
              rowOffsets
              columnIndices
              coefficients
              inputVector
              rowIndex
          MU.unsafeWrite targetVector rowIndex rowValue
          writeRows (rowIndex + 1)
{-# INLINE csrMatVecGeneralIntoMutable #-}

pentadiagonalMatVecIntoMutable ::
  Int ->
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
pentadiagonalMatVecIntoMutable
  rowCount
  (UB.V_Double (P.Vector coefficientBase _ coefficientArray))
  (UB.MV_Double (PM.MVector inputBase _ inputArray))
  (UB.MV_Double (PM.MVector targetBase _ targetArray)) =
    go 0
  where
    go !rowIndex
      | rowIndex >= rowCount = pure ()
      | otherwise = do
          rowValue <-
            pentadiagonalRowDotMutable
              rowCount
              coefficientBase
              coefficientArray
              inputBase
              inputArray
              rowIndex
          writeByteArray
            targetArray
            (targetBase + rowIndex)
            rowValue
          go (rowIndex + 1)
{-# INLINE pentadiagonalMatVecIntoMutable #-}

pentadiagonalMatVecDotIntoMutable ::
  Int ->
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s Double
pentadiagonalMatVecDotIntoMutable
  rowCount
  (UB.V_Double (P.Vector coefficientBase _ coefficientArray))
  (UB.MV_Double (PM.MVector inputBase _ inputArray))
  (UB.MV_Double (PM.MVector targetBase _ targetArray)) =
    go 0 0.0
  where
    go !rowIndex !dotAccumulator
      | rowIndex >= rowCount = pure dotAccumulator
      | otherwise = do
          rowValue <-
            pentadiagonalRowDotMutable
              rowCount
              coefficientBase
              coefficientArray
              inputBase
              inputArray
              rowIndex
          inputValue <-
            readByteArray inputArray (inputBase + rowIndex)
          writeByteArray
            targetArray
            (targetBase + rowIndex)
            rowValue
          go
            (rowIndex + 1)
            (dotAccumulator + (inputValue :: Double) * rowValue)
{-# INLINE pentadiagonalMatVecDotIntoMutable #-}

pentadiagonalResidualSquaredIntoMutable ::
  Int ->
  U.Vector Double ->
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s Double
pentadiagonalResidualSquaredIntoMutable
  rowCount
  (UB.V_Double (P.Vector coefficientBase _ coefficientArray))
  (UB.V_Double (P.Vector rhsBase _ rhsArray))
  (UB.MV_Double (PM.MVector guessBase _ guessArray))
  (UB.MV_Double (PM.MVector residualBase _ residualArray)) =
    go 0 0.0
  where
    go !rowIndex !sumSquares
      | rowIndex >= rowCount = pure sumSquares
      | otherwise = do
          imageValue <-
            pentadiagonalRowDotMutable
              rowCount
              coefficientBase
              coefficientArray
              guessBase
              guessArray
              rowIndex
          let !rhsValue =
                ( indexByteArray
                    rhsArray
                    (rhsBase + rowIndex)
                    :: Double
                )
              !residualValue = rhsValue - imageValue
          writeByteArray
            residualArray
            (residualBase + rowIndex)
            residualValue
          go
            (rowIndex + 1)
            (sumSquares + residualValue * residualValue)
{-# INLINE pentadiagonalResidualSquaredIntoMutable #-}

pentadiagonalRowDotMutable ::
  Int ->
  Int ->
  ByteArray ->
  Int ->
  MutableByteArray s ->
  Int ->
  ST s Double
pentadiagonalRowDotMutable
  rowCount
  coefficientBase
  coefficientArray
  inputBase
  inputArray
  rowIndex
    | rowIndex == 0 = do
        input0 <- readInput 0
        input1 <- readInput 1
        input2 <- readInput 2
        pure
          ( coefficientAt 0 * input0
              + coefficientAt 1 * input1
              + coefficientAt 2 * input2
          )
    | rowIndex == 1 = do
        input0 <- readInput 0
        input1 <- readInput 1
        input2 <- readInput 2
        input3 <- readInput 3
        pure
          ( coefficientAt 3 * input0
              + coefficientAt 4 * input1
              + coefficientAt 5 * input2
              + coefficientAt 6 * input3
          )
    | rowIndex + 2 < rowCount = do
        let !entryIndex = 5 * rowIndex - 3
        input0 <- readInput (rowIndex - 2)
        input1 <- readInput (rowIndex - 1)
        input2 <- readInput rowIndex
        input3 <- readInput (rowIndex + 1)
        input4 <- readInput (rowIndex + 2)
        pure
          ( coefficientAt entryIndex * input0
              + coefficientAt (entryIndex + 1) * input1
              + coefficientAt (entryIndex + 2) * input2
              + coefficientAt (entryIndex + 3) * input3
              + coefficientAt (entryIndex + 4) * input4
          )
    | rowIndex + 1 < rowCount = do
        let !entryIndex = 5 * rowCount - 13
        input0 <- readInput (rowCount - 4)
        input1 <- readInput (rowCount - 3)
        input2 <- readInput (rowCount - 2)
        input3 <- readInput (rowCount - 1)
        pure
          ( coefficientAt entryIndex * input0
              + coefficientAt (entryIndex + 1) * input1
              + coefficientAt (entryIndex + 2) * input2
              + coefficientAt (entryIndex + 3) * input3
          )
    | otherwise = do
        let !entryIndex = 5 * rowCount - 9
        input0 <- readInput (rowCount - 3)
        input1 <- readInput (rowCount - 2)
        input2 <- readInput (rowCount - 1)
        pure
          ( coefficientAt entryIndex * input0
              + coefficientAt (entryIndex + 1) * input1
              + coefficientAt (entryIndex + 2) * input2
          )
  where
    coefficientAt !entryIndex =
      ( indexByteArray
          coefficientArray
          (coefficientBase + entryIndex)
          :: Double
      )
    readInput !columnIndex =
      readByteArray inputArray (inputBase + columnIndex)
{-# INLINE pentadiagonalRowDotMutable #-}

contiguousBandMatVecIntoMutable ::
  Int ->
  Int ->
  Int ->
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
contiguousBandMatVecIntoMutable
  rowCount
  lowerBandwidth
  upperBandwidth
  coefficients
  inputVector
  targetVector =
    writeRows 0 0
  where
    writeRows !rowIndex !entryIndex
      | rowIndex >= rowCount = pure ()
      | otherwise = do
          let !firstColumn = max 0 (rowIndex - lowerBandwidth)
              !lastColumn = min (rowCount - 1) (rowIndex + upperBandwidth)
              !entryCount = lastColumn - firstColumn + 1
          rowValue <-
            accumulateBand
              entryIndex
              firstColumn
              entryCount
              0.0
          MU.unsafeWrite targetVector rowIndex rowValue
          writeRows (rowIndex + 1) (entryIndex + entryCount)

    accumulateBand !entryIndex !columnIndex !remaining !accumulator
      | remaining <= 0 = pure accumulator
      | otherwise = do
          inputValue <- MU.unsafeRead inputVector columnIndex
          let !coefficient = coefficients `U.unsafeIndex` entryIndex
          accumulateBand
            (entryIndex + 1)
            (columnIndex + 1)
            (remaining - 1)
            (accumulator + coefficient * inputValue)
{-# INLINE contiguousBandMatVecIntoMutable #-}

csrMatVecDotIntoMutable ::
  SparseCSR Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s Double
csrMatVecDotIntoMutable sparseMatrix inputVector targetVector =
  case csrExecutionPlan sparseMatrix of
    CSRGeneral ->
      csrMatVecDotGeneralIntoMutable
        sparseMatrix
        inputVector
        targetVector
    CSRContiguousBand 2 2
      | csrRows sparseMatrix >= 5 ->
          pentadiagonalMatVecDotIntoMutable
            (csrRows sparseMatrix)
            (csrValuesVector sparseMatrix)
            inputVector
            targetVector
    CSRContiguousBand lowerBandwidth upperBandwidth ->
      contiguousBandMatVecDotIntoMutable
        (csrRows sparseMatrix)
        lowerBandwidth
        upperBandwidth
        (csrValuesVector sparseMatrix)
        inputVector
        targetVector
{-# INLINE csrMatVecDotIntoMutable #-}

csrMatVecDotGeneralIntoMutable ::
  SparseCSR Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s Double
csrMatVecDotGeneralIntoMutable sparseMatrix inputVector targetVector =
  writeRows 0 0.0
  where
    !rowCount = csrRows sparseMatrix
    !rowOffsets = csrRowOffsetsVector sparseMatrix
    !columnIndices = csrColumnIndicesVector sparseMatrix
    !coefficients = csrValuesVector sparseMatrix

    writeRows !rowIndex !dotAccumulator
      | rowIndex >= rowCount = pure dotAccumulator
      | otherwise = do
          rowValue <-
            csrRowDotMutable
              rowOffsets
              columnIndices
              coefficients
              inputVector
              rowIndex
          inputValue <- MU.unsafeRead inputVector rowIndex
          MU.unsafeWrite targetVector rowIndex rowValue
          writeRows
            (rowIndex + 1)
            (dotAccumulator + inputValue * rowValue)
{-# INLINE csrMatVecDotGeneralIntoMutable #-}

contiguousBandMatVecDotIntoMutable ::
  Int ->
  Int ->
  Int ->
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s Double
contiguousBandMatVecDotIntoMutable
  rowCount
  lowerBandwidth
  upperBandwidth
  coefficients
  inputVector
  targetVector =
    writeRows 0 0 0.0
  where
    writeRows !rowIndex !entryIndex !dotAccumulator
      | rowIndex >= rowCount = pure dotAccumulator
      | otherwise = do
          let !firstColumn = max 0 (rowIndex - lowerBandwidth)
              !lastColumn = min (rowCount - 1) (rowIndex + upperBandwidth)
              !entryCount = lastColumn - firstColumn + 1
          rowValue <-
            accumulateBand
              entryIndex
              firstColumn
              entryCount
              0.0
          inputValue <- MU.unsafeRead inputVector rowIndex
          MU.unsafeWrite targetVector rowIndex rowValue
          writeRows
            (rowIndex + 1)
            (entryIndex + entryCount)
            (dotAccumulator + inputValue * rowValue)

    accumulateBand !entryIndex !columnIndex !remaining !accumulator
      | remaining <= 0 = pure accumulator
      | otherwise = do
          inputValue <- MU.unsafeRead inputVector columnIndex
          let !coefficient = coefficients `U.unsafeIndex` entryIndex
          accumulateBand
            (entryIndex + 1)
            (columnIndex + 1)
            (remaining - 1)
            (accumulator + coefficient * inputValue)
{-# INLINE contiguousBandMatVecDotIntoMutable #-}

residualIntoMutable ::
  SparseCSR Double ->
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
residualIntoMutable
  sparseMatrix
  rhsValues
  guessVector
  imageVector
  residualVector = do
    csrMatVecIntoMutable sparseMatrix guessVector imageVector
    writeResidual 0
  where
    !dimension = U.length rhsValues

    writeResidual !indexValue
      | indexValue >= dimension = pure ()
      | otherwise = do
          imageValue <- MU.unsafeRead imageVector indexValue
          MU.unsafeWrite
            residualVector
            indexValue
            (rhsValues `U.unsafeIndex` indexValue - imageValue)
          writeResidual (indexValue + 1)
{-# INLINE residualIntoMutable #-}

csrResidualSquaredIntoMutable ::
  SparseCSR Double ->
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s Double
csrResidualSquaredIntoMutable
  sparseMatrix
  rhsValues
  guessVector
  residualVector =
    case csrExecutionPlan sparseMatrix of
      CSRGeneral ->
        csrResidualSquaredGeneralIntoMutable
          sparseMatrix
          rhsValues
          guessVector
          residualVector
      CSRContiguousBand 2 2
        | csrRows sparseMatrix >= 5 ->
            pentadiagonalResidualSquaredIntoMutable
              (csrRows sparseMatrix)
              (csrValuesVector sparseMatrix)
              rhsValues
              guessVector
              residualVector
      CSRContiguousBand lowerBandwidth upperBandwidth ->
        contiguousBandResidualSquaredIntoMutable
          (csrRows sparseMatrix)
          lowerBandwidth
          upperBandwidth
          (csrValuesVector sparseMatrix)
          rhsValues
          guessVector
          residualVector
{-# INLINE csrResidualSquaredIntoMutable #-}

csrResidualSquaredGeneralIntoMutable ::
  SparseCSR Double ->
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s Double
csrResidualSquaredGeneralIntoMutable
  sparseMatrix
  rhsValues
  guessVector
  residualVector =
    writeRows 0 0.0
  where
    !rowCount = csrRows sparseMatrix
    !rowOffsets = csrRowOffsetsVector sparseMatrix
    !columnIndices = csrColumnIndicesVector sparseMatrix
    !coefficients = csrValuesVector sparseMatrix

    writeRows !rowIndex !sumSquares
      | rowIndex >= rowCount = pure sumSquares
      | otherwise = do
          imageValue <-
            csrRowDotMutable
              rowOffsets
              columnIndices
              coefficients
              guessVector
              rowIndex
          let !residualValue =
                rhsValues `U.unsafeIndex` rowIndex - imageValue
          MU.unsafeWrite residualVector rowIndex residualValue
          writeRows
            (rowIndex + 1)
            (sumSquares + residualValue * residualValue)
{-# INLINE csrResidualSquaredGeneralIntoMutable #-}

contiguousBandResidualSquaredIntoMutable ::
  Int ->
  Int ->
  Int ->
  U.Vector Double ->
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s Double
contiguousBandResidualSquaredIntoMutable
  rowCount
  lowerBandwidth
  upperBandwidth
  coefficients
  rhsValues
  guessVector
  residualVector =
    writeRows 0 0 0.0
  where
    writeRows !rowIndex !entryIndex !sumSquares
      | rowIndex >= rowCount = pure sumSquares
      | otherwise = do
          let !firstColumn = max 0 (rowIndex - lowerBandwidth)
              !lastColumn = min (rowCount - 1) (rowIndex + upperBandwidth)
              !entryCount = lastColumn - firstColumn + 1
          imageValue <-
            accumulateBand
              entryIndex
              firstColumn
              entryCount
              0.0
          let !residualValue =
                rhsValues `U.unsafeIndex` rowIndex - imageValue
          MU.unsafeWrite residualVector rowIndex residualValue
          writeRows
            (rowIndex + 1)
            (entryIndex + entryCount)
            (sumSquares + residualValue * residualValue)

    accumulateBand !entryIndex !columnIndex !remaining !accumulator
      | remaining <= 0 = pure accumulator
      | otherwise = do
          guessValue <- MU.unsafeRead guessVector columnIndex
          let !coefficient = coefficients `U.unsafeIndex` entryIndex
          accumulateBand
            (entryIndex + 1)
            (columnIndex + 1)
            (remaining - 1)
            (accumulator + coefficient * guessValue)
{-# INLINE contiguousBandResidualSquaredIntoMutable #-}

updateSolutionAndResidualSquaredMutable ::
  Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s Double
updateSolutionAndResidualSquaredMutable
  !alphaValue
  directionVector
  imageDirectionVector
  guessVector
  residualVector =
    go 0 0.0
  where
    !dimension = MU.length guessVector

    go !indexValue !sumSquares
      | indexValue >= dimension = pure sumSquares
      | otherwise = do
          directionValue <- MU.unsafeRead directionVector indexValue
          imageDirectionValue <-
            MU.unsafeRead imageDirectionVector indexValue
          guessValue <- MU.unsafeRead guessVector indexValue
          residualValue <- MU.unsafeRead residualVector indexValue
          let !nextGuessValue =
                guessValue + alphaValue * directionValue
              !nextResidualValue =
                residualValue - alphaValue * imageDirectionValue
          MU.unsafeWrite guessVector indexValue nextGuessValue
          MU.unsafeWrite residualVector indexValue nextResidualValue
          go
            (indexValue + 1)
            (sumSquares + nextResidualValue * nextResidualValue)
{-# INLINE updateSolutionAndResidualSquaredMutable #-}

updateDirectionMutable ::
  Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
updateDirectionMutable !betaValue sourceVector directionVector =
  go 0
  where
    !dimension = MU.length directionVector

    go !indexValue
      | indexValue >= dimension = pure ()
      | otherwise = do
          sourceValue <- MU.unsafeRead sourceVector indexValue
          directionValue <- MU.unsafeRead directionVector indexValue
          MU.unsafeWrite
            directionVector
            indexValue
            (sourceValue + betaValue * directionValue)
          go (indexValue + 1)
{-# INLINE updateDirectionMutable #-}

initializeZeroJacobiMutable ::
  U.Vector Double ->
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s (Double, Double)
initializeZeroJacobiMutable
  rhsValues
  diagonalValues
  residualVector
  preconditionedResidualVector
  directionVector =
    go 0 0.0 0.0
  where
    !dimension = U.length rhsValues

    go !indexValue !residualSquared !rhoValue
      | indexValue >= dimension =
          pure (residualSquared, rhoValue)
      | otherwise = do
          let !residualValue = rhsValues `U.unsafeIndex` indexValue
              !preconditionedValue =
                residualValue
                  / (diagonalValues `U.unsafeIndex` indexValue)
          MU.unsafeWrite residualVector indexValue residualValue
          MU.unsafeWrite
            preconditionedResidualVector
            indexValue
            preconditionedValue
          MU.unsafeWrite
            directionVector
            indexValue
            preconditionedValue
          go
            (indexValue + 1)
            (residualSquared + residualValue * residualValue)
            (rhoValue + residualValue * preconditionedValue)
{-# INLINE initializeZeroJacobiMutable #-}

divideByDiagonalDotAndCopyMutable ::
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s Double
divideByDiagonalDotAndCopyMutable
  diagonalValues
  sourceVector
  targetVector
  directionVector =
    go 0 0.0
  where
    !dimension = U.length diagonalValues

    go !indexValue !dotAccumulator
      | indexValue >= dimension = pure dotAccumulator
      | otherwise = do
          sourceValue <- MU.unsafeRead sourceVector indexValue
          let !targetValue =
                sourceValue
                  / (diagonalValues `U.unsafeIndex` indexValue)
          MU.unsafeWrite targetVector indexValue targetValue
          MU.unsafeWrite directionVector indexValue targetValue
          go
            (indexValue + 1)
            (dotAccumulator + sourceValue * targetValue)
{-# INLINE divideByDiagonalDotAndCopyMutable #-}

updateSolutionResidualJacobiMutable ::
  Double ->
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s (Double, Double)
updateSolutionResidualJacobiMutable
  !alphaValue
  diagonalValues
  directionVector
  imageDirectionVector
  guessVector
  residualVector
  preconditionedResidualVector =
    go 0 0.0 0.0
  where
    !dimension = MU.length guessVector

    go !indexValue !residualSquared !rhoValue
      | indexValue >= dimension =
          pure (residualSquared, rhoValue)
      | otherwise = do
          directionValue <- MU.unsafeRead directionVector indexValue
          imageDirectionValue <-
            MU.unsafeRead imageDirectionVector indexValue
          guessValue <- MU.unsafeRead guessVector indexValue
          residualValue <- MU.unsafeRead residualVector indexValue
          let !nextGuessValue =
                guessValue + alphaValue * directionValue
              !nextResidualValue =
                residualValue - alphaValue * imageDirectionValue
              !preconditionedValue =
                nextResidualValue
                  / (diagonalValues `U.unsafeIndex` indexValue)
          MU.unsafeWrite guessVector indexValue nextGuessValue
          MU.unsafeWrite residualVector indexValue nextResidualValue
          MU.unsafeWrite
            preconditionedResidualVector
            indexValue
            preconditionedValue
          go
            (indexValue + 1)
            (residualSquared + nextResidualValue * nextResidualValue)
            (rhoValue + nextResidualValue * preconditionedValue)
{-# INLINE updateSolutionResidualJacobiMutable #-}

divideByDiagonalAndDotIntoMutable ::
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s Double
divideByDiagonalAndDotIntoMutable
  diagonalValues
  sourceVector
  targetVector =
    go 0 0.0
  where
    !dimension = U.length diagonalValues

    go !indexValue !dotAccumulator
      | indexValue >= dimension = pure dotAccumulator
      | otherwise = do
          sourceValue <- MU.unsafeRead sourceVector indexValue
          let !targetValue =
                sourceValue
                  / (diagonalValues `U.unsafeIndex` indexValue)
          MU.unsafeWrite targetVector indexValue targetValue
          go
            (indexValue + 1)
            (dotAccumulator + sourceValue * targetValue)
{-# INLINE divideByDiagonalAndDotIntoMutable #-}

divideByDiagonalIntoMutable ::
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
divideByDiagonalIntoMutable diagonalValues sourceVector targetVector =
  go 0
  where
    !dimension = U.length diagonalValues

    go !indexValue
      | indexValue >= dimension = pure ()
      | otherwise = do
          sourceValue <- MU.unsafeRead sourceVector indexValue
          MU.unsafeWrite
            targetVector
            indexValue
            (sourceValue / (diagonalValues `U.unsafeIndex` indexValue))
          go (indexValue + 1)
{-# INLINE divideByDiagonalIntoMutable #-}

multiplyByDiagonalIntoMutable ::
  U.Vector Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
multiplyByDiagonalIntoMutable diagonalValues sourceVector targetVector =
  go 0
  where
    !dimension = U.length diagonalValues

    go !indexValue
      | indexValue >= dimension = pure ()
      | otherwise = do
          sourceValue <- MU.unsafeRead sourceVector indexValue
          MU.unsafeWrite
            targetVector
            indexValue
            (sourceValue * (diagonalValues `U.unsafeIndex` indexValue))
          go (indexValue + 1)
{-# INLINE multiplyByDiagonalIntoMutable #-}

lowerTriangularSolveIntoMutable ::
  U.Vector Double ->
  SparseCSR Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
lowerTriangularSolveIntoMutable
  diagonalValues
  sparseMatrix
  rhsVector
  targetVector =
    solveRows 0
  where
    !dimension = csrRows sparseMatrix
    !rowOffsets = csrRowOffsetsVector sparseMatrix
    !columnIndices = csrColumnIndicesVector sparseMatrix
    !coefficients = csrValuesVector sparseMatrix

    solveRows !rowIndex
      | rowIndex >= dimension = pure ()
      | otherwise = do
          rhsValue <- MU.unsafeRead rhsVector rowIndex
          knownProduct <-
            lowerKnownProduct
              rowOffsets
              columnIndices
              coefficients
              targetVector
              rowIndex
          MU.unsafeWrite
            targetVector
            rowIndex
            ( (rhsValue - knownProduct)
                / (diagonalValues `U.unsafeIndex` rowIndex)
            )
          solveRows (rowIndex + 1)
{-# INLINE lowerTriangularSolveIntoMutable #-}

upperTriangularSolveIntoMutable ::
  U.Vector Double ->
  SparseCSR Double ->
  MutableDoubleVector s ->
  MutableDoubleVector s ->
  ST s ()
upperTriangularSolveIntoMutable
  diagonalValues
  sparseMatrix
  rhsVector
  targetVector =
    solveRows (dimension - 1)
  where
    !dimension = csrRows sparseMatrix
    !rowOffsets = csrRowOffsetsVector sparseMatrix
    !columnIndices = csrColumnIndicesVector sparseMatrix
    !coefficients = csrValuesVector sparseMatrix

    solveRows !rowIndex
      | rowIndex < 0 = pure ()
      | otherwise = do
          rhsValue <- MU.unsafeRead rhsVector rowIndex
          knownProduct <-
            upperKnownProduct
              rowOffsets
              columnIndices
              coefficients
              targetVector
              rowIndex
          MU.unsafeWrite
            targetVector
            rowIndex
            ( (rhsValue - knownProduct)
                / (diagonalValues `U.unsafeIndex` rowIndex)
            )
          solveRows (rowIndex - 1)
{-# INLINE upperTriangularSolveIntoMutable #-}

csrRowDotMutable ::
  U.Vector Int ->
  U.Vector Int ->
  U.Vector Double ->
  MutableDoubleVector s ->
  Int ->
  ST s Double
csrRowDotMutable
  rowOffsets
  columnIndices
  coefficients
  inputVector
  rowIndex =
    go startIndex 0.0
  where
    !startIndex = rowOffsets `U.unsafeIndex` rowIndex
    !stopIndex = rowOffsets `U.unsafeIndex` (rowIndex + 1)

    go !entryIndex !accumulator
      | entryIndex >= stopIndex = pure accumulator
      | otherwise = do
          let !columnIndex =
                columnIndices `U.unsafeIndex` entryIndex
              !coefficientValue =
                coefficients `U.unsafeIndex` entryIndex
          inputValue <- MU.unsafeRead inputVector columnIndex
          go
            (entryIndex + 1)
            (accumulator + coefficientValue * inputValue)
{-# INLINE csrRowDotMutable #-}

lowerKnownProduct ::
  U.Vector Int ->
  U.Vector Int ->
  U.Vector Double ->
  MutableDoubleVector s ->
  Int ->
  ST s Double
lowerKnownProduct
  rowOffsets
  columnIndices
  coefficients
  solutionVector
  rowIndex =
    go startIndex 0.0
  where
    !startIndex = rowOffsets `U.unsafeIndex` rowIndex
    !stopIndex = rowOffsets `U.unsafeIndex` (rowIndex + 1)

    go !entryIndex !accumulator
      | entryIndex >= stopIndex = pure accumulator
      | otherwise =
          let !columnIndex =
                columnIndices `U.unsafeIndex` entryIndex
              !coefficientValue =
                coefficients `U.unsafeIndex` entryIndex
           in if columnIndex < rowIndex
                then do
                  solutionValue <-
                    MU.unsafeRead solutionVector columnIndex
                  go
                    (entryIndex + 1)
                    (accumulator + coefficientValue * solutionValue)
                else go (entryIndex + 1) accumulator
{-# INLINE lowerKnownProduct #-}

upperKnownProduct ::
  U.Vector Int ->
  U.Vector Int ->
  U.Vector Double ->
  MutableDoubleVector s ->
  Int ->
  ST s Double
upperKnownProduct
  rowOffsets
  columnIndices
  coefficients
  solutionVector
  rowIndex =
    go startIndex 0.0
  where
    !startIndex = rowOffsets `U.unsafeIndex` rowIndex
    !stopIndex = rowOffsets `U.unsafeIndex` (rowIndex + 1)

    go !entryIndex !accumulator
      | entryIndex >= stopIndex = pure accumulator
      | otherwise =
          let !columnIndex =
                columnIndices `U.unsafeIndex` entryIndex
              !coefficientValue =
                coefficients `U.unsafeIndex` entryIndex
           in if columnIndex > rowIndex
                then do
                  solutionValue <-
                    MU.unsafeRead solutionVector columnIndex
                  go
                    (entryIndex + 1)
                    (accumulator + coefficientValue * solutionValue)
                else go (entryIndex + 1) accumulator
{-# INLINE upperKnownProduct #-}