packages feed

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

{-# LANGUAGE TypeFamilies #-}
module Numeric.LAPACK.Singular (
   values,
   decompose,
   decomposeNarrow,
   decomposeSquat,
   determinantAbsolute,
   leastSquaresMinimumNormRCond,
   pseudoInverseRCond,
   RealOf,
   ) where

import qualified Numeric.LAPACK.Matrix.Shape.Private as MatrixShape
import qualified Numeric.LAPACK.Matrix.Square as Square
import qualified Numeric.LAPACK.Matrix as Matrix
import qualified Numeric.LAPACK.Vector as Vector
import Numeric.LAPACK.Matrix.Square (Square)
import Numeric.LAPACK.Matrix.Shape.Private (Order(RowMajor,ColumnMajor))
import Numeric.LAPACK.Matrix.Private (General, ZeroInt, zeroInt)
import Numeric.LAPACK.Vector (Vector)
import Numeric.LAPACK.Private
         (RealOf, withAutoWorkspace, fromReal, allocArray, allocHigherArray,
          copyToTemp, copyToColumnMajor, copyToSubColumnMajor)

import qualified Numeric.LAPACK.FFI.Complex as LapackComplex
import qualified Numeric.LAPACK.FFI.Real as LapackReal
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 System.IO.Unsafe (unsafePerformIO)

import Foreign.Marshal.Array (allocaArray)
import Foreign.Marshal.Alloc (alloca)
import Foreign.C.Types (CInt, CChar)
import Foreign.ForeignPtr (withForeignPtr)
import Foreign.Ptr (Ptr, nullPtr)
import Foreign.Storable (Storable, peek)

import Control.Monad.Trans.Cont (evalContT)
import Control.Monad.IO.Class (liftIO)
import Control.Applicative ((<$>))

import Text.Printf (printf)

import Data.Complex (Complex)


values ::
   (Shape.C height, Shape.C width, Class.Floating a) =>
   General height width a -> Vector ZeroInt (RealOf a)
values =
   getValues $
   Class.switchFloating
      (Values valuesAux) (Values valuesAux)
      (Values valuesAux) (Values valuesAux)

type Values_ height width a =
   General height width a -> Vector ZeroInt (RealOf a)

newtype Values height width a = Values {getValues :: Values_ height width a}

valuesAux ::
   (Shape.C height, Shape.C width,
    Class.Floating a, RealOf a ~ ar, Storable ar) =>
   Values_ height width a
valuesAux (Array shape@(MatrixShape.General _order height width) a) =
   Array.unsafeCreateWithSize
      (zeroInt $ min (Shape.size height) (Shape.size width)) $ \mn sPtr -> do
   let (m,n) = MatrixShape.dimensions shape
   let lda = m
   evalContT $ do
      jobuPtr <- Call.char 'N'
      jobvtPtr <- Call.char 'N'
      mPtr <- Call.cint m
      nPtr <- Call.cint n
      aPtr <- copyToTemp (m*n) a
      ldaPtr <- Call.cint lda
      let uPtr = nullPtr
      let vtPtr = nullPtr
      lduPtr <- Call.cint m
      ldvtPtr <- Call.cint n
      liftIO $
         withInfo "gesvd" $ \infoPtr ->
         gesvd jobuPtr jobvtPtr mPtr nPtr
            aPtr ldaPtr sPtr uPtr lduPtr vtPtr ldvtPtr mn infoPtr


determinantAbsolute ::
   (Shape.C height, Shape.C width, Class.Floating a) =>
   General height width a -> RealOf a
determinantAbsolute =
   getDeterminantAbsolute $
   Class.switchFloating
      (DeterminantAbsolute determinantAbsoluteAux)
      (DeterminantAbsolute determinantAbsoluteAux)
      (DeterminantAbsolute determinantAbsoluteAux)
      (DeterminantAbsolute determinantAbsoluteAux)

newtype DeterminantAbsolute f a =
   DeterminantAbsolute {
      getDeterminantAbsolute :: f a -> RealOf a
   }

determinantAbsoluteAux ::
   (Shape.C height, Shape.C width,
    Class.Floating a, RealOf a ~ ar, Class.Floating ar) =>
   General height width a -> ar
determinantAbsoluteAux = Vector.product . values


decompose ::
   (Shape.C height, Shape.C width, Class.Floating a) =>
   General height width a ->
   (Square height a, Vector ZeroInt (RealOf a), Square width a)
decompose =
   getDecompose $
   Class.switchFloating
      (Decompose decomposeAux)
      (Decompose decomposeAux)
      (Decompose decomposeAux)
      (Decompose decomposeAux)

newtype Decompose m f v g a =
   Decompose {
      getDecompose :: m a -> (f a, v (RealOf a), g a)
   }

decomposeAux ::
   (Shape.C height, Shape.C width,
    Class.Floating a, RealOf a ~ ar, Storable ar) =>
   General height width a ->
   (Square height a, Vector ZeroInt ar, Square width a)
decomposeAux (Array (MatrixShape.General order height width) a) =
   unsafePerformIO $ evalContT $ do
      (u,uPtr0) <- allocArray (MatrixShape.Square order height)
      (vt,vtPtr0) <- allocArray (MatrixShape.Square order width)
      let ((m,n),(uPtr,vtPtr)) =
            case order of
               RowMajor ->
                  ((Shape.size width, Shape.size height), (vtPtr0,uPtr0))
               ColumnMajor ->
                  ((Shape.size height, Shape.size width), (uPtr0,vtPtr0))
      let mn = min m n
      let lda = m
      jobuPtr <- Call.char 'A'
      jobvtPtr <- Call.char 'A'
      mPtr <- Call.cint m
      nPtr <- Call.cint n
      aPtr <- copyToTemp (m*n) a
      ldaPtr <- Call.cint lda
      (s,sPtr) <- allocArray (zeroInt mn)
      lduPtr <- Call.cint m
      ldvtPtr <- Call.cint n
      liftIO $
         withInfo "gesvd" $ \infoPtr ->
         gesvd jobuPtr jobvtPtr mPtr nPtr
            aPtr ldaPtr sPtr uPtr lduPtr vtPtr ldvtPtr mn infoPtr
      return (u, s, vt)


decomposeSquat ::
   (Shape.C height, Shape.C width, Class.Floating a) =>
   General height width a ->
   (Square height a, Vector height (RealOf a), General height width a)
decomposeSquat a =
   let (u,s,vt) = decomposeNarrow $ Matrix.transpose a
   in  (Square.transpose vt, s, Matrix.transpose u)

decomposeNarrow ::
   (Shape.C height, Shape.C width, Class.Floating a) =>
   General height width a ->
   (General height width a, Vector width (RealOf a), Square width a)
decomposeNarrow =
   getDecompose $
   Class.switchFloating
      (Decompose decomposeThin)
      (Decompose decomposeThin)
      (Decompose decomposeThin)
      (Decompose decomposeThin)

decomposeThin ::
   (Shape.C height, Shape.C width,
    Class.Floating a, RealOf a ~ ar, Storable ar) =>
   General height width a ->
   (General height width a, Vector width ar, Square width a)
decomposeThin (Array (MatrixShape.General order height width) a) =
   unsafePerformIO $ do
     Call.assert "Singular.decomposeThin: matrix is wider than high"
         (Shape.size height >= Shape.size width)
     evalContT $ do
      (u,uPtr0) <- allocArray (MatrixShape.General order height width)
      (vt,vtPtr0) <- allocArray (MatrixShape.Square order width)
      let ((m,n),(uPtr,vtPtr)) =
            case order of
               RowMajor ->
                  ((Shape.size width, Shape.size height), (vtPtr0,uPtr0))
               ColumnMajor ->
                  ((Shape.size height, Shape.size width), (uPtr0,vtPtr0))
      let mn = min m n
      let lda = m
      jobuPtr <- Call.char 'S'
      jobvtPtr <- Call.char 'S'
      mPtr <- Call.cint m
      nPtr <- Call.cint n
      aPtr <- copyToTemp (m*n) a
      ldaPtr <- Call.cint lda
      (s,sPtr) <- allocArray width
      lduPtr <- Call.cint m
      ldvtPtr <- Call.cint mn
      liftIO $
         withInfo "gesvd" $ \infoPtr ->
         gesvd jobuPtr jobvtPtr mPtr nPtr
            aPtr ldaPtr sPtr uPtr lduPtr vtPtr ldvtPtr mn infoPtr
      return (u, s, vt)


type GESVD_ ar a =
   Ptr CChar -> Ptr CChar -> Ptr CInt -> Ptr CInt ->
   Ptr a -> Ptr CInt -> Ptr ar ->
   Ptr a -> Ptr CInt -> Ptr a -> Ptr CInt -> Int -> Ptr CInt -> IO ()

newtype GESVD a = GESVD {getGESVD :: GESVD_ (RealOf a) a}

gesvd :: Class.Floating a => GESVD_ (RealOf a) a
gesvd =
   getGESVD $
   Class.switchFloating
      (GESVD gesvdReal)
      (GESVD gesvdReal)
      (GESVD gesvdComplex)
      (GESVD gesvdComplex)

gesvdReal :: (Class.Real a) => GESVD_ a a
gesvdReal jobuPtr jobvtPtr mPtr nPtr
      aPtr ldaPtr sPtr uPtr lduPtr vtPtr ldvtPtr _mn infoPtr =
   withAutoWorkspace $ \workPtr lworkPtr ->
   LapackReal.gesvd jobuPtr jobvtPtr
      mPtr nPtr aPtr ldaPtr sPtr uPtr lduPtr vtPtr ldvtPtr
      workPtr lworkPtr infoPtr

gesvdComplex :: (Class.Real a) => GESVD_ a (Complex a)
gesvdComplex jobuPtr jobvtPtr
      mPtr nPtr aPtr ldaPtr sPtr uPtr lduPtr vtPtr ldvtPtr mn infoPtr =
   allocaArray (5*mn) $ \rworkPtr ->
   withAutoWorkspace $ \workPtr lworkPtr ->
   LapackComplex.gesvd jobuPtr jobvtPtr
      mPtr nPtr aPtr ldaPtr sPtr uPtr lduPtr vtPtr ldvtPtr
      workPtr lworkPtr rworkPtr infoPtr


leastSquaresMinimumNormRCond ::
   (Shape.C height, Eq height, Shape.C width, Shape.C nrhs, Class.Floating a) =>
   RealOf a -> General height width a -> General height nrhs a ->
   (Int, General width nrhs a)
leastSquaresMinimumNormRCond =
   getLeastSquaresMinimumNormRCond $
   Class.switchFloating
      (LeastSquaresMinimumNormRCond leastSquaresMinimumNormRCondAux)
      (LeastSquaresMinimumNormRCond leastSquaresMinimumNormRCondAux)
      (LeastSquaresMinimumNormRCond leastSquaresMinimumNormRCondAux)
      (LeastSquaresMinimumNormRCond leastSquaresMinimumNormRCondAux)

newtype LeastSquaresMinimumNormRCond f g h a =
   LeastSquaresMinimumNormRCond {
      getLeastSquaresMinimumNormRCond ::
         RealOf a -> f a -> g a -> (Int, h a)
   }

leastSquaresMinimumNormRCondAux ::
   (Shape.C height, Eq height, Shape.C width, Shape.C nrhs,
    Class.Floating a, RealOf a ~ ar, Class.Floating ar) =>
   ar -> General height width a -> General height nrhs a ->
   (Int, General width nrhs a)
leastSquaresMinimumNormRCondAux rcond
   (Array (MatrixShape.General orderA heightA widthA) a)
   (Array (MatrixShape.General orderB heightB widthB) b) =
      unsafePerformIO $ do
   Call.assert "leastSquaresMinimumNorm: height shapes mismatch"
      (heightA == heightB)
   let shapeX = MatrixShape.General ColumnMajor widthA widthB
   let m = Shape.size heightA
   let n = Shape.size widthA
   let nrhs = Shape.size widthB
   let mn = min m n
   let aSize = m*n
   let lda = m
   evalContT $ do
      mPtr <- Call.cint m
      nPtr <- Call.cint n
      nrhsPtr <- Call.cint nrhs
      aPtr <- Call.allocaArray aSize
      liftIO $ withForeignPtr a $ \asrcPtr ->
         copyToColumnMajor orderA m n asrcPtr aPtr
      ldaPtr <- Call.cint lda
      (x,(tmpPtr,ldtmp)) <- allocHigherArray shapeX m n nrhs
      ldtmpPtr <- Call.cint ldtmp
      liftIO $ withForeignPtr b $ \bPtr ->
         copyToSubColumnMajor orderB m nrhs bPtr ldtmp tmpPtr

      sPtr <- Call.allocaArray mn
      rcondPtr <- Call.number rcond
      rankPtr <- Call.alloca
      liftIO $
         withInfo "gelss" $ \infoPtr ->
         gelss mPtr nPtr nrhsPtr aPtr ldaPtr tmpPtr ldtmpPtr sPtr rcondPtr
            rankPtr mn infoPtr

      rank <- liftIO $ fromIntegral <$> peek rankPtr
      return (rank, x)


type GELSS_ ar a =
   Ptr CInt -> Ptr CInt -> Ptr CInt ->
   Ptr a -> Ptr CInt -> Ptr a -> Ptr CInt ->
   Ptr ar -> Ptr ar -> Ptr CInt -> Int -> Ptr CInt -> IO ()

newtype GELSS a = GELSS {getGELSS :: GELSS_ (RealOf a) a}

gelss :: Class.Floating a => GELSS_ (RealOf a) a
gelss =
   getGELSS $
   Class.switchFloating
      (GELSS gelssReal)
      (GELSS gelssReal)
      (GELSS gelssComplex)
      (GELSS gelssComplex)

gelssReal :: (Class.Real a) => GELSS_ a a
gelssReal mPtr nPtr nrhsPtr aPtr ldaPtr bPtr ldbPtr sPtr rcondPtr
      rankPtr _mn infoPtr =
   withAutoWorkspace $ \workPtr lworkPtr ->
   LapackReal.gelss
      mPtr nPtr nrhsPtr aPtr ldaPtr bPtr ldbPtr sPtr rcondPtr
      rankPtr workPtr lworkPtr infoPtr

gelssComplex :: (Class.Real a) => GELSS_ a (Complex a)
gelssComplex mPtr nPtr nrhsPtr aPtr ldaPtr bPtr ldbPtr sPtr rcondPtr
      rankPtr mn infoPtr =
   allocaArray (5*mn) $ \rworkPtr ->
   withAutoWorkspace $ \workPtr lworkPtr ->
   LapackComplex.gelss
      mPtr nPtr nrhsPtr aPtr ldaPtr bPtr ldbPtr sPtr rcondPtr
      rankPtr workPtr lworkPtr rworkPtr infoPtr


pseudoInverseRCond ::
   (Shape.C height, Eq height, Shape.C width, Eq width, Class.Floating a) =>
   RealOf a -> General height width a -> (Int, General width height a)
pseudoInverseRCond =
   getPseudoInverseRCond $
   Class.switchFloating
      (PseudoInverseRCond pseudoInverseRCondAux)
      (PseudoInverseRCond pseudoInverseRCondAux)
      (PseudoInverseRCond pseudoInverseRCondAux)
      (PseudoInverseRCond pseudoInverseRCondAux)

newtype PseudoInverseRCond f g a =
   PseudoInverseRCond {
      getPseudoInverseRCond :: RealOf a -> f a -> (Int, g a)
   }

pseudoInverseRCondAux ::
   (Shape.C height, Eq height, Shape.C width, Eq width,
    Class.Floating a, RealOf a ~ ar, Class.Real ar) =>
   RealOf a -> General height width a -> (Int, General width height a)
pseudoInverseRCondAux rcond a =
   let (MatrixShape.General _ height width) = Array.shape a
   in if Shape.size height < Shape.size width
        then
          let (u,s,vt) = decomposeSquat a
              (rank,recipS) = recipSigma rcond s
          in  (rank,
               Matrix.multiply (Matrix.adjoint vt) $
               Matrix.scaleRows recipS $
               Square.toGeneral $ Square.adjoint u)
        else
          let (u,s,vt) = decomposeNarrow a
              (rank,recipS) = recipSigma rcond s
          in  (rank,
               Matrix.multiply (Square.toGeneral $ Square.adjoint vt) $
               Matrix.scaleRows recipS $ Matrix.adjoint u)

recipSigma ::
   (Shape.C sh, Class.Floating a, RealOf a ~ ar, Class.Real ar) =>
   ar -> Array sh ar -> (Int, Array sh a)
recipSigma rcond sigmas =
   case Array.toList sigmas of
      [] -> (0, Array.map fromReal sigmas)
      xs@(x:_) ->
         let smin = x * rcond
         in (length (takeWhile (>=smin) xs),
             Array.map (\s -> if s>=smin then fromReal (recip s) else 0) sigmas)


withInfo :: String -> (Ptr CInt -> IO ()) -> IO ()
withInfo name computation = alloca $ \infoPtr -> do
   computation infoPtr
   info <- fromIntegral <$> peek infoPtr
   case compare info (0::Int) of
      EQ -> return ()
      LT -> error $ printf "%s: illegal value in %d-th argument" name (-info)
      GT -> error $ printf "%s: %d superdiagonals did not converge" name info