packages feed

dynobud-1.7.1.0: src/Dyno/SimpleOcp.hs

{-# OPTIONS_GHC -Wall #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Dyno.SimpleOcp
       ( SimpleOcp(..)
       , S
       , solveOcp
       ) where

import GHC.Generics ( Generic, Generic1 )

import qualified Data.Foldable as F
import Data.Proxy
import Data.Vector ( Vector )

import Accessors ( Lookup )
import Casadi.SX ( SX )

import Dyno.TypeVecs
import Dyno.Ocp
import Dyno.Solvers
import Dyno.NlpUtils
import Dyno.Nlp
import Dyno.DirectCollocation.Formulate
import Dyno.DirectCollocation.Types
import Dyno.DirectCollocation.Quadratures
import Dyno.Vectorize ( Vectorize(..), Tuple(..), Id, None(..), fill, vzipWith )
import Dyno.View.View -- ( View(..) )
import Dyno.View.JV
import Dyno.View.JVec

-- | scalar symbolic type
newtype S = S {unS :: J (JV Id) SX} deriving (Num, Fractional, Floating)

data SimpleOcp x u =
  SimpleOcp
  { ode :: x S -> u S -> x S
  , objective :: x S -> u S -> S
  , xBounds :: x (Double, Double)
  , uBounds :: u (Double, Double)
  , xInitial :: x Double
  , xFinal :: x Double
  , endTime :: Double
  , initialGuess :: Double -> x Double
  }

vminus :: (Vectorize f, Num a) => f a -> f a -> f a
vminus = vzipWith (-)

dot :: (Vectorize f, Num a) => f a -> f a -> a
dot x y = F.sum $ vectorize $ vzipWith (*) x y

toOcp :: (Vectorize x, Vectorize u)
         => SimpleOcp x u
         -> OcpPhase (Tuple x u) None u None (Tuple x u) None (SimpleBc x) None None None None None
toOcp simple =
  OcpPhase
  { ocpMayer = \_ _ _ _ _ _ -> 0
  , ocpLagrange = \(Tuple x u) _ u' _ _ _ _ _ -> 1e-9 * (u' `dot` u')  + unS (objective simple (fmap S x) (fmap S u))
  , ocpQuadratures = \_ _ _ _ _ _ _ _ -> None
  , ocpQuadratureOutputs = \_ _ _ _ _ _ _ _ -> None
  , ocpDae = \(Tuple xd ud) (Tuple x u) _ u' _ _ _ ->
     let r = Tuple (xd `vminus` x') (ud `vminus` u')
         x' = fmap unS $ ode simple (fmap S x) (fmap S u)
     in (r, None)
  , ocpBc = \(Tuple x0 _) (Tuple xf _) _ _ _ _ ->SimpleBc x0 xf
  , ocpPathC = \_ _ _ _ _ _ _ -> None
  , ocpPlotOutputs = \_ _ _ _ _ _ _ _ _ _ _ -> None
  , ocpObjScale      = Nothing
  , ocpTScale        = Nothing
  , ocpXScale        = Nothing
  , ocpZScale        = Nothing
  , ocpUScale        = Nothing
  , ocpPScale        = Nothing
  , ocpResidualScale = Nothing
  , ocpBcScale       = Nothing
  , ocpPathCScale    = Nothing
  }

data SimpleBc x a = SimpleBc (x a) (x a) deriving (Functor, Generic, Generic1)
instance Vectorize x => Vectorize (SimpleBc x)
instance Lookup (x a) => Lookup (SimpleBc x a)

toOcpInputs :: (Vectorize x, Vectorize u) => SimpleOcp x u -> OcpPhaseInputs (Tuple x u) None u None (SimpleBc x) None None
toOcpInputs simple =
  OcpPhaseInputs
  { ocpBcBnds = SimpleBc
                (fmap (\x -> (Just x, Just x)) (xInitial simple))
                (fmap (\x -> (Just x, Just x)) (xFinal simple))
  , ocpPathCBnds = None
  , ocpXbnd = fmap toBounds $ Tuple (xBounds simple) (uBounds simple)
  , ocpUbnd = fill (Nothing, Nothing)
  , ocpZbnd = None
  , ocpPbnd = None
  , ocpTbnd = (Just (endTime simple), Just (endTime simple))
  , ocpFixedP = None
  }
  where
    toBounds (lb,ub) = (Just lb, Just ub)

solveOcp :: (Vectorize x, Vectorize u) => SimpleOcp x u -> IO (Either String [(x Double, u Double)])
solveOcp simple = reifyDim deg $ reifyDim n $ solveOcp' simple
  where
    n = 50
    deg = 2

solver :: Solver
solver = ipoptSolver

solveOcp' ::
  forall x u n deg
  . (Vectorize x, Vectorize u, Dim deg, Dim n)
  => SimpleOcp x u -> Proxy n -> Proxy deg -> IO (Either String [(x Double, u Double)])
solveOcp' simple _ _ = do
  let ocp = toOcp simple
      ocpInputs = toOcpInputs simple
      tf = endTime simple
      roots = Legendre
      guess :: CollTraj (Tuple x u) None u None n deg (Vector Double)
      guess = makeGuess roots tf (\t -> Tuple (initialGuess simple t) (fill 0)) (const None) (const (fill 0)) None
  cp <- makeCollProblem roots ocp ocpInputs (cat guess)
  let _ = cp :: CollProblem (Tuple x u) None u None (Tuple x u) None (SimpleBc x) None None None None None n deg
  (emsg, opt) <- solveNlp solver (cpNlp cp) Nothing
  case emsg of
    Left msg -> return (Left msg)
    Right _ -> do
      let CollTraj _ _ stages' xf' = split (xOpt opt)
          xs = map ((\(CollStage x _) -> splitJV x) . split) $ F.toList $ unJVec (split stages')
      return $ Right $ map (\(Tuple x u) -> (x, u)) (xs ++ [splitJV xf'])