packages feed

lapack-0.1: src/Numeric/LAPACK/Matrix.hs

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
module Numeric.LAPACK.Matrix (
   General,
   (##),
   Format,
   FormatArray,
   ZeroInt, zeroInt,
   transpose, adjoint,
   fromScalar, toScalar,
   fromList,
   identity,
   diagonal, getDiagonal,
   fromRows,    fromRowsWithSize,
   fromColumns, fromColumnsWithSize,
   singleRow,   singleColumn,
   flattenRow,  flattenColumn,
   pickRow, pickColumn,
   takeRows, takeColumns,
   dropRows, dropColumns,
   reverseRows, reverseColumns,
   fromRowMajor, toRowMajor, flatten,
   (|||),
   (===),

   rowSums, columnSums,
   scaleRows, scaleColumns,
   multiply,
   multiplyVector,

   Multiply, (<#>),
   MultiplyLeft, (<#),
   MultiplyRight, (#>),
   ) where

import qualified Numeric.LAPACK.Matrix.Shape.Private as MatrixShape
import qualified Numeric.LAPACK.Matrix.Square as Square
import qualified Numeric.LAPACK.Vector as Vector
import Numeric.LAPACK.Format (Format, FormatArray, (##))
import Numeric.LAPACK.Matrix.Shape.Private (Order(RowMajor, ColumnMajor))
import Numeric.LAPACK.Matrix.Multiply
         (Multiply((<#>)), MultiplyLeft((<#)), MultiplyRight((#>)),
          transpose, multiplyVector, multiply, multiplyVectorUnchecked)
import Numeric.LAPACK.Matrix.Private (General, ZeroInt, zeroInt)
import Numeric.LAPACK.Vector (Vector)
import Numeric.LAPACK.Private
         (zero, one, pointerSeq, copyTransposed, copySubMatrix, copyBlock)

import qualified Numeric.LAPACK.FFI.Generic as LapackGen
import qualified Numeric.BLAS.FFI.Generic as BlasGen
import qualified Numeric.Netlib.Utility as Call
import qualified Numeric.Netlib.Class as Class

import qualified Data.Array.Comfort.Storable.Internal as Array
import qualified Data.Array.Comfort.Shape as Shape
import Data.Array.Comfort.Storable.Internal (Array(Array))
import Data.Array.Comfort.Shape ((:+:)((:+:)))

import Foreign.Marshal.Array (copyArray, advancePtr, pokeArray)
import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
import Foreign.Ptr (Ptr)
import Foreign.Storable (Storable, poke, peek)

import System.IO.Unsafe (unsafePerformIO)

import Control.Monad.Trans.Cont (ContT(ContT), evalContT)
import Control.Monad.IO.Class (liftIO)

import qualified Data.NonEmpty as NonEmpty
import Data.Foldable (forM_)
import Data.Bool.HT (if')


{- |
conjugate transpose
-}
adjoint ::
   (Shape.C height, Shape.C width, Class.Floating a) =>
   General height width a -> General width height a
adjoint = transpose . Vector.conjugate


fromScalar :: (Storable a) => a -> General () () a
fromScalar = Square.toGeneral . Square.fromScalar

toScalar :: (Storable a) => General () () a -> a
toScalar (Array (MatrixShape.General _ () ()) a) =
   unsafePerformIO $ withForeignPtr a peek

fromList ::
   (Shape.C height, Shape.C width, Storable a) =>
   height -> width -> [a] -> General height width a
fromList height width =
   Array.fromList (MatrixShape.General RowMajor height width)


identity ::
   (Shape.C sh, Class.Floating a) =>
   sh -> General sh sh a
identity = Square.toGeneral . Square.identity

diagonal ::
   (Shape.C sh, Class.Floating a) =>
   Vector sh a -> General sh sh a
diagonal = Square.toGeneral . Square.diagonal

getDiagonal ::
   (Shape.C sh, Eq sh, Class.Floating a) =>
   General sh sh a -> Vector sh a
getDiagonal = Square.getDiagonal . Square.fromGeneral


singleRow :: Vector width a -> General () width a
singleRow (Array sh fptr) =
   Array (MatrixShape.General RowMajor () sh) fptr

singleColumn :: Vector width a -> General width () a
singleColumn (Array sh fptr) =
   Array (MatrixShape.General ColumnMajor sh ()) fptr

flattenRow :: General () width a -> Vector width a
flattenRow (Array (MatrixShape.General _ () sh) fptr) = Array sh fptr

flattenColumn :: General width () a -> Vector width a
flattenColumn (Array (MatrixShape.General _ sh ()) fptr) = Array sh fptr


fromRows ::
   (Shape.C width, Eq width, Storable a) =>
   NonEmpty.T [] (Vector width a) -> General ZeroInt width a
fromRows (NonEmpty.Cons row rows) =
   fromRowsWithSize (Array.shape row) (row:rows)

fromRowsWithSize ::
   (Shape.C width, Eq width, Storable a) =>
   width -> [Vector width a] -> General ZeroInt width a
fromRowsWithSize width rows =
   Array.unsafeCreate
      (MatrixShape.General RowMajor (zeroInt $ length rows) width)
      (gather width rows)

fromColumns ::
   (Shape.C height, Eq height, Storable a) =>
   NonEmpty.T [] (Vector height a) -> General height ZeroInt a
fromColumns (NonEmpty.Cons column columns) =
   fromColumnsWithSize (Array.shape column) (column:columns)

fromColumnsWithSize ::
   (Shape.C height, Eq height, Storable a) =>
   height -> [Vector height a] -> General height ZeroInt a
fromColumnsWithSize height columns =
   Array.unsafeCreate
      (MatrixShape.General ColumnMajor height (zeroInt $ length columns))
      (gather height columns)

gather ::
   (Shape.C width, Eq width, Storable a) =>
   width -> [Array width a] -> Ptr a -> IO ()
gather width rows dstPtr =
   let widthSize = Shape.size width
   in forM_ (zip (pointerSeq widthSize dstPtr) rows) $
         \(dstRowPtr, Array.Array rowWidth srcFPtr) ->
         withForeignPtr srcFPtr $ \srcPtr -> do
            Call.assert
               "Matrix.fromRows/fromColumns: non-matching vector size"
               (width == rowWidth)
            copyArray dstRowPtr srcPtr widthSize


pickRow ::
   (Shape.C height, Shape.C width, Shape.Index height ~ ix,
    Class.Floating a) =>
   General height width a -> ix -> Vector width a
pickRow (Array (MatrixShape.General order height width) x) ix =
   case order of
      RowMajor -> pickConsecutive height width x ix
      ColumnMajor -> pickScattered width height x ix

pickColumn ::
   (Shape.C height, Shape.C width, Shape.Index width ~ ix,
    Class.Floating a) =>
   General height width a -> ix -> Vector height a
pickColumn (Array (MatrixShape.General order height width) x) ix =
   case order of
      RowMajor -> pickScattered height width x ix
      ColumnMajor -> pickConsecutive width height x ix

pickConsecutive ::
   (Shape.C height, Shape.C width, Shape.Index height ~ ix,
    Class.Floating a) =>
   height -> width -> ForeignPtr a -> ix -> Vector width a
pickConsecutive height width x ix =
   Array.unsafeCreateWithSize width $ \n yPtr -> evalContT $ do
      let offset = Shape.offset height ix
      nPtr <- Call.cint n
      xPtr <- ContT $ withForeignPtr x
      incxPtr <- Call.cint 1
      incyPtr <- Call.cint 1
      liftIO $
         BlasGen.copy nPtr (advancePtr xPtr (n*offset)) incxPtr yPtr incyPtr

pickScattered ::
   (Shape.C height, Shape.C width, Shape.Index width ~ ix,
    Class.Floating a) =>
   height -> width -> ForeignPtr a -> ix -> Vector height a
pickScattered height width x ix =
   Array.unsafeCreateWithSize height $ \n yPtr -> evalContT $ do
      let offset = Shape.offset width ix
      nPtr <- Call.cint n
      xPtr <- ContT $ withForeignPtr x
      incxPtr <- Call.cint $ Shape.size width
      incyPtr <- Call.cint 1
      liftIO $
         BlasGen.copy nPtr (advancePtr xPtr offset) incxPtr yPtr incyPtr


takeRows, dropRows ::
   (Shape.C width, Class.Floating a) =>
   Int -> General ZeroInt width a -> General ZeroInt width a
takeRows k
      (Array (MatrixShape.General order (Shape.ZeroBased heightA) width) a) =
   let heightB = min k heightA
       n = Shape.size width
   in if' (k<0) (error "take: negative number") $
      Array.unsafeCreateWithSize
         (MatrixShape.General order (Shape.ZeroBased heightB) width) $
            \blockSize bPtr ->
      withForeignPtr a $ \aPtr ->
      case order of
         RowMajor -> copyBlock blockSize aPtr bPtr
         ColumnMajor -> copySubMatrix heightB n heightA aPtr heightB bPtr

dropRows k0
      (Array (MatrixShape.General order (Shape.ZeroBased heightA) width) a) =
   let k = min k0 heightA
       heightB = heightA - k
       n = Shape.size width
   in if' (k<0) (error "take: negative number") $
      Array.unsafeCreateWithSize
         (MatrixShape.General order (Shape.ZeroBased heightB) width) $
            \blockSize bPtr ->
      withForeignPtr a $ \aPtr ->
      case order of
         RowMajor -> copyBlock blockSize (advancePtr aPtr (k*n)) bPtr
         ColumnMajor ->
            copySubMatrix heightB n heightA (advancePtr aPtr k) heightB bPtr


takeColumns, dropColumns ::
   (Shape.C height, Class.Floating a) =>
   Int -> General height ZeroInt a -> General height ZeroInt a
takeColumns k = transpose . takeRows k . transpose
dropColumns k = transpose . dropRows k . transpose


-- alternative: laswp
reverseRows ::
   (Shape.C width, Class.Floating a) =>
   General ZeroInt width a -> General ZeroInt width a
reverseRows (Array shape@(MatrixShape.General order height width) a) =
   Array.unsafeCreateWithSize shape $ \blockSize bPtr -> evalContT $ do
      let n = Shape.size height
      let m = Shape.size width
      fwdPtr <- Call.bool True
      nPtr <- Call.cint n
      mPtr <- Call.cint m
      kPtr <- Call.allocaArray n
      aPtr <- ContT $ withForeignPtr a
      liftIO $ do
         copyBlock blockSize aPtr bPtr
         pokeArray kPtr $ take n $ iterate (subtract 1) $ fromIntegral n
         case order of
            RowMajor -> LapackGen.lapmt fwdPtr mPtr nPtr bPtr mPtr kPtr
            ColumnMajor -> LapackGen.lapmr fwdPtr nPtr mPtr bPtr nPtr kPtr

reverseColumns ::
   (Shape.C height, Class.Floating a) =>
   General height ZeroInt a -> General height ZeroInt a
reverseColumns = transpose . reverseRows . transpose


fromRowMajor ::
   (Shape.C height, Shape.C width, Class.Floating a) =>
   Array (height,width) a -> General height width a
fromRowMajor (Array (height,width) x) =
   Array (MatrixShape.General RowMajor height width) x

toRowMajor ::
   (Shape.C height, Shape.C width, Class.Floating a) =>
   General height width a -> Array (height,width) a
toRowMajor (Array (MatrixShape.General order height width) x) =
   let shape = (height, width)
   in case order of
         RowMajor -> Array shape x
         ColumnMajor -> Array.unsafeCreate shape $ \yPtr -> evalContT $ do
            let n = Shape.size width
            let m = Shape.size height
            nPtr <- Call.cint n
            xPtr <- ContT $ withForeignPtr x
            incxPtr <- Call.cint m
            incyPtr <- Call.cint 1
            liftIO $ sequence_ $ take m $
               zipWith
                  (\xkPtr ykPtr ->
                     BlasGen.copy nPtr xkPtr incxPtr ykPtr incyPtr)
                  (pointerSeq 1 xPtr)
                  (pointerSeq n yPtr)

flatten ::
   (Shape.C height, Shape.C width, Class.Floating a) =>
   General height width a -> Vector ZeroInt a
flatten x =
   case toRowMajor x of
      Array shape fptr -> Array (zeroInt $ Shape.size shape) fptr


infixl 3 |||
infixl 2 ===

(|||) ::
   (Shape.C height, Eq height, Shape.C widtha, Shape.C widthb,
    Class.Floating a) =>
   General height widtha a ->
   General height widthb a ->
   General height (widtha:+:widthb) a
(|||)
      (Array (MatrixShape.General orderA heightA widthA) a)
      (Array (MatrixShape.General orderB heightB widthB) b) =
   if heightA /= heightB
      then error "(|||): mismatching heights"
      else
         case (orderA,orderB) of
            (RowMajor,RowMajor) ->
               Array.unsafeCreate
                  (MatrixShape.General RowMajor heightA (widthA:+:widthB)) $
               \cPtr -> evalContT $ do
                  let n = Shape.size heightA
                  let ma = Shape.size widthA
                  let mb = Shape.size widthB
                  let m = ma+mb
                  maPtr <- Call.cint ma
                  mbPtr <- Call.cint mb
                  aPtr <- ContT $ withForeignPtr a
                  bPtr <- ContT $ withForeignPtr b
                  incxPtr <- Call.cint 1
                  incyPtr <- Call.cint 1
                  liftIO $
                     sequence_ $ take n $
                     zipWith3
                        (\akPtr bkPtr ckPtr -> do
                           BlasGen.copy maPtr akPtr incxPtr ckPtr incyPtr
                           BlasGen.copy mbPtr bkPtr incxPtr
                              (ckPtr `advancePtr` ma) incyPtr)
                        (pointerSeq ma aPtr)
                        (pointerSeq mb bPtr)
                        (pointerSeq m cPtr)
            (RowMajor,ColumnMajor) ->
               Array.unsafeCreate
                  (MatrixShape.General ColumnMajor heightA (widthA:+:widthB)) $
               \cPtr -> evalContT $ do
                  let n = Shape.size heightA
                  let ma = Shape.size widthA
                  let mb = Shape.size widthB
                  aPtr <- ContT $ withForeignPtr a
                  bPtr <- ContT $ withForeignPtr b
                  liftIO $ do
                     copyTransposed n ma aPtr n cPtr
                     copyBlock (n*mb) bPtr (advancePtr cPtr (n*ma))
            (ColumnMajor,RowMajor) ->
               Array.unsafeCreate
                  (MatrixShape.General ColumnMajor heightA (widthA:+:widthB)) $
               \cPtr -> evalContT $ do
                  let n = Shape.size heightA
                  let ma = Shape.size widthA
                  let mb = Shape.size widthB
                  let volA = n*ma
                  aPtr <- ContT $ withForeignPtr a
                  bPtr <- ContT $ withForeignPtr b
                  liftIO $ do
                     copyBlock volA aPtr cPtr
                     copyTransposed n mb bPtr n (advancePtr cPtr volA)
            (ColumnMajor,ColumnMajor) ->
               Array.unsafeCreate
                  (MatrixShape.General ColumnMajor heightA (widthA:+:widthB)) $
               \cPtr -> evalContT $ do
                  let n = Shape.size heightA
                  let na = n * Shape.size widthA
                  let nb = n * Shape.size widthB
                  naPtr <- Call.cint na
                  nbPtr <- Call.cint nb
                  aPtr <- ContT $ withForeignPtr a
                  bPtr <- ContT $ withForeignPtr b
                  incxPtr <- Call.cint 1
                  incyPtr <- Call.cint 1
                  liftIO $ do
                     BlasGen.copy naPtr aPtr incxPtr cPtr incyPtr
                     BlasGen.copy nbPtr bPtr incxPtr
                        (cPtr `advancePtr` na) incyPtr

(===) ::
   (Shape.C width, Eq width, Shape.C heighta, Shape.C heightb,
    Class.Floating a) =>
   General heighta width a ->
   General heightb width a ->
   General (heighta:+:heightb) width a
(===) a b = transpose (transpose a ||| transpose b)


rowSums ::
   (Shape.C height, Shape.C width, Class.Floating a) =>
   General height width a -> Vector height a
rowSums m =
   let MatrixShape.General _ _ width = Array.shape m
   in  multiplyVectorUnchecked m (Vector.constant width one)

columnSums ::
   (Shape.C height, Shape.C width, Class.Floating a) =>
   General height width a -> Vector width a
columnSums m =
   let MatrixShape.General _ height _ = Array.shape m
   in  multiplyVectorUnchecked (transpose m) (Vector.constant height one)


scaleRows ::
   (Shape.C height, Eq height, Shape.C width, Class.Floating a) =>
   Vector height a -> General height width a -> General height width a
scaleRows
   (Array heightX x) (Array shape@(MatrixShape.General order height width) a) =
      Array.unsafeCreate shape $ \bPtr -> do
   Call.assert "scaleRows: sizes mismatch" (heightX == height)
   case order of
      RowMajor -> evalContT $ do
         let m = Shape.size height
         let n = Shape.size width
         alphaPtr <- Call.alloca
         nPtr <- Call.cint n
         xPtr <- ContT $ withForeignPtr x
         aPtr <- ContT $ withForeignPtr a
         incaPtr <- Call.cint 1
         incbPtr <- Call.cint 1
         liftIO $ sequence_ $ take m $
            zipWith3
               (\xkPtr akPtr bkPtr -> do
                  poke alphaPtr =<< peek xkPtr
                  BlasGen.copy nPtr akPtr incaPtr bkPtr incbPtr
                  BlasGen.scal nPtr alphaPtr bkPtr incbPtr)
               (pointerSeq 1 xPtr)
               (pointerSeq n aPtr)
               (pointerSeq n bPtr)
      ColumnMajor -> evalContT $ do
         let m = Shape.size width
         let n = Shape.size height
         transPtr <- Call.char 'N'
         nPtr <- Call.cint n
         klPtr <- Call.cint 0
         kuPtr <- Call.cint 0
         alphaPtr <- Call.number one
         xPtr <- ContT $ withForeignPtr x
         ldxPtr <- Call.cint 1
         aPtr <- ContT $ withForeignPtr a
         incaPtr <- Call.cint 1
         betaPtr <- Call.number zero
         incbPtr <- Call.cint 1
         liftIO $ sequence_ $ take m $
            zipWith
               (\akPtr bkPtr ->
                  BlasGen.gbmv transPtr
                     nPtr nPtr klPtr kuPtr alphaPtr xPtr ldxPtr
                     akPtr incaPtr betaPtr bkPtr incbPtr)
               (pointerSeq n aPtr)
               (pointerSeq n bPtr)

scaleColumns ::
   (Shape.C height, Shape.C width, Eq width, Class.Floating a) =>
   Vector width a -> General height width a -> General height width a
scaleColumns x = transpose . scaleRows x . transpose