packages feed

moonlight-linalg-0.1.0.0: src-spectral/Moonlight/LinAlg/Pure/Krylov/Block.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE StrictData #-}

module Moonlight.LinAlg.Pure.Krylov.Block
  ( blockLanczosSymmetric
  )
where

import Data.Kind (Type)
import qualified Data.Vector as Box
import qualified Data.Vector.Unboxed as U
import Moonlight.Core (MoonlightError (..))
import Moonlight.LinAlg.Pure.Krylov.Config
import Moonlight.LinAlg.Pure.Krylov.Decomposition
import Moonlight.LinAlg.Pure.Operator
import Moonlight.LinAlg.Pure.Krylov.Internal
  ( blockInnerBlock,
    multiplyBasisByBlock,
    normalizeSeedBlock,
    orthonormalizeBlock,
    selfAdjointBlockInnerBlock,
    subtractBlocks,
    validateIterationCount,
    validateSquareOperator,
  )
import Moonlight.LinAlg.Pure.Structured.BlockTridiagonal
  ( RowMajorBlock,
    mkSymmetricBlockTridiagonal,
    transposeRowMajorBlock,
  )
import Prelude

type PreviousBlockContext :: Type
data PreviousBlockContext
  = InitialBlockContext
  | PreviousBlockContext !(Box.Vector (U.Vector Double)) !RowMajorBlock

type BlockStepResult :: Type
data BlockStepResult = BlockStepResult
  { stepDiagonalBlock :: !RowMajorBlock,
    stepNextBlock :: !(Box.Vector (U.Vector Double)),
    stepNextCouplingBlock :: !(Maybe RowMajorBlock)
  }

blockLanczosSymmetric ::
  BlockLanczosConfig ->
  LinearOperator 'SelfAdjointOperator ->
  Box.Vector (U.Vector Double) ->
  Either MoonlightError BlockLanczosDecomposition
blockLanczosSymmetric config op seedBlock = do
  validateSquareOperator "Block Lanczos" op
  let (_, dimension) = operatorShape op
      boundedBlockSize = min dimension (blockLanczosBlockSize config)
  boundedIterations <- validateIterationCount "Block Lanczos" (blockLanczosIterations config)
  initialBlock <-
    normalizeSeedBlock
      "Block Lanczos"
      dimension
      (blockLanczosTolerance config)
      boundedBlockSize
      seedBlock
  iterateBlocks boundedIterations initialBlock initialBlock InitialBlockContext [] [] 1
  where
    iterateBlocks boundedIterations accumulatedBasis currentBlock previousContext diagonalBlocksRev couplingBlocksRev blockStepCount = do
      imageBlock <- Box.fromList <$> traverse (runOperatorU op) (Box.toList currentBlock)
      stepResult <-
        blockLanczosStep
          config
          accumulatedBasis
          currentBlock
          previousContext
          imageBlock
      let nextDiagonalBlocksRev = stepDiagonalBlock stepResult : diagonalBlocksRev
          nextCouplingBlocksRev =
            maybe couplingBlocksRev (: couplingBlocksRev) (stepNextCouplingBlock stepResult)
          nextAccumulatedBasis = accumulatedBasis <> stepNextBlock stepResult
       in if blockStepCount >= boundedIterations || Box.length accumulatedBasis >= snd (operatorShape op) || Box.null (stepNextBlock stepResult)
            then
              finalize
                accumulatedBasis
                nextDiagonalBlocksRev
                couplingBlocksRev
                blockStepCount
            else
              iterateBlocks
                boundedIterations
                nextAccumulatedBasis
                (stepNextBlock stepResult)
                (case stepNextCouplingBlock stepResult of
                   Nothing -> InitialBlockContext
                   Just couplingBlock -> PreviousBlockContext currentBlock couplingBlock)
                nextDiagonalBlocksRev
                nextCouplingBlocksRev
                (blockStepCount + 1)

    finalize basisVectors diagonalBlocksRev couplingBlocksRev blockStepCount =
      let diagonalBlocks = Box.fromList (reverse diagonalBlocksRev)
          couplingBlocks = Box.fromList (reverse couplingBlocksRev)
       in do
            projectedBlockTridiagonal <- mkSymmetricBlockTridiagonal diagonalBlocks couplingBlocks
            mkBlockLanczosDecomposition basisVectors projectedBlockTridiagonal blockStepCount

blockLanczosStep ::
  BlockLanczosConfig ->
  Box.Vector (U.Vector Double) ->
  Box.Vector (U.Vector Double) ->
  PreviousBlockContext ->
  Box.Vector (U.Vector Double) ->
  Either MoonlightError BlockStepResult
blockLanczosStep config accumulatedBasis currentBlock previousContext imageBlock = do
  (alphaRows, recurrenceResidual) <- threeTermResidual currentBlock previousContext imageBlock
  let stabilizationBasis =
        if blockLanczosReorthogonalize config
          then accumulatedBasis
          else Box.empty
  nextBlock <-
    orthonormalizeBlock
      (blockLanczosReorthogonalize config)
      (blockLanczosTolerance config)
      stabilizationBasis
      recurrenceResidual
  nextCouplingBlock <-
    if Box.null nextBlock
      then Right Nothing
      else Just <$> blockInnerBlock nextBlock recurrenceResidual
  Right
    BlockStepResult
      { stepDiagonalBlock = alphaRows,
        stepNextBlock = nextBlock,
        stepNextCouplingBlock = nextCouplingBlock
      }

threeTermResidual ::
  Box.Vector (U.Vector Double) ->
  PreviousBlockContext ->
  Box.Vector (U.Vector Double) ->
  Either MoonlightError (RowMajorBlock, Box.Vector (U.Vector Double))
threeTermResidual currentBlock previousContext imageBlock = do
  alphaRows <- selfAdjointBlockInnerBlock currentBlock imageBlock
  currentContribution <- multiplyBasisByBlock currentBlock alphaRows
  residualAfterCurrent <- subtractBlocks imageBlock currentContribution
  recurrenceResidual <-
    case previousContext of
      InitialBlockContext -> Right residualAfterCurrent
      PreviousBlockContext previousBlock couplingRows -> do
        previousContribution <- multiplyBasisByBlock previousBlock (transposeRowMajorBlock couplingRows)
        subtractBlocks residualAfterCurrent previousContribution
  Right (alphaRows, recurrenceResidual)