packages feed

sparse-linear-algebra 0.2.1.1 → 0.2.2.0

raw patch · 10 files changed

+491/−339 lines, 10 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Data.Sparse.IntMap2.IntMap2: countSubdiagonalNZ :: IntMap (IntMap a) -> Int
- Data.Sparse.IntMap2.IntMap2: filterSubdiag :: IntMap (IntMap a) -> IntMap (IntMap a)
- Data.Sparse.IntMap2.IntMap2: foldlIM2 :: (a -> b -> b) -> b -> IntMap (IntMap a) -> b
- Data.Sparse.IntMap2.IntMap2: fromListIM2 :: Foldable t => t (Key, Key, a) -> IntMap (IntMap a) -> IntMap (IntMap a)
- Data.Sparse.IntMap2.IntMap2: ifilterIM2 :: (Key -> Key -> a -> Bool) -> IntMap (IntMap a) -> IntMap (IntMap a)
- Data.Sparse.IntMap2.IntMap2: ifoldlIM2 :: (Key -> Key -> t -> IntMap a -> IntMap a) -> IntMap (IntMap t) -> IntMap a
- Data.Sparse.IntMap2.IntMap2: ifoldlIM2' :: (Key -> Key -> a -> b -> b) -> b -> IntMap (IntMap a) -> b
- Data.Sparse.IntMap2.IntMap2: imapIM2 :: (Key -> Key -> a -> b) -> IntMap (IntMap a) -> IntMap (IntMap b)
- Data.Sparse.IntMap2.IntMap2: insertIM2 :: Key -> Key -> a -> IntMap (IntMap a) -> IntMap (IntMap a)
- Data.Sparse.IntMap2.IntMap2: instance Numeric.LinearAlgebra.Class.Additive Data.IntMap.Base.IntMap
- Data.Sparse.IntMap2.IntMap2: instance Numeric.LinearAlgebra.Class.Hilbert Data.IntMap.Base.IntMap
- Data.Sparse.IntMap2.IntMap2: instance Numeric.LinearAlgebra.Class.Normed Data.IntMap.Base.IntMap
- Data.Sparse.IntMap2.IntMap2: instance Numeric.LinearAlgebra.Class.Set Data.IntMap.Base.IntMap
- Data.Sparse.IntMap2.IntMap2: instance Numeric.LinearAlgebra.Class.VectorSpace Data.IntMap.Base.IntMap
- Data.Sparse.IntMap2.IntMap2: lookupIM2 :: Key -> Key -> IntMap (IntMap a) -> Maybe a
- Data.Sparse.IntMap2.IntMap2: lookupWD_IM :: Num a => IntMap (IntMap a) -> (IxRow, IxCol) -> a
- Data.Sparse.IntMap2.IntMap2: mapColumnIM2 :: (b -> b) -> IntMap (IntMap b) -> Int -> IntMap (IntMap b)
- Data.Sparse.IntMap2.IntMap2: mapIM2 :: (a -> b) -> IntMap (IntMap a) -> IntMap (IntMap b)
- Data.Sparse.IntMap2.IntMap2: mapKeysIM2 :: (Key -> Key) -> (Key -> Key) -> IntMap (IntMap a) -> IntMap (IntMap a)
- Data.Sparse.IntMap2.IntMap2: rpairs :: (a, [b]) -> [(a, b)]
- Data.Sparse.IntMap2.IntMap2: subdiagIndices :: IntMap (IntMap a) -> [(Key, Key)]
- Data.Sparse.IntMap2.IntMap2: transposeIM2 :: IntMap (IntMap a) -> IntMap (IntMap a)
- Data.Sparse.Types: type Cols = Int
- Data.Sparse.Types: type IxCol = Int
- Data.Sparse.Types: type IxRow = Int
- Data.Sparse.Types: type Rows = Int
- Data.Sparse.Utils: denseIxArray :: [b] -> [(Int, b)]
- Data.Sparse.Utils: denseIxArray2 :: Int -> [c] -> [(Int, Int, c)]
- Data.Sparse.Utils: foldlStrict :: (a -> b -> a) -> a -> [b] -> a
- Data.Sparse.Utils: foldrMap :: (Foldable t, Functor t) => (a -> b) -> (b -> c -> c) -> c -> t a -> c
- Data.Sparse.Utils: harness :: (t -> Bool) -> (t -> a) -> t -> Maybe a
- Data.Sparse.Utils: head' :: Vector a -> Maybe a
- Data.Sparse.Utils: ifoldr :: Num i => (a -> b -> b) -> b -> (i -> c -> d -> a) -> c -> [d] -> b
- Data.Sparse.Utils: inBounds :: LB -> UB -> Int -> Bool
- Data.Sparse.Utils: inBounds0 :: UB -> Int -> Bool
- Data.Sparse.Utils: inBounds02 :: (UB, UB) -> (Int, Int) -> Bool
- Data.Sparse.Utils: inBounds2 :: (LB, UB) -> (Int, Int) -> Bool
- Data.Sparse.Utils: maxTup :: Ord t => (t, t) -> (t, t) -> (t, t)
- Data.Sparse.Utils: minTup :: Ord t => (t, t) -> (t, t) -> (t, t)
- Data.Sparse.Utils: tail' :: Vector a -> Maybe (Vector a)
- Data.Sparse.Utils: type LB = Int
- Data.Sparse.Utils: type UB = Int
- Numeric.Eps: class Num a => Epsilon a
- Numeric.Eps: instance Numeric.Eps.Epsilon Foreign.C.Types.CDouble
- Numeric.Eps: instance Numeric.Eps.Epsilon Foreign.C.Types.CFloat
- Numeric.Eps: instance Numeric.Eps.Epsilon GHC.Types.Double
- Numeric.Eps: instance Numeric.Eps.Epsilon GHC.Types.Float
- Numeric.Eps: isNz :: Epsilon a => a -> Bool
- Numeric.Eps: nearZero :: Epsilon a => a -> Bool
- Numeric.Eps: roundOne :: Epsilon a => a -> a
- Numeric.Eps: roundZero :: Epsilon a => a -> a
- Numeric.Eps: roundZeroOne :: Epsilon a => a -> a
- Numeric.LinearAlgebra.Sparse: _x :: CGS -> SpVector Double
- Numeric.LinearAlgebra.Sparse: _xBcg :: BCG -> SpVector Double
- Numeric.LinearAlgebra.Sparse: _xBicgstab :: BICGSTAB -> SpVector Double
- Numeric.LinearAlgebra.Sparse: _xCgne :: CGNE -> SpVector Double
- Numeric.LinearAlgebra.Sparse: _xTfq :: TFQMR -> SpVector Double
- Numeric.LinearAlgebra.Sparse: bcg :: SpMatrix Double -> SpVector Double -> SpVector Double -> BCG
- Numeric.LinearAlgebra.Sparse: bicgstab :: SpMatrix Double -> SpVector Double -> SpVector Double -> SpVector Double -> BICGSTAB
- Numeric.LinearAlgebra.Sparse: bicgstabStep :: SpMatrix Double -> SpVector Double -> BICGSTAB -> BICGSTAB
- Numeric.LinearAlgebra.Sparse: cgne :: SpMatrix Double -> SpVector Double -> SpVector Double -> CGNE
- Numeric.LinearAlgebra.Sparse: cgs :: SpMatrix Double -> SpVector Double -> SpVector Double -> SpVector Double -> CGS
- Numeric.LinearAlgebra.Sparse: cgsStep :: SpMatrix Double -> SpVector Double -> CGS -> CGS
- Numeric.LinearAlgebra.Sparse: data BCG
- Numeric.LinearAlgebra.Sparse: data BICGSTAB
- Numeric.LinearAlgebra.Sparse: data CGNE
- Numeric.LinearAlgebra.Sparse: data CGS
- Numeric.LinearAlgebra.Sparse: data TFQMR
- Numeric.LinearAlgebra.Sparse: tfqmr :: SpMatrix Double -> SpVector Double -> SpVector Double -> TFQMR
+ Data.Sparse.SpMatrix: takeCols :: IxCol -> SpMatrix a -> SpMatrix a
+ Data.Sparse.SpMatrix: takeRows :: IxRow -> SpMatrix a -> SpMatrix a
+ Data.Sparse.SpVector: ei :: Num a => Int -> Key -> SpVector a
+ Numeric.LinearAlgebra.Sparse: BCG_ :: LinSolveMethod
+ Numeric.LinearAlgebra.Sparse: BICGSTAB_ :: LinSolveMethod
+ Numeric.LinearAlgebra.Sparse: CGNE_ :: LinSolveMethod
+ Numeric.LinearAlgebra.Sparse: CGS_ :: LinSolveMethod
+ Numeric.LinearAlgebra.Sparse: GMRES_ :: LinSolveMethod
+ Numeric.LinearAlgebra.Sparse: TFQMR_ :: LinSolveMethod
+ Numeric.LinearAlgebra.Sparse: instance GHC.Show.Show Numeric.LinearAlgebra.Sparse.CGNE
+ Numeric.LinearAlgebra.Sparse: instance GHC.Show.Show Numeric.LinearAlgebra.Sparse.TFQMR
+ Numeric.LinearAlgebra.Sparse: pinv :: SpMatrix Double -> SpVector Double -> SpVector Double
- Numeric.LinearAlgebra.Sparse: arnoldi :: (Floating a, Eq a) => SpMatrix a -> Int -> (SpMatrix a, SpMatrix a)
+ Numeric.LinearAlgebra.Sparse: arnoldi :: (Epsilon a, Floating a, Eq a) => SpMatrix a -> SpVector a -> Int -> (SpMatrix a, SpMatrix a)
- Numeric.LinearAlgebra.Sparse: qr :: (Epsilon a, Floating a, Real a) => SpMatrix a -> (SpMatrix a, SpMatrix a)
+ Numeric.LinearAlgebra.Sparse: qr :: (Epsilon a, Ord a, Floating a) => SpMatrix a -> (SpMatrix a, SpMatrix a)

