LTS-0.1.0.0: src/Data/LTS.hs
{- |
Module : Data.LTS
Description : A library that models labelled transition system in Haskell
Copyright : Copyright (c) 2020 Ajay Kumar Eeralla
Maintainer : ajay.eeralla@gmail.com
Stability : experimental
Portability : portable
This module implements a labelled transition system
-}
{-# LANGUAGE DeriveGeneric #-}
module Data.LTS
( LTSState (..)
, Transition (..)
, LTS
, checkTrans
, getFromIds
, getToIds
, sortById
, sortByToSt
, sortByFromSt
, collectTrans
, getStartSt
, getFinalSt
, depth
, Alphabet
, findTransIndex
, transExists
)
where
import Data.Nat
import GHC.Generics
import Data.List (sortBy)
import Data.Ord (comparing)
-- | LTSState is a record type which may hold id, output, etc.
data LTSState a =
LTSState {stateId::Int
, out::a
}
deriving (Read, Show, Eq, Generic)
-- | Define Ord instance by id
instance (Eq a) => Ord (LTSState a) where
compare = comparing stateId
-- | Transition models that on a LTSState, given input symbol from an alphabet [b],
-- | takes to the next LTSState
data Transition a b =
Transition { transitionFrom::LTSState a
, transitionGuard::b
, transitionTo::LTSState a
}
deriving (Read, Show, Eq, Generic)
-- | Define Ord instance for `Transition`
instance (Eq a, Eq b) => Ord (Transition a b) where
compare = comparing transitionFrom
-- | Alphabet is a generic list
type Alphabet b = [b]
-- | LTS is a list of `Transition`
type LTS a b = [Transition a b]
-- | Check if transition exists from a given symbol from `Alphabet` and `LTSState`
transExists :: (Eq a, Eq b) => LTSState a -> b -> LTS a b -> Bool
transExists st x (t:ts) =
(transitionFrom t == st && transitionGuard t == x) || transExists st x ts
-- | Return the index of the `Transition` that current state can take on input symbol
findTransIndex :: (Eq a, Eq b) => LTSState a -> b -> LTS a b -> Int
findTransIndex st x (t:ts) =
if transExists st x (t:ts)
then index
else error "doesn't exist"
where index = if transitionFrom t == st && transitionGuard t == x
then 0
else 1+ findTransIndex st x ts
-- | Return the next state given the input symbol on current `LTSState`
nextStateSymbol :: (Eq a, Eq b) => LTSState a -> b -> LTS a b -> LTSState a
nextStateSymbol s x ts =
if transExists s x ts
then transitionTo (ts !! findTransIndex s x ts)
else s
-- | LTS execution on a given state and `Alphabet`
nextStateAlphabet :: (Eq a, Eq b) => LTSState a -> Alphabet b -> LTS a b -> LTSState a
nextStateAlphabet s (x:xs) ts =
nextStateAlphabet (nextStateSymbol s x ts) xs ts
-- | Check if the set of transitions has same origin
checkTrans :: (Eq a, Eq b) => LTSState a -> LTS a b -> Bool
checkTrans st (t:ts) =
stateId st == stateId (transitionFrom t) && checkTrans st ts
-- | Sorting related functions
-- | Get origin `LTSState` ids
getFromIds:: (Eq a, Eq b) => LTS a b -> [Int]
getFromIds = map (stateId . transitionFrom)
-- | Get final `LTSState` ids
getToIds :: (Eq a, Eq b) => LTS a b -> [Int]
getToIds = map (stateId . transitionTo)
-- | Sort `LTSState`s by Id
sortById :: (Eq a) => [LTSState a] -> [LTSState a]
sortById = sortBy (comparing stateId)
-- | Sort transitions by from `LTSState`
sortByFromSt :: (Eq a, Eq b) => LTS a b -> LTS a b
sortByFromSt = sortBy (comparing transitionFrom)
-- | Sort transitions by to `LTSState`
sortByToSt :: (Eq a, Eq b) => LTS a b -> LTS a b
sortByToSt = sortBy (comparing transitionTo)
-- | Compute set of transitions (that can be ordered using a flag b) from a given `LTSState`
collectTrans:: (Eq a, Eq b) => LTSState a -> LTS a b -> Bool -> LTS a b
collectTrans st (t:ts) b =
let op = if stateId st == stateId (transitionFrom t)
then t:collectTrans st ts b
else collectTrans st ts b
in if b then sortByToSt op else op
-- | Get the start `LTSState`
getStartSt :: (Eq a, Eq b) => LTS a b -> LTSState a
getStartSt ts = transitionFrom (head (sortByToSt (sortByFromSt ts)))
-- | Get the final `LTSState`
getFinalSt :: (Eq a, Eq b) => LTS a b -> LTSState a
getFinalSt ts = transitionTo (head (sortByToSt (sortByFromSt ts)))
-- | Compute depth of a transition system which is the longest simple path
-- | from the start state to a final state
depth :: (Eq a, Eq b) => LTS a b -> LTSState a -> Nat
depth [] _ = 0
depth (t:ts) st
| transitionFrom t == st && (transitionFrom t /= transitionTo t) =
1 + depth ts (transitionTo t)
| otherwise = depth ts st