{-# LANGUAGE TypeFamilies #-}
module Numeric.LAPACK.Orthogonal (
leastSquares,
minimumNorm,
leastSquaresMinimumNormRCond,
pseudoInverseRCond,
determinant,
determinantAbsolute,
complement,
householder,
) where
import qualified Numeric.LAPACK.Orthogonal.Private as HH
import qualified Numeric.LAPACK.Matrix.Square.Basic as Square
import qualified Numeric.LAPACK.Matrix.Extent.Private as Extent
import qualified Numeric.LAPACK.Matrix.Basic as Basic
import Numeric.LAPACK.Matrix.Square.Basic (Square)
import Numeric.LAPACK.Matrix.Private (Full, Tall, ZeroInt, zeroInt)
import Numeric.LAPACK.Matrix (transpose, dropColumns)
import qualified Numeric.LAPACK.Matrix.Shape.Private as MatrixShape
import qualified Numeric.LAPACK.Matrix as Matrix
import qualified Numeric.LAPACK.Vector as Vector
import Numeric.LAPACK.Matrix.Shape.Private (Order(RowMajor,ColumnMajor))
import Numeric.LAPACK.Scalar (RealOf, zero, absolute)
import Numeric.LAPACK.Private
(lacgv, peekCInt,
copySubMatrix, copyToTemp, copyToColumnMajor, copyToSubColumnMajor,
withAutoWorkspaceInfo, rankMsg, errorCodeMsg, createHigherArray)
import qualified Numeric.LAPACK.FFI.Generic as LapackGen
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.Unchecked as Array
import qualified Data.Array.Comfort.Shape as Shape
import Data.Array.Comfort.Storable.Unchecked (Array(Array))
import System.IO.Unsafe (unsafePerformIO)
import Foreign.Marshal.Array (pokeArray)
import Foreign.C.Types (CInt, CChar)
import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
import Foreign.Ptr (Ptr)
import Control.Monad.Trans.Cont (ContT(ContT), evalContT)
import Control.Monad.IO.Class (liftIO)
import Data.Complex (Complex)
import Data.Tuple.HT (mapSnd)
leastSquares ::
(Extent.C vert, Extent.C horiz,
Shape.C height, Eq height, Shape.C width, Shape.C nrhs, Class.Floating a) =>
Full horiz Extent.Small height width a ->
Full vert horiz height nrhs a ->
Full vert horiz width nrhs a
leastSquares
(Array shapeA@(MatrixShape.Full orderA extentA) a)
(Array shapeB@(MatrixShape.Full orderB extentB) b) =
case Extent.fuse (Extent.generalizeWide $ Extent.transpose extentA) extentB of
Nothing -> error "leastSquares: height shapes mismatch"
Just extent ->
Array.unsafeCreate (MatrixShape.Full ColumnMajor extent) $ \xPtr -> do
let widthA = Extent.width extentA
let (height,widthB) = Extent.dimensions extentB
let (m,n) = MatrixShape.dimensions shapeA
let lda = m
let nrhs = Shape.size widthB
let ldb = Shape.size height
let ldx = Shape.size widthA
evalContT $ do
mPtr <- Call.cint m
nPtr <- Call.cint n
nrhsPtr <- Call.cint nrhs
(transPtr,aPtr) <- transposeA orderA (m*n) a
ldaPtr <- Call.leadingDim lda
bPtr <- ContT $ withForeignPtr b
ldbPtr <- Call.leadingDim ldb
let bSize = Shape.size shapeB
btmpPtr <- Call.allocaArray bSize
liftIO $ copyToColumnMajor orderB ldb nrhs bPtr btmpPtr
liftIO $ withAutoWorkspaceInfo rankMsg "gels" $
LapackGen.gels transPtr
mPtr nPtr nrhsPtr aPtr ldaPtr btmpPtr ldbPtr
liftIO $ copySubMatrix ldx nrhs ldb btmpPtr ldx xPtr
minimumNorm ::
(Extent.C vert, Extent.C horiz,
Shape.C height, Eq height, Shape.C width, Shape.C nrhs, Class.Floating a) =>
Full Extent.Small vert height width a ->
Full vert horiz height nrhs a ->
Full vert horiz width nrhs a
minimumNorm
(Array shapeA@(MatrixShape.Full orderA extentA) a)
(Array (MatrixShape.Full orderB extentB) b) =
case Extent.fuse (Extent.generalizeTall $ Extent.transpose extentA) extentB of
Nothing -> error "minimumNorm: height shapes mismatch"
Just extent ->
Array.unsafeCreate (MatrixShape.Full ColumnMajor extent) $ \xPtr -> do
let widthA = Extent.width extentA
let (height,widthB) = Extent.dimensions extentB
let (m,n) = MatrixShape.dimensions shapeA
let lda = m
let nrhs = Shape.size widthB
let ldb = Shape.size height
let ldx = Shape.size widthA
evalContT $ do
mPtr <- Call.cint m
nPtr <- Call.cint n
nrhsPtr <- Call.cint nrhs
(transPtr,aPtr) <- transposeA orderA (m*n) a
ldaPtr <- Call.leadingDim lda
bPtr <- ContT $ withForeignPtr b
ldxPtr <- Call.leadingDim ldx
liftIO $ copyToSubColumnMajor orderB ldb nrhs bPtr ldx xPtr
liftIO $ withAutoWorkspaceInfo rankMsg "gels" $
LapackGen.gels transPtr
mPtr nPtr nrhsPtr aPtr ldaPtr xPtr ldxPtr
transposeA ::
Class.Floating a =>
Order -> Int -> ForeignPtr a -> ContT r IO (Ptr CChar, Ptr a)
transposeA order size a = do
aPtr <- copyToTemp size a
trans <-
case order of
RowMajor -> do
sizePtr <- Call.cint size
incPtr <- Call.cint 1
liftIO $ lacgv sizePtr aPtr incPtr
return $ HH.invChar a
ColumnMajor -> return 'N'
transPtr <- Call.char trans
return (transPtr, aPtr)
leastSquaresMinimumNormRCond ::
(Extent.C vert, Extent.C horiz,
Shape.C height, Eq height, Shape.C width, Shape.C nrhs, Class.Floating a) =>
RealOf a ->
Full horiz vert height width a ->
Full vert horiz height nrhs a ->
(Int, Full vert horiz width nrhs a)
leastSquaresMinimumNormRCond rcond
(Array (MatrixShape.Full orderA extentA) a)
(Array (MatrixShape.Full orderB extentB) b) =
case Extent.fuse (Extent.transpose extentA) extentB of
Nothing -> error "leastSquaresMinimumNormRCond: height shapes mismatch"
Just extent ->
let widthA = Extent.width extentA
(height,widthB) = Extent.dimensions extentB
shapeX = MatrixShape.Full ColumnMajor extent
m = Shape.size height
n = Shape.size widthA
nrhs = Shape.size widthB
in if m == 0
then (0, Vector.constant shapeX zero)
else
if nrhs == 0
then
(fst $ unsafePerformIO $
case Vector.constant height zero of
Array _ b1 ->
leastSquaresMinimumNormIO rcond
(MatrixShape.general ColumnMajor widthA ())
orderA a orderB b1 m n 1,
Vector.constant shapeX zero)
else
unsafePerformIO $
leastSquaresMinimumNormIO rcond shapeX
orderA a orderB b m n nrhs
leastSquaresMinimumNormIO ::
(Shape.C sh, Class.Floating a) =>
RealOf a -> sh ->
Order -> ForeignPtr a ->
Order -> ForeignPtr a ->
Int -> Int -> Int -> IO (Int, Array sh a)
leastSquaresMinimumNormIO rcond shapeX orderA a orderB b m n nrhs =
createHigherArray shapeX m n nrhs $ \(tmpPtr,ldtmp) -> do
let aSize = m*n
let lda = m
evalContT $ do
aPtr <- ContT $ withForeignPtr a
atmpPtr <- Call.allocaArray aSize
liftIO $ copyToColumnMajor orderA m n aPtr atmpPtr
ldaPtr <- Call.leadingDim lda
ldtmpPtr <- Call.leadingDim ldtmp
bPtr <- ContT $ withForeignPtr b
liftIO $ copyToSubColumnMajor orderB m nrhs bPtr ldtmp tmpPtr
jpvtPtr <- Call.allocaArray n
liftIO $ pokeArray jpvtPtr (replicate n 0)
rankPtr <- Call.alloca
gelsy m n nrhs atmpPtr ldaPtr tmpPtr ldtmpPtr jpvtPtr rcond rankPtr
liftIO $ peekCInt rankPtr
type GELSY_ r ar a =
Int -> Int -> Int -> Ptr a -> Ptr CInt -> Ptr a -> Ptr CInt ->
Ptr CInt -> ar -> Ptr CInt -> ContT r IO ()
newtype GELSY r a = GELSY {getGELSY :: GELSY_ r (RealOf a) a}
gelsy :: (Class.Floating a) => GELSY_ r (RealOf a) a
gelsy =
getGELSY $
Class.switchFloating
(GELSY gelsyReal)
(GELSY gelsyReal)
(GELSY gelsyComplex)
(GELSY gelsyComplex)
gelsyReal :: (Class.Real a) => GELSY_ r a a
gelsyReal m n nrhs aPtr ldaPtr bPtr ldbPtr jpvtPtr rcond rankPtr = do
mPtr <- Call.cint m
nPtr <- Call.cint n
nrhsPtr <- Call.cint nrhs
rcondPtr <- Call.real rcond
liftIO $ withAutoWorkspaceInfo errorCodeMsg "gelsy" $
LapackReal.gelsy mPtr nPtr nrhsPtr
aPtr ldaPtr bPtr ldbPtr jpvtPtr rcondPtr rankPtr
gelsyComplex :: (Class.Real a) => GELSY_ r a (Complex a)
gelsyComplex m n nrhs aPtr ldaPtr bPtr ldbPtr jpvtPtr rcond rankPtr = do
mPtr <- Call.cint m
nPtr <- Call.cint n
nrhsPtr <- Call.cint nrhs
rcondPtr <- Call.real rcond
rworkPtr <- Call.allocaArray (2*n)
liftIO $
withAutoWorkspaceInfo errorCodeMsg "gelsy" $ \workPtr lworkPtr infoPtr ->
LapackComplex.gelsy mPtr nPtr nrhsPtr
aPtr ldaPtr bPtr ldbPtr jpvtPtr rcondPtr rankPtr
workPtr lworkPtr rworkPtr infoPtr
pseudoInverseRCond ::
(Extent.C vert, Extent.C horiz,
Shape.C height, Eq height, Shape.C width, Eq width, Class.Floating a) =>
RealOf a ->
Full vert horiz height width a ->
(Int, Full horiz vert width height a)
pseudoInverseRCond rcond a =
case Matrix.caseTallWide a of
Left _ ->
mapSnd transpose $
leastSquaresMinimumNormRCond rcond (transpose a) $
Square.toFull $ Square.identity $
MatrixShape.fullWidth $ Array.shape a
Right _ ->
leastSquaresMinimumNormRCond rcond a $
Square.toFull $ Square.identity $
MatrixShape.fullHeight $ Array.shape a
householder ::
(Extent.C vert, Extent.C horiz, Shape.C height, Shape.C width,
Class.Floating a) =>
Full vert horiz height width a ->
(Square height a, Full vert horiz height width a)
householder a =
let hh = HH.fromMatrix a
in (HH.extractQ hh, HH.extractR hh)
determinant :: (Shape.C sh, Class.Floating a) => Square sh a -> a
determinant = HH.determinant . HH.fromMatrix
determinantAbsolute ::
(Extent.C vert, Extent.C horiz, Shape.C height, Shape.C width,
Class.Floating a) =>
Full vert horiz height width a -> RealOf a
determinantAbsolute =
absolute .
either (HH.determinantR . HH.fromMatrix) (const zero) .
Matrix.caseTallWide
complement ::
(Shape.C height, Shape.C width, Class.Floating a) =>
Tall height width a -> Tall height ZeroInt a
complement a =
dropColumns (Shape.size $ MatrixShape.fullWidth $ Array.shape a) $
Basic.mapWidth (zeroInt . Shape.size) $ Square.toFull $
HH.extractQ $ HH.fromMatrix a