Files

README.md view
@@ -8,16 +8,18 @@  Contents : -* Iterative linear solvers+* Iterative linear solvers (`linSolve`) -    * BiConjugate Gradient (`bcg`)+    * Generalized Minimal Residual (GMRES) (non-Hermitian systems)  -    * Conjugate Gradient Squared (`cgs`)+    * BiConjugate Gradient (BCG) -    * BiConjugate Gradient Stabilized (`bicgstab`) (non-Hermitian systems)+    * Conjugate Gradient Squared (CGS) -    * Transpose-Free Quasi-Minimal Residual (`tfqmr`)+    * BiConjugate Gradient Stabilized (BiCGSTAB) (non-Hermitian systems) +    * Transpose-Free Quasi-Minimal Residual (TFQMR)+ * Direct linear solvers      * LU-based (`luSolve`)@@ -64,7 +66,11 @@     fromListSV :: Int -> [(Int, a)] -> SpVector a  can be used to create sparse vectors.+Alternatively, the user can copy the contents of a list to a (dense) SpVector using +    fromListDenseSV :: Int -> [a] -> SpVector a++ ### Displaying sparse data  Both sparse vectors and matrices can be pretty-printed using `prd`:@@ -80,7 +86,7 @@  ### Matrix operations -Matrix factorizations are available as `lu` and `qr` respectively, and are straightforward to verify by using the matrix product `##`  :+There are a few common matrix factorizations available; in the following example we compute the LU factorization of a matrix and verify it with the matrix-matrix product `##`  :      > (l, u) = lu amat     > prd $ l ## u@@ -100,12 +106,33 @@     [4.0,3.0,2.0]     [0.0,0.0,5.0] +A matrix is transposed using `transposeSM`. +Sometimes we need to compute matrix-matrix transpose products, which is why the library offers the infix operators `#^#` (M^T N) and `##^` (M N^T):++    > amat' = amat #^# amat+    > prd amat'+    ( 3 rows, 3 columns ) , 9 NZ ( sparsity 1.0 )++    [20.0,12.0,8.0]+    [12.0,9.0,6.0]+    [8.0,6.0,29.0]++    > l = chol amat'+    > prd $ l ##^ l+    ( 3 rows, 3 columns ) , 9 NZ ( sparsity 1.0 )++    [20.000000000000004,12.0,8.0]+    [12.0,9.0,10.8]+    [8.0,10.8,29.0]++In the above example we have also shown the Cholesky decomposition (M = L L^T where L is a lower-triangular matrix), which is only possible for symmetric positive-definite matrices.+ ### Linear systems -Large sparse linear systems are best solved with iterative methods. `sparse-linear-algebra` provides a selection of these via the `linSolve` function, or alternatively `<\>` (which uses BiCGSTAB as default) :+Large sparse linear systems are best solved with iterative methods. `sparse-linear-algebra` provides a selection of these via the `linSolve` function, or alternatively `<\>` (which uses GMRES as default solver method) : -    > b = fromListSV 3 [(0,3),(1,2),(2,5)]+    > b = fromListDenseSV 3 [3,2,5]     > x = amat <\> b     > prd x     ( 3 elements ) ,  3 NZ ( sparsity 1.0 )@@ -140,7 +167,7 @@  * define appropriate data structures, and how they relate to those properties (sparse vectors and matrices, defined internally via `Data.IntMap`, are made instances of the VectorSpace and Additive classes respectively). This allows to decouple the algorithms from the actual implementation of the backend, -* implement the algorithms, following 1:1 the textbook [1] +* implement the algorithms, following 1:1 the textbook [1, 2]    ## License@@ -157,3 +184,5 @@ ## References  [1] : Y. Saad, Iterative Methods for Sparse Linear Systems, 2nd ed., 2000++[2] : L. N. Trefethen, D. Bau, Numerical Linear Algebra, SIAM, 1997
sparse-linear-algebra.cabal view
@@ -1,7 +1,17 @@ name:                sparse-linear-algebra-version:             0.2.1.1+version:             0.2.2.0 synopsis:            Numerical computation in native Haskell -description:         Currently the library provides iterative linear solvers, matrix decompositions, eigenvalue computations and related utilities. Please see README.md for details+description:+  /Overview/+  .+  The @sparse-linear-algebra@ library provides iterative linear solvers, matrix decompositions, eigenvalue computations and related utilities. The user interface is provided by the top-level module "Numeric.LinearAlgebra.Sparse":+  .+  @+  import           Numeric.LinearAlgebra.Sparse+  @+  .+  Please refer to the README file for usage examples.+  . homepage:            https://github.com/ocramz/sparse-linear-algebra license:             GPL-3 license-file:        LICENSE@@ -20,10 +30,11 @@   hs-source-dirs:      src   exposed-modules:     Numeric.LinearAlgebra.Sparse                        Numeric.LinearAlgebra.Class-                       Data.Sparse.IntMap2.IntMap2                        Data.Sparse.SpVector                        Data.Sparse.SpMatrix                        Data.Sparse.Common+  other-modules:       Data.Sparse.Internal.IntMap2+                       Data.Sparse.Internal.CSR                        Data.Sparse.Utils                        Data.Sparse.Types                        Numeric.Eps
src/Data/Sparse/Common.hs view
@@ -24,7 +24,7 @@  import Data.Sparse.Utils as X import Data.Sparse.Types as X-import Data.Sparse.IntMap2.IntMap2 -- as X+import Data.Sparse.Internal.IntMap2 -- as X import Data.Sparse.SpMatrix as X import Data.Sparse.SpVector as X 
− src/Data/Sparse/IntMap2/IntMap2.hs
@@ -1,194 +0,0 @@-module Data.Sparse.IntMap2.IntMap2 where--import Numeric.LinearAlgebra.Class--import qualified Data.IntMap.Strict as IM-import Data.Sparse.Types--import Data.Maybe----instance Set IM.IntMap where-  liftU2 = IM.unionWith-  {-# INLINE liftU2 #-}-  liftI2 = IM.intersectionWith-  {-# INLINE liftI2 #-}--instance Additive IM.IntMap where-  zero = IM.empty-  {-# INLINE zero #-}-  (^+^) = liftU2 (+)-  {-# INLINE (^+^) #-}---instance VectorSpace IM.IntMap where-  n .* im = IM.map (* n) im-  -instance Hilbert IM.IntMap where-   a `dot` b = sum $ liftI2 (*) a b-              --instance Normed IM.IntMap where-  norm p v | p==1 = norm1 v-           | p==2 = norm2 v-           | otherwise = normP p v------ set-like brackets---------- * Insertion---- | Insert an element-insertIM2 ::-  IM.Key -> IM.Key -> a -> IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap a)-insertIM2 i j x imm = IM.insert i ro imm where-  ro = maybe (IM.singleton j x) (IM.insert j x) (IM.lookup i imm)-{-# inline insertIM2 #-}  ---- * Lookup---- |Lookup a key-lookupIM2 ::-  IM.Key -> IM.Key -> IM.IntMap (IM.IntMap a) -> Maybe a-lookupIM2 i j imm = IM.lookup i imm >>= IM.lookup j-{-# inline lookupIM2 #-}  ---- | Lookup with default 0-lookupWD_IM :: Num a => IM.IntMap (IM.IntMap a) -> (IxRow, IxCol) -> a-lookupWD_IM im (i,j) = fromMaybe 0 (IM.lookup i im >>= IM.lookup j)------- |Populate an IM2 from a list of (row index, column index, value)  -fromListIM2 ::-  Foldable t =>-     t (IM.Key, IM.Key, a) -> IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap a)-fromListIM2 iix sm = foldl ins sm iix where-  ins t (i,j,x) = insertIM2 i j x t----- * Folding---- |Indexed left fold over an IM2, with general accumulator-ifoldlIM2' :: (IM.Key -> IM.Key -> a -> b -> b) -> b -> IM.IntMap (IM.IntMap a) -> b-ifoldlIM2' f empty mm = IM.foldlWithKey' accRow empty mm where-  accRow acc i r = IM.foldlWithKey' (accElem i) acc r-  accElem i acc j x = f i j x acc-{-# inline ifoldlIM2' #-}---- |Indexed left fold over an IM2-ifoldlIM2 ::-  (IM.Key -> IM.Key -> t -> IM.IntMap a -> IM.IntMap a) ->-  IM.IntMap (IM.IntMap t) ->  -  IM.IntMap a-ifoldlIM2 f m         = IM.foldlWithKey' accRow IM.empty m where-  accRow    acc i row = IM.foldlWithKey' (accElem i) acc row-  accElem i acc j x   = f i j x acc-{-# inline ifoldlIM2 #-}  ---- |Left fold over an IM2, with general accumulator-foldlIM2 :: (a -> b -> b) -> b -> IM.IntMap (IM.IntMap a) -> b-foldlIM2 f empty mm = IM.foldl accRow empty mm where-  accRow acc r = IM.foldl accElem acc r-  accElem acc x = f x acc-{-# inline foldlIM2 #-}----- | Inner indices become outer ones and vice versa. No loss of information because both inner and outer IntMaps are nubbed.-transposeIM2 :: IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap a)-transposeIM2 = ifoldlIM2 (flip insertIM2)-{-# inline transposeIM2 #-}---- specialized folds---- -- extract diagonal elements--- extractDiagonalIM2 :: IM.IntMap (IM.IntMap a) -> [a]--- extractDiagonalIM2 = ifoldlIM2' (\i j x xs -> if i==j then x : xs else xs) []------- * Filtering---- |Map over outer IM and filter all inner IM's-ifilterIM2 ::-  (IM.Key -> IM.Key -> a -> Bool) ->-  IM.IntMap (IM.IntMap a) ->-  IM.IntMap (IM.IntMap a)-ifilterIM2 f  =-  IM.mapWithKey (\irow row -> IM.filterWithKey (f irow) row) -{-# inline ifilterIM2 #-}---- |Specialized filtering : keep only sub-diagonal elements-filterSubdiag :: IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap a)-filterSubdiag = ifilterIM2 (\i j _ -> i>j)--countSubdiagonalNZ :: IM.IntMap (IM.IntMap a) -> Int-countSubdiagonalNZ im =-  IM.size $ IM.filter (not . IM.null) (filterSubdiag im)---- |List of (row, col) indices of (nonzero) subdiagonal elements-subdiagIndices :: IM.IntMap (IM.IntMap a) -> [(IM.Key, IM.Key)]-subdiagIndices im = concatMap rpairs $ IM.toList (IM.map IM.keys im') where-  im' = filterSubdiag im--rpairs :: (a, [b]) -> [(a, b)]-rpairs (i, jj@(_:_)) = zip (replicate (length jj) i) jj-rpairs (_, []) = []---- -- list of (row, col) indices of elements that satisfy a criterion--- indicesThatIM2 ::---   (IM.Key -> IM.IntMap a -> Bool) -> IM.IntMap (IM.IntMap a) -> [(IM.Key, IM.Key)]--- indicesThatIM2 f im = concatMap rpairs $ IM.toList (IM.map IM.keys im') where---   im' = IM.filterWithKey f im--  ----- * Mapping---- |Map over IM2-mapIM2 :: (a -> b) -> IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap b)-mapIM2 = IM.map . IM.map   -- imapIM2 (\_ _ x -> f x)-------- |Indexed map over IM2-imapIM2 ::-  (IM.Key -> IM.Key -> a -> b) ->-  IM.IntMap (IM.IntMap a) ->-  IM.IntMap (IM.IntMap b)-imapIM2 f im = IM.mapWithKey ff im where-  ff j x = IM.mapWithKey (`f` j) x------ |Mapping keys-mapKeysIM2 ::-  (IM.Key -> IM.Key) -> (IM.Key -> IM.Key) -> IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap a)-mapKeysIM2 fi fj im = IM.map adjCols adjRows where-  adjRows = IM.mapKeys fi im-  adjCols = IM.mapKeys fj ------- map over a single `column`--mapColumnIM2 :: (b -> b) -> IM.IntMap (IM.IntMap b) -> Int -> IM.IntMap (IM.IntMap b)-mapColumnIM2 f im jj = imapIM2 (\i j x -> if j == jj then f x else x) im----
+ src/Data/Sparse/Internal/CSR.hs view
@@ -0,0 +1,34 @@+module Data.Sparse.Internal.CSR where++import qualified Data.Vector as V+import qualified Data.Vector.Unboxed as VU+import qualified Data.Vector.Unboxed.Mutable as VM++{-| Compressed Row Storage specification :++   http://netlib.org/utk/people/JackDongarra/etemplates/node373.html++   The compressed row storage (CRS) format puts the subsequent nonzeros of the matrix+   rows in contiguous memory locations. Assuming we have a nonsymmetric sparse matrix+   $A$, we create three vectors: one for floating point numbers (val) and the other+   two for integers (col_ind, row_ptr).++   The val vector stores the values of the nonzero elements of the matrix $A$ as+   they are traversed in a row-wise fashion.+ +   The col_ind vector stores the column indexes of the elements in the val vector,+   that is, if val(k)=a_{i,j}, then  col_ind(k)=j$.++   The row_ptr vector stores the locations in the val vector that start a row;+   that is, if  val(k)=a_{i,j}, then row_ptr(i) <= k < row_ptr(i+1)++-}++data CsrMatrix a =+  CsrMatrix { csrVal :: VU.Vector a,+              csrColInd :: VU.Vector Int,+              csrRowPtr :: VU.Vector Int,+              csrNnz :: {-# UNPACK #-} !Int,+              csrNrows :: {-# UNPACK #-} !Int,+              csrNcols :: {-# UNPACK #-} !Int } deriving Eq+
+ src/Data/Sparse/Internal/IntMap2.hs view
@@ -0,0 +1,194 @@+module Data.Sparse.Internal.IntMap2 where++import Numeric.LinearAlgebra.Class++import qualified Data.IntMap.Strict as IM+import Data.Sparse.Types++import Data.Maybe++++instance Set IM.IntMap where+  liftU2 = IM.unionWith+  {-# INLINE liftU2 #-}+  liftI2 = IM.intersectionWith+  {-# INLINE liftI2 #-}++instance Additive IM.IntMap where+  zero = IM.empty+  {-# INLINE zero #-}+  (^+^) = liftU2 (+)+  {-# INLINE (^+^) #-}+++instance VectorSpace IM.IntMap where+  n .* im = IM.map (* n) im+  +instance Hilbert IM.IntMap where+   a `dot` b = sum $ liftI2 (*) a b+              ++instance Normed IM.IntMap where+  norm p v | p==1 = norm1 v+           | p==2 = norm2 v+           | otherwise = normP p v++++-- set-like brackets++++++++-- * Insertion++-- | Insert an element+insertIM2 ::+  IM.Key -> IM.Key -> a -> IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap a)+insertIM2 i j x imm = IM.insert i ro imm where+  ro = maybe (IM.singleton j x) (IM.insert j x) (IM.lookup i imm)+{-# inline insertIM2 #-}  ++-- * Lookup++-- |Lookup a key+lookupIM2 ::+  IM.Key -> IM.Key -> IM.IntMap (IM.IntMap a) -> Maybe a+lookupIM2 i j imm = IM.lookup i imm >>= IM.lookup j+{-# inline lookupIM2 #-}  ++-- | Lookup with default 0+lookupWD_IM :: Num a => IM.IntMap (IM.IntMap a) -> (IxRow, IxCol) -> a+lookupWD_IM im (i,j) = fromMaybe 0 (IM.lookup i im >>= IM.lookup j)+++++-- |Populate an IM2 from a list of (row index, column index, value)  +fromListIM2 ::+  Foldable t =>+     t (IM.Key, IM.Key, a) -> IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap a)+fromListIM2 iix sm = foldl ins sm iix where+  ins t (i,j,x) = insertIM2 i j x t+++-- * Folding++-- |Indexed left fold over an IM2, with general accumulator+ifoldlIM2' :: (IM.Key -> IM.Key -> a -> b -> b) -> b -> IM.IntMap (IM.IntMap a) -> b+ifoldlIM2' f empty mm = IM.foldlWithKey' accRow empty mm where+  accRow acc i r = IM.foldlWithKey' (accElem i) acc r+  accElem i acc j x = f i j x acc+{-# inline ifoldlIM2' #-}++-- |Indexed left fold over an IM2+ifoldlIM2 ::+  (IM.Key -> IM.Key -> t -> IM.IntMap a -> IM.IntMap a) ->+  IM.IntMap (IM.IntMap t) ->  +  IM.IntMap a+ifoldlIM2 f m         = IM.foldlWithKey' accRow IM.empty m where+  accRow    acc i row = IM.foldlWithKey' (accElem i) acc row+  accElem i acc j x   = f i j x acc+{-# inline ifoldlIM2 #-}  ++-- |Left fold over an IM2, with general accumulator+foldlIM2 :: (a -> b -> b) -> b -> IM.IntMap (IM.IntMap a) -> b+foldlIM2 f empty mm = IM.foldl accRow empty mm where+  accRow acc r = IM.foldl accElem acc r+  accElem acc x = f x acc+{-# inline foldlIM2 #-}+++-- | Inner indices become outer ones and vice versa. No loss of information because both inner and outer IntMaps are nubbed.+transposeIM2 :: IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap a)+transposeIM2 = ifoldlIM2 (flip insertIM2)+{-# inline transposeIM2 #-}++-- specialized folds++-- -- extract diagonal elements+-- extractDiagonalIM2 :: IM.IntMap (IM.IntMap a) -> [a]+-- extractDiagonalIM2 = ifoldlIM2' (\i j x xs -> if i==j then x : xs else xs) []+++++-- * Filtering++-- |Map over outer IM and filter all inner IM's+ifilterIM2 ::+  (IM.Key -> IM.Key -> a -> Bool) ->+  IM.IntMap (IM.IntMap a) ->+  IM.IntMap (IM.IntMap a)+ifilterIM2 f  =+  IM.mapWithKey (\irow row -> IM.filterWithKey (f irow) row) +{-# inline ifilterIM2 #-}++-- |Specialized filtering : keep only sub-diagonal elements+filterSubdiag :: IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap a)+filterSubdiag = ifilterIM2 (\i j _ -> i>j)++countSubdiagonalNZ :: IM.IntMap (IM.IntMap a) -> Int+countSubdiagonalNZ im =+  IM.size $ IM.filter (not . IM.null) (filterSubdiag im)++-- |List of (row, col) indices of (nonzero) subdiagonal elements+subdiagIndices :: IM.IntMap (IM.IntMap a) -> [(IM.Key, IM.Key)]+subdiagIndices im = concatMap rpairs $ IM.toList (IM.map IM.keys im') where+  im' = filterSubdiag im++rpairs :: (a, [b]) -> [(a, b)]+rpairs (i, jj@(_:_)) = zip (replicate (length jj) i) jj+rpairs (_, []) = []++-- -- list of (row, col) indices of elements that satisfy a criterion+-- indicesThatIM2 ::+--   (IM.Key -> IM.IntMap a -> Bool) -> IM.IntMap (IM.IntMap a) -> [(IM.Key, IM.Key)]+-- indicesThatIM2 f im = concatMap rpairs $ IM.toList (IM.map IM.keys im') where+--   im' = IM.filterWithKey f im++  +++-- * Mapping++-- |Map over IM2+mapIM2 :: (a -> b) -> IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap b)+mapIM2 = IM.map . IM.map   -- imapIM2 (\_ _ x -> f x)++++++-- |Indexed map over IM2+imapIM2 ::+  (IM.Key -> IM.Key -> a -> b) ->+  IM.IntMap (IM.IntMap a) ->+  IM.IntMap (IM.IntMap b)+imapIM2 f im = IM.mapWithKey ff im where+  ff j x = IM.mapWithKey (`f` j) x++++-- |Mapping keys+mapKeysIM2 ::+  (IM.Key -> IM.Key) -> (IM.Key -> IM.Key) -> IM.IntMap (IM.IntMap a) -> IM.IntMap (IM.IntMap a)+mapKeysIM2 fi fj im = IM.map adjCols adjRows where+  adjRows = IM.mapKeys fi im+  adjCols = IM.mapKeys fj +++++-- map over a single `column`++mapColumnIM2 :: (b -> b) -> IM.IntMap (IM.IntMap b) -> Int -> IM.IntMap (IM.IntMap b)+mapColumnIM2 f im jj = imapIM2 (\i j x -> if j == jj then f x else x) im++++
src/Data/Sparse/SpMatrix.hs view
@@ -16,7 +16,7 @@ import Numeric.Eps import Numeric.LinearAlgebra.Class -import Data.Sparse.IntMap2.IntMap2+import Data.Sparse.Internal.IntMap2  import qualified Data.IntMap as IM @@ -58,12 +58,13 @@ instance Set SpMatrix where   liftU2 f2 (SM n1 x1) (SM n2 x2) = SM (maxTup n1 n2) ((liftU2.liftU2) f2 x1 x2)   liftI2 f2 (SM n1 x1) (SM n2 x2) = SM (minTup n1 n2) ((liftI2.liftI2) f2 x1 x2)-  ++-- | 'SpMatrix'es form a ring, in that they can be added and possess a zero element   instance Additive SpMatrix where   zero = SM (0,0) IM.empty   (^+^) = liftU2 (+) -+-- | 'SpMatrix'es are maps between finite-dimensional spaces instance FiniteDim SpMatrix where   type FDSize SpMatrix = (Rows, Cols)   dim = smDim@@ -75,6 +76,7 @@ instance Sparse SpMatrix a where   spy = spySM +-- | 'SpMatrix'es are sparse containers too, i.e. any specific component may be missing (so it is assumed to be 0) instance Num a => SpContainer SpMatrix a where   type ScIx SpMatrix = (Rows, Cols)   scInsert (i,j) = insertSpMatrix i j@@ -288,6 +290,12 @@ extractSubmatrix :: SpMatrix a -> (IxRow, IxRow) -> (IxCol, IxCol) -> SpMatrix a extractSubmatrix = extractSubmatrixSM id id ++takeRows :: IxRow -> SpMatrix a -> SpMatrix a+takeRows n mm = extractSubmatrix mm (0, n-1) (0, ncols mm - 1)++takeCols :: IxCol -> SpMatrix a -> SpMatrix a+takeCols n mm = extractSubmatrix mm (0, nrows mm - 1) (0, n - 1)   
src/Data/Sparse/SpVector.hs view
@@ -12,7 +12,7 @@  import Data.Sparse.Utils import Data.Sparse.Types-import Data.Sparse.IntMap2.IntMap2+import Data.Sparse.Internal.IntMap2  import Numeric.Eps import Numeric.LinearAlgebra.Class@@ -58,11 +58,11 @@   (^+^) = liftU2 (+)  -                      +-- | 'SpVector's form a vector space because they can be multiplied by a scalar instance VectorSpace SpVector where   n .* v = scale n v -+-- | 'SpVector's are finite-dimensional vectors instance FiniteDim SpVector where   type FDSize SpVector = Int   dim = svDim  @@ -75,7 +75,7 @@   spy = spySV  -+-- | 'SpVector's are sparse containers too, i.e. any specific component may be missing (so it is assumed to be 0) instance Num a => SpContainer SpVector a where   type ScIx SpVector = Int   scInsert = insertSpVector@@ -84,14 +84,14 @@   -+-- | 'SpVector's form a Hilbert space, in that we can define an inner product over them instance Hilbert SpVector where   a `dot` b | dim a == dim b = dot (dat a) (dat b)             | otherwise =                      error $ "dot : sizes must coincide, instead we got " ++                            show (dim a, dim b) -+-- | Since 'SpVector's form a Hilbert space, we can define a norm for them  instance Normed SpVector where   norm p (SV _ v) = norm p v @@ -104,17 +104,22 @@  -- ** Creation --- | empty sparse vector (length n, no entries)+-- | Empty sparse vector (length n, no entries) zeroSV :: Int -> SpVector a zeroSV n = SV n IM.empty  --- | singleton sparse vector (length 1)+-- | Singleton sparse vector (length 1) singletonSV :: a -> SpVector a singletonSV x = SV 1 (IM.singleton 0 x)  +-- | Canonical basis vector in R^n+ei :: Num a => Int -> IM.Key -> SpVector a+ei n i = SV n (IM.insert (i - 1) 1 IM.empty) ++ -- | create a sparse vector from an association list while discarding all zero entries mkSpVector :: Epsilon a => Int -> IM.IntMap a -> SpVector a mkSpVector d im = SV d $ IM.filterWithKey (\k v -> isNz v && inBounds0 d k) im@@ -203,6 +208,10 @@ fromListSV :: Int -> [(Int, a)] -> SpVector a fromListSV d iix = SV d (IM.fromList (filter (inBounds0 d . fst) iix )) +-- fromListSV' d iix = SV d (F.foldl' insf IM.empty iix') where+--   insf im (i, x) = IM.insert i x im+--   iix' = filter (inBounds0 d . fst) iix   -- filtering forces list as only instance+  -- ** toList toListSV :: SpVector a -> [(IM.Key, a)] toListSV sv = IM.toList (dat sv)
src/Numeric/LinearAlgebra/Sparse.hs view
@@ -3,7 +3,8 @@ module Numeric.LinearAlgebra.Sparse        (          -- * Matrix factorizations-         qr, lu,+         qr,+         lu,          chol,          -- * Condition number          conditionNumberSM,@@ -12,18 +13,15 @@          -- * Givens' rotation          givens,          -- * Arnoldi iteration-         arnoldi,+         arnoldi,           -- * Eigensolvers          eigsQR, eigRayleigh,          -- * Linear solvers-         linSolve, LinSolveMethod, (<\>),+         -- ** Iterative methods+         linSolve, LinSolveMethod(..), (<\>),+         pinv,          -- ** Direct methods          luSolve,-         -- ** Iterative methods-         cgne, tfqmr, bicgstab, cgs, bcg,-         _xCgne, _xTfq, _xBicgstab, _x, _xBcg,-         cgsStep, bicgstabStep,-         CGNE, TFQMR, BICGSTAB, CGS, BCG,          -- * Preconditioners          ilu0, mSsor,          -- * Matrix partitioning@@ -49,7 +47,7 @@ import Control.Monad.Primitive import Control.Monad (mapM_, forM_, replicateM) import Control.Monad.State.Strict-import Control.Monad.Writer+-- import Control.Monad.Writer -- import Control.Monad.Trans -- import Control.Monad.Trans.State (runStateT) -- import Control.Monad.Trans.Writer (runWriterT)@@ -85,7 +83,7 @@  -- |uses the R matrix from the QR factorization conditionNumberSM :: (Epsilon a, RealFloat a) => SpMatrix a -> a-conditionNumberSM m | isInfinite kappa = error "Infinite condition number : rank-deficient system"+conditionNumberSM m | nearZero lmin = error "Infinite condition number : rank-deficient system"                     | otherwise = kappa where   kappa = lmax / lmin   (_, r) = qr m@@ -162,8 +160,8 @@ {-# inline givens #-} givens :: (Floating a, Epsilon a, Ord a) => SpMatrix a -> IxRow -> IxCol -> SpMatrix a givens mm i j -  | isValidIxSM mm (i,j) && isSquareSM mm =-       sparsifySM $ fromListSM' [(i,i,c),(j,j,c),(j,i,-s),(i,j,s)] (eye (nrows mm))+  | isValidIxSM mm (i,j) && nrows mm >= ncols mm =+       fromListSM' [(i,i,c),(j,j,c),(j,i,-s),(i,j,s)] (eye (nrows mm))   | otherwise = error "givens : indices out of bounds"         where     (c, s, _) = givensCoef a b@@ -188,40 +186,33 @@   --- * QR decomposition  --- | Given a matrix A, returns a pair of matrices (Q, R) such that Q R = A, Q is orthogonal and R is upper triangular. Applies Givens rotation iteratively to zero out sub-diagonal elements-qr :: (Epsilon a, Floating a, Real a) => SpMatrix a -> (SpMatrix a, SpMatrix a)-qr mm = (transposeSM qmatt, rmat)  where-  qmatt = F.foldl' (#~#) ee $ gmats mm -- Q^T = (G_n * G_n-1 ... * G_1)-  rmat = qmatt #~# mm                  -- R = Q^T A-  ee = eye (nrows mm)-      --- | Givens matrices in order [G1, G2, .. , G_N ]-gmats :: (Epsilon a, Real a, Floating a) => SpMatrix a -> [SpMatrix a]-gmats mm = gm mm (subdiagIndicesSM mm) where- gm m ((i,j):is) = let g = givens m i j-                   in g : gm (g #~# m) is- gm _ [] = []+-- * QR decomposition  +-- | Given a matrix A, returns a pair of matrices (Q, R) such that Q R = A, where Q is orthogonal and R is upper triangular. Applies Givens rotation iteratively to zero out sub-diagonal elements.+qr :: (Epsilon a, Ord a, Floating a) => SpMatrix a -> (SpMatrix a, SpMatrix a)+qr mm = (transposeSM qt, r) where+  (qt, r, _) = execState (modifyUntil qf stepf) gminit+  qf (_, _, iis) = null iis+  stepf (qmatt, m, iis) = (qmatt', m', tail iis) where+    (i, j) = head iis+    g = givens m i j+    qmatt' = g #~# qmatt  -- update Q'+    m' = g #~# m          -- update R+  gminit = (eye (nrows mm), mm, subdiagIndicesSM mm) +    +    --- -- | QR algorithm, state transformer version--- gmatST0 (m, (i,j):is) = (m', is) where    -- WRONG, possible access to []---   g = givens m i j                        ---   m' = g #~# m--- gmatST0 (m, []) = (eye (nrows m), []) --- gmatST m = gmatST0 (m, subdiagIndicesSM m)     - -- * Eigenvalue algorithms  -- ** QR algorithm@@ -353,7 +344,7 @@ -- ** Doolittle algorithm {- Doolittle algorithm for factoring A' = P A, where P is a permutation matrix such that A' has a nonzero as its (0, 0) entry -} --- | Given a matrix A, returns a pair of matrices (L, U) such that L U = A+-- | Given a matrix A, returns a pair of matrices (L, U) where L is lower triangular and U is upper triangular such that L U = A lu :: (Epsilon a, Fractional a, Real a) => SpMatrix a -> (SpMatrix a, SpMatrix a) lu aa = (lf, ufin) where   (ixf, lf, uf) = execState (modifyUntil q luUpd) luInit@@ -429,61 +420,48 @@   --- * Incomplete LU --- | used for Incomplete LU : remove entries in `m` corresponding to zero entries in `m2`  -ilu0 :: (Epsilon a, Real a, Fractional a) => SpMatrix a -> (SpMatrix a, SpMatrix a)-ilu0 aa = (lh, uh) where-  (l, u) = lu aa-  lh = sparsifyLU l aa-  uh = sparsifyLU u aa-  sparsifyLU m m2 = ifilterSM f m where-    f i j _ = isJust (lookupSM m2 i j)  ---- -- * Arnoldi iteration --- | Given a matrix A and a positive integer `n`, this procedure finds the basis of an order `n` Krylov subspace (as the columns of matrix Q), along with an upper Hessenberg matrix H, such that A = Q^T H Q.+-- | Given a matrix A, a vector b and a positive integer `n`, this procedure finds the basis of an order `n` Krylov subspace (as the columns of matrix Q), along with an upper Hessenberg matrix H, such that A = Q^T H Q. -- At the i`th iteration, it finds (i + 1) coefficients (the i`th column of the Hessenberg matrix H) and the (i + 1)`th Krylov vector.+ arnoldi ::-  (Floating a, Eq a) => SpMatrix a -> Int -> (SpMatrix a, SpMatrix a)-arnoldi aa kn = (fromCols qvfin, hhfin) where-  (qvfin, hhfin, _) = execState (modifyUntil tf arnoldiStep) arnInit -  tf (_, _, ii) = ii == kn -- termination criterion+  (Epsilon a, Floating a, Eq a) => SpMatrix a -> SpVector a -> Int -> (SpMatrix a, SpMatrix a)+arnoldi aa b kn = (fromCols qvfin, fromListSM (nmax + 1, nmax) hhfin)+  where+  (qvfin, hhfin, nmax, _) = execState (modifyUntil tf arnoldiStep) arnInit +  tf (_, _, ii, fbreak) = ii == kn || fbreak -- termination criterion   (m, n) = dim aa-  arnInit = (qv1, hh1, 1) where      -      q0 = normalize 2 $ onesSV n -- starting basis vector-      aq0 = aa #> q0-      h11 = q0 `dot` aq0+  arnInit = (qv1, hh1, 1, False) where+      q0 = normalize 2 b   -- starting basis vector+      aq0 = aa #> q0       -- A q0+      h11 = q0 `dot` aq0                 q1nn = (aq0 ^-^ (h11 .* q0))-      hh1 = fromListSM (m + 1, n) [(0, 0, h11), (1, 0, h21)] where        +      hh1 = V.fromList [(0, 0, h11), (1, 0, h21)] where                 h21 = norm 2 q1nn-      q1 = normalize 2 q1nn+      q1 = normalize 2 q1nn       -- q1 `dot` q0 ~ 0       qv1 = V.fromList [q0, q1]-  arnoldiStep (qv, hh, i) = (qv', hh', i + 1)-   where+  arnoldiStep (qv, hh, i, _) = (qv', hh', i + 1, fb') where     qi = V.last qv     aqi = aa #> qi     hhcoli = fmap (`dot` aqi) qv -- H_{1, i}, H_{2, i}, .. , H_{m + 1, i}     zv = zeroSV m     qipnn =       aqi ^-^ (V.foldl' (^+^) zv (V.zipWith (.*) hhcoli qv)) -- unnormalized q_{i+1}-    qipnorm = singletonSV $ norm 2 qipnn      -- normalization factor H_{i+1, i}+    qipnorm = norm 2 qipnn      -- normalization factor H_{i+1, i}     qip = normalize 2 qipnn              -- q_{i + 1}-    hh' = insertCol hh (concatSV (fromVector hhcoli) qipnorm) i -- update H+    hh' = (V.++) hh (indexed2 $ V.snoc hhcoli qipnorm) where -- update H+      indexed2 v = V.zip3 ii jj v+      ii = V.fromList [0 .. n]    -- nth col of upper Hessenberg has `n+1` nz+      jj = V.replicate (n + 1) i  -- `n+1` replicas of `i`     qv' = V.snoc qv qip        -- append q_{i+1} to Krylov basis Q_i---  ---+    fb' | nearZero qipnorm = True  -- breakdown condition+        | otherwise = False   @@ -501,6 +479,25 @@   f = extractSuperDiag aa  +-- -- ** Jacobi preconditioner++-- -- | Returns the reciprocal of the diagonal +-- jacobiPreconditioner :: SpMatrix Double -> SpMatrix Double+-- jacobiPreconditioner = reciprocal . extractDiag+++-- ** Incomplete LU++-- | Used for Incomplete LU : remove entries in `m` corresponding to zero entries in `m2`+ilu0 :: (Epsilon a, Real a, Fractional a) => SpMatrix a -> (SpMatrix a, SpMatrix a)+ilu0 aa = (lh, uh) where+  (l, u) = lu aa+  lh = sparsifyLU l aa+  uh = sparsifyLU u aa+  sparsifyLU m m2 = ifilterSM f m where+    f i j _ = isJust (lookupSM m2 i j)++ -- ** SSOR  -- | `mSsor aa omega` : if `omega = 1` it returns the symmetric Gauss-Seidel preconditioner@@ -524,18 +521,19 @@ luSolve ::   (Fractional a, Eq a, Epsilon a) => SpMatrix a -> SpMatrix a -> SpVector a -> SpVector a luSolve ll uu b-  | isLowerTriSM ll && isUpperTriSM uu = lubwSolve uu (lufwSolve ll b)+  | isLowerTriSM ll && isUpperTriSM uu = triUpperSolve uu (triLowerSolve ll b)   | otherwise = error "luSolve : factors must be triangular matrices"  -lufwSolve ll b = sparsifySV v where+triLowerSolve :: (Epsilon a, Fractional a) => SpMatrix a -> SpVector a -> SpVector a+triLowerSolve ll b = sparsifySV v where   (v, _) = execState (modifyUntil q lStep) lInit where   q (_, i) = i == dim b-  lStep (ww, i) = (wwi, i + 1) where+  lStep (ww, i) = (ww', i + 1) where     lii = ll @@ (i, i)     bi = b @@ i     wi = (bi - r)/lii where       r = extractSubRow ll i (0, i-1) `dot` takeSV i ww-    wwi = insertSpVector i wi ww+    ww' = insertSpVector i wi ww   lInit = (ww0, 1) where     l00 = ll @@ (0, 0)     b0 = b @@ 0@@ -543,15 +541,16 @@     ww0 = insertSpVector 0 w0 $ zeroSV (dim b)    -- | NB in the computation of `xi` we must rebalance the subrow indices because `dropSV` does that too, in order to take the inner product with consistent index pairs-lubwSolve uu w = sparsifySV x where+triUpperSolve :: (Epsilon a, Fractional a) => SpMatrix a -> SpVector a -> SpVector a+triUpperSolve uu w = sparsifySV x where   (x, _) = execState (modifyUntil q uStep) uInit   q (_, i) = i == (- 1)-  uStep (xx, i) = (xxi, i - 1) where+  uStep (xx, i) = (xx', i - 1) where     uii = uu @@ (i, i)     wi = w @@ i     xi = (wi - r) / uii where         r = extractSubRow_RK uu i (i + 1, dim w - 1) `dot` dropSV (i + 1) xx-    xxi = insertSpVector i xi xx+    xx' = insertSpVector i xi xx   uInit = (xx0, i - 1) where     i = dim w - 1     u00 = uu @@ (i, i)@@ -569,12 +568,42 @@  -- ** GMRES +-- | Given a linear system `A x = b` where `A` is an (m x m) real-valued matrix, the GMRES method finds an approximate solution `xhat` such that the Euclidean norm of the residual `A xhat - b` is minimized. `xhat` is spanned by the order-`n` Krylov subspace of (A, b).+-- In this implementation:+-- 1) the Arnoldi iteration is carried out until numerical breakdown (therefore yielding _at_most_ `m` Krylov basis vectors)+-- 2) the resulting Hessenberg matrix is factorized in QR form+-- 3) the Krylov-subspace solution `yhat` is found by backsubstitution+-- 4) the approximate solution in the original space `xhat` is computed using the Krylov basis, `xhat = Q_n yhat`+--+-- Many optimizations are possible, for example interleaving the QR factorization with the Arnoldi process (and employing an updating QR factorization which only requires one Givens' rotation at every update). ++gmres :: (Epsilon a, Ord a, Floating a) => SpMatrix a -> SpVector a -> SpVector a+gmres aa b = qa' #> yhat where+  m = ncols aa+  (qa, ha) = arnoldi aa b m   -- at most m steps of Arnoldi (aa, b)+  -- b' = (transposeSe qa) #> b+  b' = norm2 b .* (ei mp1 1)  -- b rotated back to canonical basis by Q^T+     where mp1 = nrows ha     -- 1 + # Arnoldi iterations+  (qh, rh) = qr ha            -- QR fact.of H+  yhat = triUpperSolve rh' rhs' where+    rhs' = takeSV (dim b' - 1) (transposeSM qh #> b')+    rh' = takeRows (nrows rh - 1) rh+  qa' = takeCols (ncols qa - 1) qa+++++++ -- *** Left-preconditioning     ++ -- ** CGNE  cgneStep :: SpMatrix Double -> CGNE -> CGNE@@ -587,6 +616,10 @@  data CGNE =   CGNE {_xCgne , _rCgne, _pCgne :: SpVector Double} deriving Eq+instance Show CGNE where+    show (CGNE x r p) = "x = " ++ show x ++ "\n" +++                       "r = " ++ show r ++ "\n" +++                       "p = " ++ show p ++ "\n"  cgne :: SpMatrix Double -> SpVector Double -> SpVector Double -> CGNE cgne aa b x0 = execState (untilConverged _xCgne (cgneStep aa)) cgneInit where@@ -645,7 +678,8 @@           _mTfq :: Int,           _tauTfq, _thetaTfq, _etaTfq, _rhoTfq, _alphaTfq :: Double}   deriving Eq-+instance Show TFQMR where+    show (TFQMR x _ _ _ _ _ _ _ _ _ _) = "x = " ++ show x ++ "\n"   -- ** BCG@@ -782,7 +816,7 @@  -- * Linear solver interface -data LinSolveMethod = CGNE_ | TFQMR_ | BCG_ | CGS_ | BICGSTAB_ deriving (Eq, Show) +data LinSolveMethod = GMRES_ | CGNE_ | TFQMR_ | BCG_ | CGS_ | BICGSTAB_ deriving (Eq, Show)   -- -- | Linear solve with _random_ starting vector -- linSolveM ::@@ -805,18 +839,19 @@       solve aa' b' | isDiagonalSM aa' = reciprocal aa' #> b' -- diagonal solve is easy                    | otherwise = solveWith aa' b'        solveWith aa' b' = case method of+        GMRES_ -> gmres aa' b'         CGNE_ -> _xCgne (cgne aa' b' x0)         TFQMR_ -> _xTfq (tfqmr aa' b' x0)         BCG_ -> _xBcg (bcg aa' b' x0)-        CGS_ ->  _xBicgstab (bicgstab aa' b' x0 x0)-        BICGSTAB_ -> _x (cgs aa' b' x0 x0)+        BICGSTAB_ ->  _xBicgstab (bicgstab aa' b' x0 x0)+        CGS_ -> _x (cgs aa' b' x0 x0)       x0 = mkSpVectorD n $ replicate n 0.1        (m, n) = dim aa       nb     = dim b  -- | <\\> : linSolve using the BiCGSTAB method as default (<\>) :: SpMatrix Double -> SpVector Double -> SpVector Double      -(<\>) = linSolve BICGSTAB_ +(<\>) = linSolve GMRES_      
test/LibSpec.hs view
@@ -13,14 +13,13 @@ import Numeric.LinearAlgebra.Sparse -- import Numeric.LinearAlgebra.Class -import Control.Monad (liftM, liftM2)+import Control.Applicative (liftA2)+-- import Control.Monad (liftM, liftM2, replicateM) import Control.Monad.Primitive import Data.Foldable (foldrM)  import Data.Sparse.Common --import Control.Monad (replicateM) import Control.Monad.State.Strict (execState)  import qualified System.Random.MWC as MWC@@ -55,9 +54,9 @@     it "eye : identity matrix" $       infoSM (eye 10) `shouldBe` SMInfo 10 0.1     it "insertCol : insert a column in a SpMatrix" $-      insertCol (eye 3) (fromListDenseSV 3 [2,2,2]) 0 `shouldBe` (fromListSM (3,3) [(0,0,2),(1,0,2),(1,1,1),(2,0,2),(2,2,1)])+      insertCol (eye 3) (fromListDenseSV 3 [2,2,2]) 0 `shouldBe` fromListSM (3,3) [(0,0,2),(1,0,2),(1,1,1),(2,0,2),(2,2,1)]     it "insertRow : insert a row in a SpMatrix" $-      insertRow (eye 3) (fromListDenseSV 3 [2,2,2]) 1 `shouldBe` (fromListSM (3,3) [(0,0,1), (1,0,2), (1,1,2), (1,2,2), (2,2,1)])+      insertRow (eye 3) (fromListDenseSV 3 [2,2,2]) 1 `shouldBe` fromListSM (3,3) [(0,0,1), (1,0,2), (1,1,2), (1,2,2), (2,2,1)]     it "extractCol -> insertCol : identity" $       insertCol (eye 3) (extractCol (eye 3) 1) 1 `shouldBe` eye 3     it "extractRow -> insertRow : identity" $@@ -77,61 +76,76 @@   describe "Numeric.LinearAlgebra.Sparse : Iterative linear solvers" $ do     -- it "TFQMR (2 x 2 dense)" $     --   normSq (_xTfq (tfqmr aa0 b0 x0) ^-^ x0true) <= eps `shouldBe` True+    it "GMRES (2 x 2 dense)" $+      nearZero (normSq (linSolve GMRES_ aa0 b0 ^-^ x0true)) `shouldBe` True+    it "GMRES (3 x 3 sparse, s.p.d.)" $+      nearZero (normSq (linSolve GMRES_ aa2 b2 ^-^ x2)) `shouldBe` True+    it "GMRES (4 x 4 sparse)" $+      nearZero (normSq (linSolve GMRES_ aa1 b1 ^-^ x1)) `shouldBe` True           it "BCG (2 x 2 dense)" $-      nearZero (normSq (_xBcg (bcg aa0 b0 x0) ^-^ x0true)) `shouldBe` True-    it "BiCGSTAB (2 x 2 dense)" $ -      nearZero (normSq (aa0 <\> b0 ^-^ x0true)) `shouldBe` True+      nearZero (normSq (linSolve BCG_ aa0 b0 ^-^ x0true)) `shouldBe` True+    it "BCG (3 x 3 sparse, s.p.d.)" $+      nearZero (normSq (linSolve BCG_ aa2 b2 ^-^ x2)) `shouldBe` True      +    -- it "BiCGSTAB (2 x 2 dense)" $ +    --   nearZero (normSq (linSolve BICGSTAB_ aa0 b0 ^-^ x0true)) `shouldBe` True+    it "BiCGSTAB (3 x 3 sparse, s.p.d.)" $ +      nearZero (normSq (linSolve BICGSTAB_ aa2 b2 ^-^ x2)) `shouldBe` True           it "CGS (2 x 2 dense)" $ -      nearZero (normSq (_x (cgs aa0 b0 x0 x0) ^-^ x0true)) `shouldBe` True-  describe "Numeric.LinearAlgebra.Sparse : Direct linear solvers" $ do-    it "LU (unoptimized) (4 x 4 sparse)" $ +      nearZero (normSq (linSolve CGS_ aa0 b0 ^-^ x0true)) `shouldBe` True+    it "CGS (3 x 3 sparse, s.p.d.)" $ +      nearZero (normSq (linSolve CGS_ aa2 b2 ^-^ x2)) `shouldBe` True      +  describe "Numeric.LinearAlgebra.Sparse : Direct linear solvers" $ +    it "luSolve (4 x 4 sparse)" $        checkLuSolve aa1 b1 `shouldBe` True            describe "Numeric.LinearAlgebra.Sparse : QR decomposition" $ do    -    it "QR (4 x 4 sparse)" $+    it "qr (4 x 4 sparse)" $       checkQr tm4 `shouldBe` True-    it "QR (3 x 3 dense)" $ +    it "qr (3 x 3 dense)" $        checkQr tm2 `shouldBe` True   describe "Numeric.LinearAlgebra.Sparse : LU decomposition" $ do-    it "LU (4 x 4 dense)" $+    it "lu (4 x 4 dense)" $       checkLu tm6 `shouldBe` True-    it "LU (10 x 10 sparse)" $+    it "lu (10 x 10 sparse)" $       checkLu tm7 `shouldBe` True-  describe "Numeric.LinearAlgebra.Sparse : Cholesky decomposition (PSD matrices only)" $ +  describe "Numeric.LinearAlgebra.Sparse : Cholesky decomposition (PSD matrices)" $      it "chol (5 x 5 sparse)" $       checkChol tm7 `shouldBe` True-  describe "Numeric.LinearAlgebra.Sparse : Arnoldi iteration" $ do-    it "Arnoldi iteration (3 x 3 dense)" $-      checkArnoldi aa2 3 `shouldBe` True-    it "Arnoldi iteration (5 x 5 sparse)" $+  describe "Numeric.LinearAlgebra.Sparse : Arnoldi iteration, early breakdown detection" $ do      +    it "arnoldi (4 x 4 dense)" $+      checkArnoldi tm6 4 `shouldBe` True+    it "arnoldi (5 x 5 sparse)" $       checkArnoldi tm7 5 `shouldBe` True      - {- QR-}   checkQr :: (Epsilon a, Real a, Floating a) => SpMatrix a -> Bool-checkQr a = c1 && c2 where+checkQr a = c1 && c2 && c3 where   (q, r) = qr a   c1 = nearZero $ normFrobenius ((q #~# r) ^-^ a)   c2 = isOrthogonalSM q+  c3 = isUpperTriSM r    {- LU -}  checkLu :: (Epsilon a, Real a, Floating a) => SpMatrix a -> Bool-checkLu a = lup == a where+checkLu a = c1 && c2 where   (l, u) = lu a-  lup = l #~# u+  c1 = nearZero (normFrobenius ((l #~# u) ^-^ a))+  c2 = isUpperTriSM u && isLowerTriSM l    {- Cholesky -}  checkChol :: (Epsilon a, Real a, Floating a) => SpMatrix a -> Bool-checkChol a = nearZero $ normFrobenius ((l ##^ l) ^-^ a) where+checkChol a = c1 && c2 where   l = chol a+  c1 = nearZero $ normFrobenius ((l ##^ l) ^-^ a)+  c2 = isLowerTriSM l   {- direct linear solver -}@@ -145,12 +159,13 @@    {- Arnoldi iteration -} checkArnoldi :: (Epsilon a, Floating a, Eq a) => SpMatrix a -> Int -> Bool-checkArnoldi aa kn = nearZero $ normFrobenius $ (aa #~# qvprev) ^-^ (qv #~# hh) where-  (qv, hh) = arnoldi aa kn-  (m, n) = dim qv-  qvprev = extractSubmatrix qv (0, m - 1) (0, n - 2)--+checkArnoldi aa kn = nearZero (normFrobenius $ lhs ^-^ rhs) where+  b = onesSV (nrows aa)+  (q, h) = arnoldi aa b kn+  (m, n) = dim q+  q' = extractSubmatrix q (0, m - 1) (0, n - 2) -- q' = all but one column of q+  rhs = q #~# h+  lhs = aa #~# q'   {-@@ -258,19 +273,20 @@ --   let --     diags = [-bw .. bw - 1] -randDiagMat :: PrimMonad m =>-     Rows -> Double -> Double -> Int -> m (SpMatrix Double)-randDiagMat n mu sig i = do-  x <- randArray n mu sig-  return $ mkSubDiagonal n i x+-- randDiagMat :: PrimMonad m =>+--      Rows -> Double -> Double -> Int -> m (SpMatrix Double)+-- randDiagMat n mu sig i = do+--   x <- randArray n mu sig+--   return $ mkSubDiagonal n i x  -go (m:ms) mat =-  m ^+^ go ms mat-go [] mat = mat+-- go (m:ms) mat =+--   m ^+^ go ms mat+-- go [] mat = mat -  -plusM x y = return $ x ^+^ y+-- plusM ::+--   (Additive f1, Applicative f, Num a) => f (f1 a) -> f (f1 a) -> f (f1 a)  +-- plusM = liftA2 (^+^)   @@ -397,6 +413,11 @@   b = mkSubDiagonal n 0 $ replicate n 2   c = mkSubDiagonal n (-1) $ replicate n (-1) +++++ -- -- run N iterations   -- -- runNBiC :: Int -> SpMatrix Double -> SpVector Double -> BICGSTAB@@ -434,3 +455,8 @@  tm8' :: SpMatrix Double tm8' = fromListSM (2,2) [(0,0,1), (1,0,1), (1,1,1)]++++tm9 :: SpMatrix Double+tm9 = fromListSM (4, 3) [(0,0,pi), (1,1, 3), (2,2,4), (3,2, 1), (3,1, 5)]