dynobud-1.3.0.0: src/Dyno/AutoScaling.hs
{-# OPTIONS_GHC -Wall #-}
{-# Language ScopedTypeVariables #-}
{-# Language DeriveFunctor #-}
{-# Language DeriveGeneric #-}
module Dyno.AutoScaling
( scalingNlp
, kktScalingInfo
, beforeAndAfter
) where
import Data.List ( minimumBy, maximumBy )
import Data.Proxy ( Proxy(..) )
import qualified Data.Vector as V
--import qualified Numeric.LinearAlgebra.Data as HMat
--import qualified Numeric.LinearAlgebra.HMatrix as HMat
import Text.Printf ( printf )
import Casadi.Sparsity ( getRow, getCol )
import Casadi.SX ( SX )
import Casadi.DMatrix ( DMatrix, ddata )
import qualified Casadi.CMatrix as CM
import Dyno.View.Unsafe.View ( mkJ, unJ )
import Dyno.View.Unsafe.M ( unM )
import Dyno.Vectorize ( Id(..) )
import Dyno.Nlp ( KKT(..), Nlp(..) )
import Dyno.View.View ( View(..), J, JNone(..), v2d, d2v, jfill)
import Dyno.View.Viewable ( Viewable )
import qualified Dyno.View.M as M
import Dyno.View.M ( M )
import Dyno.View.JV ( JV, splitJV )
toSparse :: (View f, View g) => String -> M f g DMatrix -> [(Int,Int,Double)]
toSparse name mat0
| V.length row /= V.length col = error $ name ++ " row/column index mismatch"
| V.length row /= V.length dat = error $ name ++ " sparsity patter size doesn't match data size"
| otherwise = V.toList $ V.zip3 row col dat
where
mat = unM $ M.sparse mat0
sp = CM.sparsity mat
dat = ddata mat
row = getRow sp
col = getCol sp
kktScalingInfo :: (View f, View g) => KKT f g -> String
kktScalingInfo kkt =
init $ unlines
[ showOne "hessLag " (kktHessLag kkt)
, showOne "hessF " (kktHessF kkt)
, showOne "hessLamG " (kktHessLambdaG kkt)
, showOne "jacG " (kktJacG kkt)
, showOne "gradF " (M.col (kktGradF kkt))
]
where
showOne name m =
printf "%s size (%5d, %5d), nonzeros %7d/%10d (%6.2f %%), min: %s, max: %s, ratio: %s"
name r c nz (r*c)
(100 * fromIntegral nz / fromIntegral (r*c) :: Double)
min' max' ratio
where
byAbs x y = compare (abs x) (abs y)
min' = case d of
[] -> " N/A"
ds -> printf "% 8.2e" (minimumBy byAbs ds)
max' = case d of
[] -> " N/A"
ds -> printf "% 8.2e" (maximumBy byAbs ds)
ratio = case d of
[] -> " N/A"
ds -> printf "% 8.2e" (minimumBy byAbs ds / maximumBy byAbs ds)
nz = length d
(_,_,d) = unzip3 (toSparse name m)
r = CM.size1 (unM m)
c = CM.size2 (unM m)
-- log |aij| + sj + si (+ sf)
data LogScaling a =
LogScaling
{ lsHessF :: [a]
, lsHessLambdaG :: [a]
, lsHessLag :: [a]
, lsJacG :: [a]
, lsGradF :: [a]
} deriving Functor
toObjective :: Floating a => LogScaling a -> a
toObjective (LogScaling hf hlg hl jg gf) = sum (map sqr hf) + sum (map sqr hlg) + 0*sum (map sqr hl) + 2*sum (map sqr jg) + sum (map sqr gf)
where
sqr x = x*x
toMatrixCoeffs :: Floating a => LogScaling a -> LogScaling a
toMatrixCoeffs (LogScaling hf hlg hl jg gf) = LogScaling (f hf) (f hlg) (f hl) (f jg) (f gf)
where
f = map exp
toLogScaling ::
forall x g sdv a
. (View x, View g, View sdv, Viewable a, CM.CMatrix a)
=> KKT x g -> (J sdv a -> (J (JV Id) a, J x a, J g a)) -> J sdv a -> LogScaling (J (JV Id) a)
toLogScaling kkt expand sdvs =
LogScaling
{ lsJacG = jacGObjValues
, lsHessF = hessFObjValues
, lsHessLambdaG = hessLambdaGObjValues
, lsHessLag = hessLagObjValues
, lsGradF = gradFObjValues
}
where
jacGMatValues = toSparse "jacG" (kktJacG kkt)
hessFMatValues = toSparse "hessF" (kktHessF kkt)
hessLambdaGMatValues = toSparse "hessLamG" (kktHessLambdaG kkt)
hessLagMatValues = toSparse "hessLag" (kktHessLag kkt)
gradFMatValues = toSparse "gradF" (M.col (kktGradF kkt))
objScale' :: J (JV Id) a
x :: J x a
g' :: J g a
(objScale', x, g') = expand sdvs
-- constraints and objective are inverted
objScale = negate objScale'
g = negate g'
reproxy :: J f a -> Proxy f
reproxy = const Proxy
nx = size (reproxy x)
ng = size (reproxy g)
xs,gs :: V.Vector (J (JV Id) a)
xs = fmap mkJ $ CM.vertsplit (unJ x) (V.fromList [0..nx])
gs = fmap mkJ $ CM.vertsplit (unJ g) (V.fromList [0..ng])
gradFObjValues :: [J (JV Id) a]
gradFObjValues = map (toSum xs (V.singleton objScale)) gradFMatValues
jacGObjValues :: [J (JV Id) a]
jacGObjValues = map (toSum gs xs) jacGMatValues
hessFObjValues :: [J (JV Id) a]
hessFObjValues = map ((+ objScale) . toSum xs xs) hessFMatValues
hessLambdaGObjValues :: [J (JV Id) a]
hessLambdaGObjValues = map ((+ objScale) . toSum xs xs) hessLambdaGMatValues
hessLagObjValues :: [J (JV Id) a]
hessLagObjValues = map ((+ objScale) . toSum xs xs) hessLagMatValues
toSum :: forall a .
(Fractional a) =>
V.Vector a -> V.Vector a -> (Int, Int, Double) -> a
toSum rowVec colVec (rowi,colj,value)
| absValue == 0 = error "toSum: log(0)"
| logAbsValue' < -1000 = error "really really small value"
| logAbsValue' > 1000 = error "really really big value"
| otherwise = logAbsValue + si + sj
where
absValue = abs value
logAbsValue = realToFrac (log absValue)
logAbsValue' = log absValue
si,sj :: a
si = rowVec V.! rowi
sj = colVec V.! colj
scalingNlp ::
forall x g sdv
. (View x, View g, View sdv)
=> KKT x g -> (J sdv SX -> (J (JV Id) SX, J x SX, J g SX)) -> Nlp sdv JNone JNone SX
scalingNlp kkt expand =
Nlp
{ nlpBX = jfill (Nothing, Nothing)
, nlpBG = cat JNone
, nlpX0 = jfill 0 -- unit scaling, initially
, nlpP = cat JNone
, nlpLamX0 = Nothing
, nlpLamG0 = Nothing
, nlpScaleF = Nothing
, nlpScaleX = Nothing
, nlpScaleG = Nothing
, nlpFG = fg
}
where
fg :: J sdv SX -> J JNone SX -> (J (JV Id) SX, J JNone SX)
fg sdvs _ = (obj, cat JNone)
where
obj = toObjective $ toLogScaling kkt expand sdvs
beforeAndAfter
:: (View x, View g, View sdv)
=> KKT x g
-> (J sdv DMatrix -> (J (JV Id) DMatrix, J x DMatrix, J g DMatrix))
-> J sdv (V.Vector Double)
-> String
beforeAndAfter kkts expand scalingSol =
init $ unlines
[ minMax "hessF0" hessF0
, minMax "hessF " hessF
, ""
, minMax "hessLamG0" hessLamG0
, minMax "hessLamG " hessLamG
, ""
, minMax "hessLag0" hessLag0
, minMax "hessLag " hessLag
, ""
, minMax "jacG0" jacG0
, minMax "jacG " jacG
, ""
, minMax "gradF0" gradF0
, minMax "gradF " gradF
]
where
ls0 = fmap (unId . splitJV . d2v) $ toLogScaling kkts expand (v2d (jfill 0))
LogScaling hessF0 hessLamG0 hessLag0 jacG0 gradF0 = toMatrixCoeffs ls0 :: LogScaling Double
ls :: LogScaling Double
ls = fmap (unId . splitJV . d2v) $ toLogScaling kkts expand (v2d scalingSol)
LogScaling hessF hessLamG hessLag jacG gradF = toMatrixCoeffs ls :: LogScaling Double
minMax name xs = printf "%s min: %s, max: %s, ratio: %s" name min' max' ratio
where
-- protect against empty list
min' = case xs of
[] -> "N/A"
xs' -> printf "% 8.2e" (minimum xs')
max' = case xs of
[] -> "N/A"
xs' -> printf "% 8.2e" (maximum xs')
ratio = case xs of
[] -> "N/A"
xs' -> printf "% 8.2e" (minimum xs' / maximum xs')
--analyzeSol :: Nlp'
-- (CollTraj AcX None AcU AcP NCollStages CollDeg)
-- JNone
-- (CollOcpConstraints NCollStages CollDeg AcX AcX Bc PathC)
-- MX ->
-- Save
-- (CollTraj AcX None AcU AcP NCollStages CollDeg)
-- (CollOcpConstraints NCollStages CollDeg AcX AcX Bc PathC) ->
-- IO ()
--analyzeSol nlp save = do
-- let sol = savedNlpOut save
-- putStrLn "creating jacobian..."
-- --nj <- nlpJac nlp
-- nj' <- nlpJac' nlp
-- putStrLn "evaluating jacobian..."
-- --(jacFG', fg) <- nj (v2d (xOpt' sol)) (cat JNone)
-- (dgdx, _) <- nj' (v2d (xOpt' sol))
-- putStrLn "finished! analyzing..."
-- let --JTuple f0' g0' = split fg
-- --Id _f0 = splitJV (d2v f0')
-- --_g0 = unJ $ d2v g0'
-- --
-- --dfgdx :: M
-- -- (JTuple (JV Id) (CollOcpConstraints NCollStages CollDeg AcX AcX Bc PathC))
-- -- (CollTraj AcX None AcU AcP NCollStages CollDeg)
-- -- DMatrix
-- --(dfgdx,_) = M.hsplitTup jacFG'
-- --_dfdx :: M (JV Id) (CollTraj AcX None AcU AcP NCollStages CollDeg) DMatrix
-- --dgdx :: M
-- -- (CollOcpConstraints NCollStages CollDeg AcX AcX Bc PathC)
-- -- (CollTraj AcX None AcU AcP NCollStages CollDeg)
-- -- DMatrix
-- --(_dfdx, dgdx) = M.vsplitTup dfgdx
--
--
-- -- todo: this only works for worhp heh
-- isActive :: Double -> Bool
-- isActive lambda = (abs lambda) > 1e-15
---- isActive :: (Double,Double) -> Double -> Double -> Bool
---- isActive (lb, ub) val lambda
---- | val <= lb = True
---- | ub <= val = True
---- | (abs lambda) > 1e-15 = True
---- | otherwise = False
--
-- activeX = V.map isActive (unJ (lambdaXOpt' sol))
-- activeG = V.map isActive (unJ (lambdaGOpt' sol))
-- activeAll = activeX V.++ activeG
--
-- activeXIndices = map fst $ filter snd $ zip [(0::Int)..] (V.toList activeX)
--
-- nx = size (Proxy :: Proxy (CollTraj AcX None AcU AcP NCollStages CollDeg))
-- ng = size (Proxy :: Proxy (CollOcpConstraints NCollStages CollDeg AcX AcX Bc PathC))
-- fullJac = (HMat.ident nx) HMat.=== dgdx'
-- dgdx' = M.toHMat dgdx
--
-- delRows [] [] = []
-- delRows (False:act) (_:gs) = delRows act gs
-- delRows (True :act) (g:gs) = g : delRows act gs
-- delRows _ _ = error "delRows got length mismatch"
--
-- activeFullJac :: HMat.Matrix Double
-- activeFullJac = HMat.fromRows $ delRows (V.toList activeAll) (HMat.toRows fullJac)
--
-- activeGJac :: HMat.Matrix Double
-- activeGJac = HMat.fromRows $ delRows (V.toList activeG) (HMat.toRows dgdx')
--
-- printf "num x: %5d, active x: %5d\n" nx (V.length (V.filter id activeX))
-- printf "num g: %5d, active g: %5d\n" ng (V.length (V.filter id activeG))
---- putStrLn $ take 100 $ show dgdx
---- putStrLn "===================="
---- putStrLn $ take 100 $ show dgdx'
-- printf "dgdx': (%d, %d)\n" (HMat.rows dgdx') (HMat.cols dgdx')
-- printf "active full jac size: (%d,%d)\n" (HMat.rows activeFullJac) (HMat.cols activeFullJac)
-- printf "active g jac size: (%d,%d)\n" (HMat.rows activeGJac) (HMat.cols activeGJac)
-- putStrLn $ "active design vars: " ++ take 100 (show activeXIndices)
-- writeFile "/home/ghorn/takeIt.txt" (saveMat (HMat.toLists activeFullJac))
-- printf " g jac rank: %d\n" (HMat.rank activeGJac)
-- printf " g jac rcond: %.3e\n" (HMat.rcond activeGJac)
-- printf "full jac rank: %d\n" (HMat.rank activeFullJac)
-- printf "full jac rcond: %.3e\n" (HMat.rcond activeFullJac)
--
-- let CollTraj tf' p' _stages _xf = split $ lambdaXOpt' sol
-- p = splitJV p'
-- Id tf = splitJV tf'
-- print tf
-- print p
-- return ()