module Numeric.LinearAlgebra.Array.Solve (
solve,
solveHomog, solveHomog1, solveH,
solveP,
ALSParam(..), defaultParameters,
mlSolve, mlSolveH, mlSolveP,
solveFactors, solveFactorsH,
eps, eqnorm, infoRank,
solve', solveHomog', solveHomog1', solveP'
) where
import Numeric.LinearAlgebra.Array.Util
import Numeric.LinearAlgebra.Exterior
import Numeric.LinearAlgebra.Array.Internal(mkNArray, selDims, debug, namesR)
import Numeric.LinearAlgebra hiding ((.*), scalar)
import Data.List
import System.Random
solve :: (Compat i, Coord t)
=> NArray i t
-> NArray i t
-> NArray i t
solve = solve' id
solve' g a b = x where
nx = namesR a \\ namesR b
na = namesR a \\ nx
nb = namesR b \\ namesR a
aM = g $ matrixator a na nx
bM = matrixator b na nb
xM = linearSolveSVD aM bM
dx = map opos (selDims (dims a) nx) ++ selDims (dims b) nb
x = mkNArray dx (flatten xM)
solveHomog :: (Compat i, Coord t)
=> NArray i t
-> [Name]
-> Either Double Int
-> [NArray i t]
solveHomog = solveHomog' id
solveHomog' g a nx' hint = xs where
nx = filter (`elem` (namesR a)) nx'
na = namesR a \\ nx
aM = g $ matrixator a na nx
vs = nullspaceSVD hint aM (rightSV aM)
dx = map opos (selDims (dims a) nx)
xs = map (mkNArray dx) vs
solveHomog1 :: (Compat i, Coord t)
=> NArray i t
-> [Name]
-> NArray i t
solveHomog1 = solveHomog1' id
solveHomog1' g m ns = head $ solveHomog' g m ns (Right (k1))
where k = product $ map iDim $ selDims (dims m) ns
solveH :: (Compat i, Coord t) => NArray i t -> [Char] -> NArray i t
solveH m ns = solveHomog1 m (map return ns)
solveP :: Tensor Double
-> Tensor Double
-> Name
-> Tensor Double
solveP = solveP' id
solveP' g a b h = mapTat (solveP1 g h a) (namesR b \\ (h:namesR a)) b
solveP1 g nh a b = solveHomog1' g ou ns where
k = size nh b
epsi = t $ leviCivita k `renameO` (nh : (take (k1) $ (map (('e':).(:[])) ['2'..])))
ou = a .* b' * epsi
ns = (namesR a \\ namesR b) ++ x
b' = renameExplicit [(nh,"e2")] b
x = if nh `elem` (namesR a) then [] else [nh]
t = if typeOf nh b == Co then contrav else cov
data ALSParam i t = ALSParam
{ nMax :: Int
, delta :: Double
, epsilon :: Double
, post :: [NArray i t] -> [NArray i t]
, postk :: Int -> NArray i t -> NArray i t
, presys :: Matrix t -> Matrix t
}
optimize :: (x -> x)
-> (x -> Double)
-> x
-> ALSParam i t
-> (x, [Double])
optimize method errfun s0 p = (sol,e) where
sols = take (max 1 (nMax p)) $ iterate method s0
errs = map errfun sols
(sol,e) = convergence (zip sols errs) []
convergence [] _ = error "impossible"
convergence [(s,err)] prev = (s, err:prev)
convergence ((s1,e1):(s2,e2):ses) prev
| e1 < epsilon p = (s1, e1:prev)
| abs (100*(e1 e2)/e1) < delta p = (s2, e2:prev)
| otherwise = convergence ((s2,e2):ses) (e1:prev)
percent t s = 100 * frobT (t smartProduct s) / frobT t
percentP h t s = 100 * frobT (t' s') / frobT t' where
t' = f t
s' = f (smartProduct s)
f = mapTat g (namesR t \\ [h])
g v = v / atT v [n]
n = size h t 1
frobT t = realToFrac . pnorm PNorm2 . coords $ t
dropElemPos k xs = take k xs ++ drop (k+1) xs
replaceElemPos k v xs = take k xs ++ v : drop (k+1) xs
takes [] _ = []
takes (n:ns) xs = take n xs : takes ns (drop n xs)
alsStep f params a x = (foldl1' (.) (map (f params a) [n,n1 .. 0])) x
where n = length x 1
mlSolve
:: (Compat i, Coord t, Num (NArray i t))
=> ALSParam i t
-> [NArray i t]
-> [NArray i t]
-> NArray i t
-> ([NArray i t], [Double])
mlSolve params a x0 b
= optimize (post params . alsStep (alsArg b) params a) (percent b . (a++)) x0 params
alsArg _ _ _ _ [] = error "alsArg _ _ []"
alsArg b params a k xs = sol where
p = smartProduct (a ++ dropElemPos k xs)
x = solve' (presys params) p b
x' = postk params k x
sol = replaceElemPos k x' xs
mlSolveH
:: (Compat i, Coord t, Num (NArray i t))
=> ALSParam i t
-> [NArray i t]
-> [NArray i t]
-> ([NArray i t], [Double])
mlSolveH params a x0
= optimize (post params . alsStep alsArgH params a) (frobT . smartProduct . (a++)) x0 params
alsArgH _ _ _ [] = error "alsArgH _ _ []"
alsArgH params a k xs = sol where
p = smartProduct (a ++ dropElemPos k xs)
x = solveHomog1' (presys params) p (namesR (xs!!k))
x' = postk params k x
sol = replaceElemPos k x' xs
mlSolveP
:: ALSParam Variant Double
-> [Tensor Double]
-> [Tensor Double]
-> Tensor Double
-> Name
-> ([Tensor Double], [Double])
mlSolveP params a x0 b h
= optimize (post params . alsStep (alsArgP b h) params a) (percentP h b . (a++)) x0 params
alsArgP _ _ _ _ _ [] = error "alsArgP _ _ []"
alsArgP b h params a k xs = sol where
p = smartProduct (a ++ dropElemPos k xs)
x = solveP' (presys params) p b h
x' = postk params k x
sol = replaceElemPos k x' xs
solveFactors :: (Coord t, Random t, Compat i, Num (NArray i t))
=> Int
-> ALSParam i t
-> [NArray i t]
-> String
-> NArray i t
-> ([NArray i t],[Double])
solveFactors seed params a pairs b =
mlSolve params a (initFactorsRandom seed (smartProduct a) pairs b) b
initFactorsSeq rs a pairs b | ok = as
| otherwise = error "solveFactors index pairs"
where
(ia,ib) = unzip (map (\[x,y]->([x],[y])) (words pairs))
ic = intersect (namesR a) (namesR b)
ok = sort (namesR b\\ic) == sort ib && sort (namesR a\\ic) == sort ia
db = selDims (dims b) ib
da = selDims (dims a) ia
nb = map iDim db
na = map iDim da
ts = takes (zipWith (*) nb na) rs
as = zipWith5 f ts ib ia db da
f c i1 i2 d1 d2 = (mkNArray [d1,opos d2] (fromList c)) `renameO` [i1,i2]
initFactorsRandom seed a b = initFactorsSeq (randomRs (1,1) (mkStdGen seed)) a b
solveFactorsH
:: (Coord t, Random t, Compat i, Num (NArray i t))
=> Int
-> ALSParam i t
-> [NArray i t]
-> String
-> ([NArray i t], [Double])
solveFactorsH seed params a pairs =
mlSolveH params a (initFactorsHRandom seed (smartProduct a) pairs)
initFactorsHSeq rs a pairs = as where
(ir,it) = unzip (map (\[x,y]->([x],[y])) (words pairs))
nr = map (flip size a) ir
nt = map (flip size a) it
ts = takes (zipWith (*) nr nt) rs
as = zipWith5 f ts ir it (selDims (dims a) ir) (selDims (dims a) it)
f c i1 i2 d1 d2 = (mkNArray (map opos [d1,d2]) (fromList c)) `renameO` [i1,i2]
initFactorsHRandom seed a pairs = initFactorsHSeq (randomRs (1,1) (mkStdGen seed)) a pairs
eqnorm :: (Compat i,Show (NArray i Double))
=> [NArray i Double] -> [NArray i Double]
eqnorm [] = error "eqnorm []"
eqnorm as = as' where
n = length as
fs = map (frobT) as
s = product fs ** (1/fromIntegral n)
as' = zipWith g as fs where g a f = a * (scalar (s/f))
defaultParameters :: ALSParam i t
defaultParameters = ALSParam {
nMax = 20,
epsilon = 1E-3,
delta = 1,
post = id,
postk = const id,
presys = id
}
infoRank :: Field t => Matrix t -> Matrix t
infoRank a = debug "" (const (rows a, cols a, rank a)) a