dynobud-1.7.1.0: src/Dyno/DirectCollocation/Export.hs
{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE PolyKinds #-}
module Dyno.DirectCollocation.Export
( Export(..)
, ExportConfig(..)
, exportTraj
, exportTraj'
-- * matlab specific
, matlabParam
, matlabTraj
-- * python specific
, PythonExporter
, runPythonExporter
, pythonParam
, pythonTraj
, write
) where
import Control.Monad ( unless )
import Data.List ( unzip6, intercalate )
import Data.Proxy ( Proxy(..) )
import Linear.V ( Dim(..) )
import Data.Vector ( Vector )
import qualified Data.Foldable as F
import Control.Monad.State.Lazy ( State )
import qualified Control.Monad.State.Lazy as State
import qualified Data.Set as S
import Accessors ( Lookup, Getter(..), flatten, flatten', accessors )
import Dyno.Nlp ( NlpOut(..) )
import Dyno.TypeVecs ( Vec )
import Dyno.Vectorize ( Vectorize, Id(..), None(..), fill )
import Dyno.View.View ( View(..) )
import Dyno.View.JV ( splitJV, catJV )
import Dyno.DirectCollocation.Formulate ( CollProblem(..) )
import Dyno.DirectCollocation.Types ( CollTraj(..), CollOcpConstraints(..)
, StageOutputs(..), Quadratures(..)
, getXzus'''
)
import Dyno.DirectCollocation.Quadratures ( timesFromTaus )
data ExportConfig =
ExportConfig
{ ecMatlabVariableName :: String
, ecPythonVariableName :: String
}
data Export =
Export
{ exportMatlab :: String
, exportPython :: String
}
exportTraj ::
forall x z u p fp r o c h q qo po n deg
. ( Lookup (x Double), Vectorize x
, Lookup (z Double), Vectorize z
, Lookup (u Double), Vectorize u
, Lookup (o Double), Vectorize o
, Lookup (p Double), Vectorize p
, Lookup (c Double), Vectorize c
, Vectorize r
, Lookup (fp Double), Vectorize fp
, Lookup (h Double), Vectorize h
, Lookup (q Double), Vectorize q
, Lookup (po Double), Vectorize po
, Lookup (qo Double), Vectorize qo
, Dim n, Dim deg
)
=> ExportConfig
-> CollProblem x z u p r o c h q qo po fp n deg
-> fp Double
-> NlpOut (CollTraj x z u p n deg) (CollOcpConstraints x r c h n deg) (Vector Double)
-> IO Export
exportTraj = exportTraj' (Nothing :: Maybe ([String], None Double))
-- | this version takes optional user data
exportTraj' ::
forall x z u p fp r o c h q qo po n deg e
. ( Lookup (x Double), Vectorize x
, Lookup (z Double), Vectorize z
, Lookup (u Double), Vectorize u
, Lookup (o Double), Vectorize o
, Lookup (p Double), Vectorize p
, Lookup (c Double), Vectorize c
, Vectorize r
, Lookup (fp Double), Vectorize fp
, Lookup (h Double), Vectorize h
, Lookup (q Double), Vectorize q
, Lookup (po Double), Vectorize po
, Lookup (qo Double), Vectorize qo
, Dim n, Dim deg
, Lookup (e Double), Vectorize e
)
=> Maybe ([String], e Double)
-> ExportConfig
-> CollProblem x z u p r o c h q qo po fp n deg
-> fp Double
-> NlpOut (CollTraj x z u p n deg) (CollOcpConstraints x r c h n deg) (Vector Double)
-> IO Export
exportTraj' mextra exportConfig cp fp nlpOut = do
let matlabRetName = ecMatlabVariableName exportConfig
pyRetName = ecPythonVariableName exportConfig
let ct@(CollTraj tf' p' _ _) = split (xOpt nlpOut)
CollTraj lagTf' lagP' _ _ = split (lambdaXOpt nlpOut)
lagBc' = coBc $ split (lambdaGOpt nlpOut)
(_, outs, finalQuads) <- cpHellaOutputs cp (cat ct) (catJV fp)
let _ = outs :: Vec n (StageOutputs x o h q qo po deg Double)
_ = finalQuads :: Quadratures q qo Double
let taus :: Vec deg Double
taus = cpTaus cp
Id tf = splitJV tf'
n = reflectDim (Proxy :: Proxy n)
times :: Vec n (Double, Vec deg Double)
times = timesFromTaus 0 taus dt
where
dt = tf / fromIntegral n
xTimes = concatMap (\(t0,ts) -> t0 : F.toList ts) (F.toList times) ++ [tf]
zuoTimes = concatMap (\(_,ts) -> F.toList ts) (F.toList times)
xss :: Vec n (x Double, Vec deg (x Double))
xf :: x Double
zss :: Vec n (Vec deg (z Double))
uss :: Vec n (Vec deg (u Double))
((xss,xf), zss, uss) = getXzus''' ct
fullXs :: [x Double]
fullXs = concatMap (\(x0, xs') -> x0 : F.toList xs') (F.toList xss) ++ [xf]
xs :: [x Double]
xs = concatMap (F.toList . snd) (F.toList xss)
zs :: [z Double]
zs = concatMap F.toList (F.toList zss)
us :: [u Double]
us = concatMap F.toList (F.toList uss)
os :: [o Double]
xdots :: [x Double]
hs :: [h Double]
-- drop the interpolated value
os = map splitJV os'
xdots = map splitJV xdots'
hs = map splitJV hs'
pos = map splitJV pos'
(os', xdots', hs', pos', _, _) = unzip6 $ F.concatMap (F.toList . soVec) outs
qsFull :: [Quadratures q qo Double]
qsFull = fill 0 : F.concatMap toQFull outs
where
toQFull :: StageOutputs x o h q qo po deg Double -> [Quadratures q qo Double]
toQFull stageOutputs = (map (\(_,_,_,_,qs',_) -> qs') (F.toList (soVec stageOutputs))) ++ [soQNext stageOutputs]
qs :: [Quadratures q qo Double]
qs = F.concatMap toQ outs
where
toQ :: StageOutputs x o h q qo po deg Double -> [Quadratures q qo Double]
toQ stageOutputs = map (\(_,_,_,_,qs',_) -> qs') (F.toList (soVec stageOutputs))
toQd :: StageOutputs x o h q qo po deg Double -> [Quadratures q qo Double]
toQd stageOutputs = (map (\(_,_,_,_,_,qd) -> qd) (F.toList (soVec stageOutputs)))
qds :: [Quadratures q qo Double]
qds = F.concatMap toQd outs
matlabOut :: String
matlabOut = unlines $
matlabTraj (matlabRetName ++ ".diffStatesFull") fullXs ++
matlabTraj (matlabRetName ++ ".diffStates") xs ++
matlabTraj (matlabRetName ++ ".diffStateDerivs") xdots ++
matlabTraj (matlabRetName ++ ".algVars") zs ++
matlabTraj (matlabRetName ++ ".controls") us ++
matlabTraj (matlabRetName ++ ".outputs") os ++
matlabTraj (matlabRetName ++ ".pathConstraints") hs ++
matlabTraj (matlabRetName ++ ".plotOutputs") pos ++
matlabTraj (matlabRetName ++ ".quadratureStatesFull") qsFull ++
matlabTraj (matlabRetName ++ ".quadratureStates") qs ++
matlabTraj (matlabRetName ++ ".quadratureStateDerivs") qds ++
matlabParam (matlabRetName ++ ".params") (splitJV p') ++
( case mextra of
Nothing -> []
Just (names,extra) -> matlabParam (intercalate "." (matlabRetName : names)) extra
) ++
matlabParam (matlabRetName ++ ".lagrangeMultipliers.params") (splitJV lagP') ++
matlabParam (matlabRetName ++ ".lagrangeMultipliers.bc") (splitJV lagBc') ++
matlabParam (matlabRetName ++ ".finalQuadratureStates") finalQuads ++
[ matlabRetName ++ ".lagrangeMultipliers.T = " ++ show (unId (splitJV lagTf')) ++ ";"
, ""
, matlabRetName ++ ".tx = " ++ show xTimes ++ ";"
, matlabRetName ++ ".tzuo = " ++ show zuoTimes ++ ";"
, matlabRetName ++ ".T = " ++ show tf ++ ";"
, matlabRetName ++ ".N = " ++ show n ++ ";"
, matlabRetName ++ ".deg = " ++ show (reflectDim (Proxy :: Proxy deg)) ++ ";"
, matlabRetName ++ ".collocationRoots = '" ++ show (cpRoots cp) ++ "';"
]
runRet :: State PythonExporter ()
runRet = do
write "import numpy"
write ""
write $ pyRetName ++ " = {}"
pythonTraj pyRetName ["diffStatesFull"] fullXs
pythonTraj pyRetName ["diffStates"] xs
pythonTraj pyRetName ["diffStateDerivs"] xdots
pythonTraj pyRetName ["algVars"] zs
pythonTraj pyRetName ["controls"] us
pythonTraj pyRetName ["outputs"] os
pythonTraj pyRetName ["pathConstraints"] hs
pythonTraj pyRetName ["plotOutputs"] pos
pythonTraj pyRetName ["quadratureStatesFull"] qsFull
pythonTraj pyRetName ["quadratureStates"] qs
pythonTraj pyRetName ["quadratureStateDerivs"] qds
pythonParam pyRetName ["params"] (splitJV p')
case mextra of
Nothing -> return ()
Just (names,extra) -> pythonParam pyRetName names extra
pythonParam pyRetName ["lagrangeMultipliers","params"] (splitJV lagP')
pythonParam pyRetName ["lagrangeMultipliers","bc"] (splitJV lagBc')
pythonParam pyRetName ["finalQuadratureStates"] finalQuads
putVal pyRetName ["lagrangeMultipliers","T"] (show (unId (splitJV lagTf')))
write ""
putVal pyRetName ["tx"] (npArray (show xTimes))
putVal pyRetName ["tzuo"] (npArray (show zuoTimes))
putVal pyRetName ["T"] (show tf)
putVal pyRetName ["N"] (show n)
putVal pyRetName ["deg"] (show (reflectDim (Proxy :: Proxy deg)))
putVal pyRetName ["collocationRoots"] ("'" ++ show (cpRoots cp) ++ "'")
return $ Export
{ exportMatlab = matlabOut
, exportPython = unlines (runPythonExporter runRet)
}
runPythonExporter :: State PythonExporter () -> [String]
runPythonExporter action = reverse pythonOut
where
PythonExporter (_, pythonOut) = State.execState action (PythonExporter (S.empty, []))
npArray :: String -> String
npArray str = "numpy.array(" ++ str ++ ")"
toDub :: Getter (xzu Double) -> xzu Double -> Double
toDub (GetDouble f) = f
toDub (GetFloat f) = realToFrac . f
toDub (GetInt f) = realToFrac . f
toDub (GetBool f) = fromIntegral . fromEnum . f
toDub (GetString _) = const (read "NaN")
toDub GetSorry = const (read "NaN")
pythonParam :: forall p . (Vectorize p, Lookup (p Double))
=> String -> [String] -> p Double -> State PythonExporter ()
pythonParam pyRetName topNames p = mapM_ pyParam at'
where
pyParam :: ([String], (p Double -> Double)) -> State PythonExporter ()
pyParam (name, get) = putVal pyRetName (topNames ++ name) (show (get p))
at' :: [([String], p Double -> Double)]
at' = map (\(fn,g,_) -> (fn, toDub g)) $ flatten' $ accessors (fill (0 :: Double))
pythonTraj :: forall x . (Vectorize x, Lookup (x Double))
=> String -> [String] -> [x Double] -> State PythonExporter ()
pythonTraj pyRetName topNames xs = mapM_ pyArray at'
where
pyArray :: ([String], (x Double -> Double)) -> State PythonExporter ()
pyArray (name, get) = putVal pyRetName (topNames ++ name) (npArray (show (map get xs)))
at' :: [([String], x Double -> Double)]
at' = map (\(fn,g,_) -> (fn, toDub g)) $ flatten' $ accessors (fill (0 :: Double))
matlabParam :: forall p . (Vectorize p, Lookup (p Double)) => String -> p Double -> [String]
matlabParam topName p = map (uncurry mlParam) at
where
mlParam :: String -> (p Double -> Double) -> String
mlParam name get = topName ++ "." ++ name ++ " = " ++ show (get p) ++ ";"
at :: [(String, p Double -> Double)]
at = map (\(fn,g,_) -> (fn, toDub g)) $ flatten $ accessors (fill (0 :: Double))
matlabTraj :: forall x . (Vectorize x, Lookup (x Double)) => String -> [x Double] -> [String]
matlabTraj topName xs = map (uncurry mlArray) at
where
mlArray :: String -> (x Double -> Double) -> String
mlArray name get =
topName ++ "." ++ name ++ " = " ++ show (map get xs) ++ ";"
at :: [(String, x Double -> Double)]
at = map (\(fn,g,_) -> (fn, toDub g)) $ flatten $ accessors (fill (0 :: Double))
data PythonExporter = PythonExporter (S.Set [String], [String])
pyname :: String -> [String] -> String
pyname topName xs = topName ++ concatMap (\x -> "['" ++ x ++ "']") xs
putNameIfMissing :: String -> [String] -> State PythonExporter ()
putNameIfMissing _ [] = return ()
putNameIfMissing topName name = do
PythonExporter (set0, _) <- State.get
unless (S.member name set0) $ do
putNameIfMissing topName (init name)
PythonExporter (set1, out1) <- State.get
State.put $ PythonExporter (S.insert name set1, (pyname topName name ++ " = {}") : out1)
write :: String -> State PythonExporter ()
write str = do
PythonExporter (set0, outs0) <- State.get
State.put $ PythonExporter (set0, str:outs0)
putVal :: String -> [String] -> String -> State PythonExporter ()
putVal topName name val = do
putNameIfMissing topName name
write (pyname topName name ++ " = " ++ val)