Использование Monad/ST для непараллельной передачи сообщений в изменчивом графе
Я пытаюсь разработать структуру данных для следующей ситуации.
Структура графика
Я планирую иметь граф узлов с невзвешенными, направленными ребрами: Graph = [Node]
Каждый узел имеет:
- Некоторые TBD внутреннего (постоянного) состояния
- Очередь входящих сообщений
- Тип сообщения, которое он может отправить, определяется функцией, которая принимает текущее состояние узла (с возможностью сбоя)
- Список ребер
Node { nodeState :: NodeState, inbox :: Queue NodeMessage, nodeMessage :: (NodeState -> Maybe NodeMessage), connections::[NodeEdge] }
Каждое ребро является промежуточным этапом захвата ожидающих сообщений для целевого узла.
NodeEdge { pendingMessage:: Maybe NodeMessage, targetNode :: Node }
Передача сообщений
Передача сообщений происходит поэтапно и не является одновременной (хотя очереди могут обрабатываться параллельно для сокращения времени вычислений).
- Этап 1. Проверьте входящие сообщения каждого узла, обрабатывая любые сообщения и обновляя
NodeState
если актуально. - Этап 2. Запустите каждый узел
nodeMessage
, если это приводит кJust NodeMessage
отправьте NodeMessage для каждого соединения ([NodeEdge]
) - Этап 3: Проверьте каждый край узла, если у него есть сообщение, добавьте его в очередь сообщений целевого узла.
Monat / ST
Мой первоначальный план состоял в том, чтобы назначить каждому узлу идентификатор (возможно, простое Int) и сохранить каждый узел в узле Map Int. Я не пробовал ST Monad раньше, но я решил, что могу использовать что-то вроде ST s (M.Map Int Node)
, Для любой заданной фазы активность отправки сообщения каждого узла может быть обработана в O(k log N).
С другой стороны, если узлы / ребра смогли обновить изменяемое состояние своих ребер / узлов, то любая O-очередь могла быть обработана в O(k).
В то время как метод ST/Map кажется довольно интуитивно понятным, иметь изменяемость всего графика мне не по силам.
Любые предложения / советы / рекомендуемое чтение?
1 ответ
Я не собираюсь отмечать этот ответ как правильный, потому что он не дает правильного ответа на вопрос. Однако это решение, с которым я иду.
Поскольку число узлов в моем графике никогда не изменится, я понял, что могу использовать массив. Я на самом деле пересматриваю использование изменяемого типа данных - хотя я получаю намного более простой рабочий процесс обновления массива, я получаю меньше преимуществ от лени, и в итоге я пишу тонну кода императивного стиля. Я на самом деле думаю об использовании Array и State Monad, а не ST.
Вот немного тестового кода, который я написал, используя STArray. "Правильный" ответ на этот вопрос будет подобным типом данных специально для Graphs - возможно, есть библиотека STGraph?
В любом случае - вот пример кода с использованием STArray:
import Control.Monad.ST
import Data.Array.ST
import Data.Array
import qualified Data.Dequeue as DQ
type Id = Int
data Node = Node {
nodeId :: Id,
nodeState :: NodeState,
nodeInbox :: DQ.BankersDequeue NodeMessage,
nodeMessage :: (NodeState -> Maybe NodeMessage),
connections :: [NodeEdge] }
instance Show Node where
show x = "Node: " ++ (show . nodeId $ x) ++ " :: Inbox: " ++ (show . nodeInbox $ x) ++ " :: " ++ (show . connections $ x)
data NodeEdge = NodeEdge { pendingMessage:: Maybe NodeMessage, targetNode :: Id } deriving Show
data NodeState = NodeState { stateLevel :: Int } deriving Show
data NodeMessage = NodeMessage { value :: Int } deriving Show
es = [[NodeEdge Nothing 1,NodeEdge Nothing 2],[NodeEdge Nothing 0,NodeEdge Nothing 2],[NodeEdge Nothing 0,NodeEdge Nothing 1]]
ns = take 3 $ map (\x -> Node x (NodeState 0) (DQ.fromList []) (\_ -> Nothing) (es !! x)) $ [0,1..]
testArray :: Array Int Node
testArray = listArray (0,2) ns
testSTarr = do arr <- newListArray (0,2) ns :: ST s (STArray s Int Node)
a <- readArray arr 1
let i = targetNode . head $ connections a
b <- readArray arr i
let m = NodeMessage 2
ms = DQ.pushBack (nodeInbox b) m
b' = b { nodeInbox = ms }
writeArray arr (nodeId b) b'
return arr
testSTarr' x = do a <- readArray x 0
return a
bp = testSTarr >>= testSTarr'
main = do
print $ runST bp
return ()