moonlight-linalg-0.1.0.0: src-carrier/Moonlight/LinAlg/Pure/Dense/Flat.hs
{-# LANGUAGE StrictData #-}
-- | Flat row-major dense storage for hot Double kernels.
--
-- Nested lists remain the validation and authoring surface; this module owns
-- contiguous row-major execution.
module Moonlight.LinAlg.Pure.Dense.Flat
( DenseDoubleMatrix,
mkDenseDoubleMatrixRowMajor,
mkDenseDoubleMatrixRows,
trustedDenseDoubleMatrixRowMajor,
denseDoubleMatrixShape,
denseDoubleMatrixToRowMajorVector,
denseDoubleMatrixToRows,
denseDoubleMatrixVectorProduct,
)
where
import Data.Bifunctor (first)
import Data.Kind (Type)
import Data.Vector.Storable qualified as S
import Moonlight.Core
( MoonlightError (..),
checkedNonNegativeProduct,
fieldValueValid,
)
import Moonlight.LinAlg.Pure.Dense.Rows
( denseRowsShape,
denseRowsToLists,
mkDenseRows,
)
import Prelude
type DenseDoubleMatrix :: Type
data DenseDoubleMatrix = DenseDoubleMatrix
{ denseDoubleMatrixRowCount :: !Int,
denseDoubleMatrixColumnCount :: !Int,
denseDoubleMatrixPayload :: !(S.Vector Double)
}
deriving stock (Eq, Show)
mkDenseDoubleMatrixRowMajor :: Int -> Int -> S.Vector Double -> Either MoonlightError DenseDoubleMatrix
mkDenseDoubleMatrixRowMajor rowCount columnCount rowMajorValues
| rowCount < 0 || columnCount < 0 =
Left (InvariantViolation "dense Double matrix dimensions must be non-negative")
| otherwise = do
expectedLength <-
first
(const (InvariantViolation "dense Double matrix dimensions exceed Int cardinality"))
(checkedNonNegativeProduct rowCount columnCount)
if S.length rowMajorValues /= expectedLength
then
Left
( InvariantViolation
( "dense Double row-major payload length mismatch: expected "
<> show expectedLength
<> " values but received "
<> show (S.length rowMajorValues)
)
)
else
if S.any (not . fieldValueValid) rowMajorValues
then Left (InvariantViolation "dense Double row-major payload requires finite entries")
else
Right
( trustedDenseDoubleMatrixRowMajor
rowCount
columnCount
rowMajorValues
)
trustedDenseDoubleMatrixRowMajor :: Int -> Int -> S.Vector Double -> DenseDoubleMatrix
trustedDenseDoubleMatrixRowMajor rowCount columnCount rowMajorValues =
DenseDoubleMatrix
{ denseDoubleMatrixRowCount = rowCount,
denseDoubleMatrixColumnCount = columnCount,
denseDoubleMatrixPayload = rowMajorValues
}
mkDenseDoubleMatrixRows :: [[Double]] -> Either MoonlightError DenseDoubleMatrix
mkDenseDoubleMatrixRows rowValues = do
denseRowsValue <- mkDenseRows rowValues
let (rowCount, columnCount) = denseRowsShape denseRowsValue
mkDenseDoubleMatrixRowMajor
rowCount
columnCount
(S.fromList (concat (denseRowsToLists denseRowsValue)))
denseDoubleMatrixShape :: DenseDoubleMatrix -> (Int, Int)
denseDoubleMatrixShape matrixValue =
(denseDoubleMatrixRowCount matrixValue, denseDoubleMatrixColumnCount matrixValue)
denseDoubleMatrixToRowMajorVector :: DenseDoubleMatrix -> S.Vector Double
denseDoubleMatrixToRowMajorVector = denseDoubleMatrixPayload
denseDoubleMatrixToRows :: DenseDoubleMatrix -> [[Double]]
denseDoubleMatrixToRows matrixValue =
fmap rowValues [0 .. denseDoubleMatrixRowCount matrixValue - 1]
where
columnCount = denseDoubleMatrixColumnCount matrixValue
payload = denseDoubleMatrixPayload matrixValue
rowValues rowIndex =
S.toList (S.slice (rowIndex * columnCount) columnCount payload)
denseDoubleMatrixVectorProduct :: DenseDoubleMatrix -> S.Vector Double -> Either MoonlightError (S.Vector Double)
denseDoubleMatrixVectorProduct matrixValue vectorValue =
if S.length vectorValue /= denseDoubleMatrixColumnCount matrixValue
then
Left
( InvariantViolation
( "dense Double matrix/vector shape mismatch (matrix="
<> show (denseDoubleMatrixShape matrixValue)
<> ", vector="
<> show (S.length vectorValue)
<> ")"
)
)
else
Right
( S.generate
(denseDoubleMatrixRowCount matrixValue)
(denseDoubleMatrixRowDot matrixValue vectorValue)
)
{-# INLINE denseDoubleMatrixVectorProduct #-}
denseDoubleMatrixRowDot :: DenseDoubleMatrix -> S.Vector Double -> Int -> Double
denseDoubleMatrixRowDot matrixValue vectorValue rowIndex =
S.ifoldl' accumulateEntry 0.0 vectorValue
where
columnCount = denseDoubleMatrixColumnCount matrixValue
rowOffset = rowIndex * columnCount
payload = denseDoubleMatrixPayload matrixValue
accumulateEntry accumulator columnIndex vectorEntry =
accumulator
+ (payload `S.unsafeIndex` (rowOffset + columnIndex))
* vectorEntry
{-# INLINE denseDoubleMatrixRowDot #-}