Blobs-0.1: src/Network.hs
module Network
(
-- * Types
Network, Node, Edge
, NodeNr, EdgeNr, ViaNr
, networkNodes -- dangerous
, networkEdges -- dangerous
-- * Creating and printing a network
, Network.empty
, dumpNetwork
, getNodeNrs
, getNodeAssocs, setNodeAssocs
, getEdgeAssocs, setEdgeAssocs
, getCanvasSize, setCanvasSize
, getPalette, setPalette
, getGlobalInfo, setGlobalInfo
, getNode
, getEdge
, getNodes
, getEdges
, getChildren
, getParents
, getParentMap, ParentMap
, nodeExists, edgeExists
, findEdge, findNodeNrsByName
, updateNode
, updateEdge
, updateVia
, mapNodeNetwork
, addNode, addNodes, removeNode, addNodeEx
, addEdge, addEdges, removeEdge, addEdgeWithPorts
, removeAllEdges
, newViaEdge, removeVia
, constructNode
, getNodeInfo, getNodeName, getNodePosition, getNodeNameAbove, getNodeShape
, setNodeInfo, setNodeName, setNodePosition, setNodeNameAbove, setNodeShape
, getNodeArity
, setNodeArity
, getInfo, getName, getPosition, getNameAbove, getShape, getArity
, setInfo, setName, setPosition, setNameAbove, setShape, setArity
, constructEdge
, getEdgeFrom, getEdgeTo, getEdgeVia, getEdgeInfo
, setEdgeFrom, setEdgeTo, setEdgeVia, setEdgeInfo
, getEdgeFromPort, getEdgeToPort
, setEdgeFromPort, setEdgeToPort
) where
import Common
import Math
import InfoKind
import Shape
import Palette hiding (delete)
import qualified Data.IntMap as IntMap -- hiding (map)
data Network g n e = Network
{ networkNodes :: !(IntMap.IntMap (Node n)) -- ^ maps node numbers to nodes
, networkEdges :: !(IntMap.IntMap (Edge e)) -- ^ maps edge numbers to edges
, networkPalette :: Palette n
, networkCanvasSize :: (Double, Double)
, networkInfo :: g
} deriving Show
data Edge e = Edge
{ edgeFrom :: !NodeNr -- ^ the number of the node where the edge starts
, edgeTo :: !NodeNr -- ^ the number of the node the edge points to
, edgeVia :: [DoublePoint] -- ^ intermediate vertices when drawing
, edgeInfo :: e
, edgeFromPort :: !PortNr -- ^ the connection port on the 'from' node
, edgeToPort :: !PortNr -- ^ the connection port on the 'to' node
} deriving (Show, Read, Eq)
data Node n = Node
{ nodePosition :: DoublePoint -- ^ the position of the node on screen
, nodeName :: !String
, nodeNameAbove :: Bool -- ^ should the name be displayed above (True) of below (False)
, nodeShape :: Either String Shape -- ^ name from palette, or shape
, nodeInfo :: n
, nodeArity :: Maybe (PortNr,PortNr) -- ^ number of in/out connection ports
} deriving (Show, Read)
type NodeNr = Int
type EdgeNr = Int
type ViaNr = Int
type PortNr = Int
-- | Create an empty network
empty :: (InfoKind n g, InfoKind e g) => g -> n -> e -> Network g n e
empty g _ _ = Network
{ networkNodes = IntMap.empty
, networkEdges = IntMap.empty
, networkPalette = Palette.empty
, networkCanvasSize = (15, 9)
, networkInfo = g
}
-- | Map a function over the nodes, possibly changes the type
-- of the Network (i.e. the kind of values stored in the
-- probability tables)
mapNodeNetwork :: InfoKind m g =>
(Node n->Node m) -> Network g n e -> Network g m e
mapNodeNetwork nodeFun network =
let numberedNodes = getNodeAssocs network
newNodes = [ (nr, nodeFun node) | (nr, node) <- numberedNodes ]
in Network
{ networkNodes = IntMap.fromList newNodes
, networkEdges = networkEdges network
, networkPalette = fmap (const blank) $ networkPalette network
, networkCanvasSize = networkCanvasSize network
, networkInfo = networkInfo network
}
constructEdge :: NodeNr -> PortNr -> NodeNr -> PortNr
-> [DoublePoint] -> e -> Edge e
constructEdge fromNr fromPort toNr toPort via info =
Edge
{ edgeFrom = fromNr
, edgeTo = toNr
, edgeVia = via
, edgeInfo = info
, edgeFromPort = fromPort
, edgeToPort = toPort
}
getEdgeFrom :: Edge e -> NodeNr
getEdgeFrom = edgeFrom
getEdgeFromPort :: Edge e -> PortNr
getEdgeFromPort = edgeFromPort
getEdgeTo :: Edge e -> NodeNr
getEdgeTo = edgeTo
getEdgeToPort :: Edge e -> PortNr
getEdgeToPort = edgeToPort
getEdgeVia :: Edge e -> [DoublePoint]
getEdgeVia = edgeVia
getEdgeInfo :: Edge e -> e
getEdgeInfo = edgeInfo
setEdgeFrom :: NodeNr -> Edge e -> Edge e
setEdgeFrom fromNr edge = edge { edgeFrom = fromNr }
setEdgeFromPort :: PortNr -> Edge e -> Edge e
setEdgeFromPort fromPortNr edge = edge { edgeFromPort = fromPortNr }
setEdgeTo :: NodeNr -> Edge e -> Edge e
setEdgeTo toNr edge = edge { edgeTo = toNr }
setEdgeToPort :: PortNr -> Edge e -> Edge e
setEdgeToPort toPortNr edge = edge { edgeToPort = toPortNr }
setEdgeVia :: [DoublePoint] -> Edge e -> Edge e
setEdgeVia via edge = edge { edgeVia = via }
setEdgeInfo :: e -> Edge oldInfo -> Edge e
setEdgeInfo info edge = edge { edgeInfo = info }
constructNode :: (InfoKind n g) =>
String -> DoublePoint -> Bool
-> Either String Shape -> n -> Maybe (PortNr,PortNr) -> Node n
constructNode name position nameAbove shape info arity =
Node
{ nodeName = name
, nodePosition = position
, nodeNameAbove = nameAbove
, nodeShape = shape
, nodeInfo = info
, nodeArity = arity
}
getNodeName :: Network g n e -> NodeNr -> String
getNodeName network nodeNr = nodeName (networkNodes network IntMap.! nodeNr)
setNodeName :: NodeNr -> String -> Network g n e -> Network g n e
setNodeName nodeNr name network =
network { networkNodes = IntMap.insert nodeNr (node { nodeName = name }) (networkNodes network) }
where node = networkNodes network IntMap.! nodeNr
getNodePosition :: Network g n e -> NodeNr -> DoublePoint
getNodePosition network nodeNr = nodePosition (networkNodes network IntMap.! nodeNr)
setNodePosition :: NodeNr -> DoublePoint -> Network g n e -> Network g n e
setNodePosition nodeNr position network =
network { networkNodes = IntMap.insert nodeNr (node { nodePosition = position }) (networkNodes network) }
where node = networkNodes network IntMap.! nodeNr
getNodeNameAbove :: Network g n e -> NodeNr -> Bool
getNodeNameAbove network nodeNr = nodeNameAbove (networkNodes network IntMap.! nodeNr)
setNodeNameAbove :: NodeNr -> Bool -> Network g n e -> Network g n e
setNodeNameAbove nodeNr nameAbove network =
network { networkNodes = IntMap.insert nodeNr (node { nodeNameAbove = nameAbove }) (networkNodes network) }
where node = networkNodes network IntMap.! nodeNr
getNodeShape :: Network g n e -> NodeNr -> Either String Shape
getNodeShape network nodeNr = nodeShape (networkNodes network IntMap.! nodeNr)
setNodeShape :: NodeNr -> Either String Shape -> Network g n e -> Network g n e
setNodeShape nodeNr shape network =
network { networkNodes = IntMap.insert nodeNr (node { nodeShape = shape })
(networkNodes network) }
where node = networkNodes network IntMap.! nodeNr
getNodeInfo :: Network g n e -> NodeNr -> n
getNodeInfo network nodeNr = nodeInfo (networkNodes network IntMap.! nodeNr)
setNodeInfo :: NodeNr -> n -> Network g n e -> Network g n e
setNodeInfo nodeNr info network =
network { networkNodes = IntMap.insert nodeNr (node { nodeInfo = info }) (networkNodes network) }
where node = networkNodes network IntMap.! nodeNr
getNodeArity :: Network g n e -> NodeNr -> Maybe (PortNr,PortNr)
getNodeArity network nodeNr = nodeArity (networkNodes network IntMap.! nodeNr)
setNodeArity :: NodeNr -> Maybe (PortNr,PortNr) -> Network g n e
-> Network g n e
setNodeArity nodeNr arity network =
network { networkNodes = IntMap.insert nodeNr (node { nodeArity = arity })
(networkNodes network) }
where node = networkNodes network IntMap.! nodeNr
getNameAbove :: Node a -> Bool
getNameAbove node = nodeNameAbove node
getName :: Node a -> String
getName node = nodeName node
getShape :: Node a -> Either String Shape
getShape node = nodeShape node
getPosition :: Node a -> DoublePoint
getPosition node = nodePosition node
getInfo :: Node a -> a
getInfo node = nodeInfo node
getArity :: Node a -> Maybe (PortNr,PortNr)
getArity node = nodeArity node
-- | Set whether the name should appear above (True) or below (False) the node
setNameAbove :: Bool -> Node a -> Node a
setNameAbove above node = node { nodeNameAbove = above }
setName :: String -> Node a -> Node a
setName name node = node { nodeName = name }
setShape :: Either String Shape -> Node a -> Node a
setShape s node = node { nodeShape = s }
setPosition :: DoublePoint -> Node a -> Node a
setPosition position node = node { nodePosition = position }
setInfo :: a -> Node a -> Node a
setInfo info node = node { nodeInfo = info }
setArity :: Maybe (PortNr,PortNr) -> Node a -> Node a
setArity arity node = node { nodeArity = arity }
-- | Get the next unused node number
getUnusedNodeNr :: Network g n e -> NodeNr
getUnusedNodeNr network | null used = 1
| otherwise = maximum used + 1
where
used = IntMap.keys (networkNodes network)
-- | Get the next unused edge number
getUnusedEdgeNr :: Network g n e -> EdgeNr
getUnusedEdgeNr network | null used = 1
| otherwise = maximum used + 1
where
used = IntMap.keys (networkEdges network)
-- | Get the node numbers of the parents of a given node
getParents :: Network g n e -> NodeNr -> [NodeNr]
getParents network child =
[ parent
| edge <- getEdges network
, edgeTo edge == child
, let parent = edgeFrom edge
]
type ParentMap = IntMap.IntMap [NodeNr]
-- | getParents is quite expensive (see above) and so
-- we store the parent relationship in an IntMap
getParentMap :: Network g n e -> ParentMap
getParentMap network =
IntMap.fromList
[ (nodeNr, getParents network nodeNr)
| nodeNr <- getNodeNrs network
]
-- | Get the node numbers of the children of a given node
getChildren :: Network g n e -> NodeNr -> [NodeNr]
getChildren network parent =
[ child
| edge <- getEdges network
, edgeFrom edge == parent
, let child = edgeTo edge
]
-- | Get node with given index, raises exception if node number does not exist
getNode :: NodeNr -> Network g n e -> Node n
getNode nodeNr network
| IntMap.member nodeNr nodesMap = nodesMap IntMap.! nodeNr
| otherwise = internalError "Network" "getNode" "illegal node number"
where
nodesMap = networkNodes network
-- | Get edge with given index, raises exception if edge number does not exist
getEdge :: EdgeNr -> Network g n e -> Edge e
getEdge edgeNr network = networkEdges network IntMap.! edgeNr
-- | Get all of the nodes in the network
getNodes :: Network g n e -> [Node n]
getNodes network = IntMap.elems (networkNodes network)
-- | Get all of the edges in the network
getEdges :: Network g n e -> [Edge e]
getEdges network = IntMap.elems (networkEdges network)
-- | Get all of the node numbers in the network
getNodeNrs :: Network g n e -> [NodeNr]
getNodeNrs network = IntMap.keys (networkNodes network)
getPalette :: Network g n e -> Palette n
getPalette network = networkPalette network
getCanvasSize :: Network g n e -> (Double, Double)
getCanvasSize network = networkCanvasSize network
getGlobalInfo :: Network g n e -> g
getGlobalInfo network = networkInfo network
-- | Find the number of an edge given start and end node number
findEdge :: NodeNr -> NodeNr -> Network g n e -> Maybe EdgeNr
findEdge fromNodeNr toNodeNr network =
let hits = IntMap.filter
(sameFromAndTo (Edge { edgeFrom = fromNodeNr
, edgeTo = toNodeNr
, edgeVia = undefined
, edgeInfo = undefined
, edgeFromPort = 0
, edgeToPort = 0 }))
(networkEdges network)
in case IntMap.keys hits of
[key] -> Just key
_ -> Nothing
-- | Find node numbers given a node name
findNodeNrsByName :: String -> Network g n e -> [NodeNr]
findNodeNrsByName theNodeName network =
[ nodeNr
| nodeNr <- getNodeNrs network
, getNodeName network nodeNr == theNodeName
]
-- | Get a list of pairs where each pair contains a node number and the corresponding node
getNodeAssocs :: Network g n e -> [(NodeNr, Node n)]
getNodeAssocs network = IntMap.assocs (networkNodes network)
setNodeAssocs :: [(NodeNr, Node n)] -> Network g n e -> Network g n e
setNodeAssocs nodeAssocs network =
network { networkNodes = IntMap.fromList nodeAssocs }
-- | Get a list of pairs where each pair contains a edge number and the corresponding edge
getEdgeAssocs :: Network g n e -> [(EdgeNr, Edge e)]
getEdgeAssocs network = IntMap.assocs (networkEdges network)
setEdgeAssocs :: [(EdgeNr, Edge e)] -> Network g n e -> Network g n e
setEdgeAssocs edgeAssocs network =
network { networkEdges = IntMap.fromList edgeAssocs }
-- | Create a string that describes the network
dumpNetwork :: InfoKind e g => Network g String e -> String
dumpNetwork network = show (getNodeAssocs network) ++ "\n" ++ show (getEdgeAssocs network)
-- | Test for existence of a node number
nodeExists :: NodeNr -> Network g n e -> Bool
nodeExists nodeNr network =
IntMap.member nodeNr (networkNodes network)
-- | Test for existence of an edge number
edgeExists :: EdgeNr -> Network g n e -> Bool
edgeExists edgeNr network =
IntMap.member edgeNr (networkEdges network)
{-----------------------------------
Functions that change the network
-----------------------------------}
-- | Add a node to the network
addNode :: InfoKind n g
=> Network g n e -- ^ the network to add the node to
-> (NodeNr, Network g n e) -- ^ the number of the new node and
-- the extended network
addNode network =
addNodeEx ("Node " ++ show nodeNr)
(DoublePoint 0.0 0.0)
True
(Right Shape.circle)
blank
Nothing
network
where
nodeNr = getUnusedNodeNr network
addNodes :: InfoKind n g => Int -> Network g n e -> ([NodeNr], Network g n e)
addNodes 0 network = ([], network)
addNodes n network1 =
let (nodeNr, network2) = addNode network1
(nodeNrs, network3) = addNodes (n-1) network2
in (nodeNr:nodeNrs, network3)
addNodeEx :: InfoKind n g =>
String -> DoublePoint -> Bool -> Either String Shape -> n
-> Maybe (PortNr,PortNr)
-> Network g n e -> (NodeNr, Network g n e)
addNodeEx name position labelAbove shape info arity network =
( nodeNr
, network { networkNodes = IntMap.insert nodeNr node (networkNodes network) }
)
where
nodeNr = getUnusedNodeNr network
node = constructNode name position labelAbove shape info arity
-- | Add an edge to the network.
addEdge :: InfoKind e g => NodeNr -> NodeNr -> Network g n e -> Network g n e
addEdge fromNodeNr toNodeNr network
| any (sameFromAndTo edge) edgesList || -- prohibit double edges
any (sameFromAndTo (reverseEdge edge)) edgesList = -- prohibit edges in opposite direction
network
| otherwise =
let edgeNr = getUnusedEdgeNr network
in setNodeArity fromNodeNr (updateFromArity fromArity) $
setNodeArity toNodeNr (updateToArity toArity) $
network { networkEdges = IntMap.insert edgeNr edge (networkEdges network) }
where
edge = constructEdge fromNodeNr fromPortNr toNodeNr toPortNr [] blank
edgesList = IntMap.elems (networkEdges network)
fromArity = getNodeArity network fromNodeNr
toArity = getNodeArity network toNodeNr
fromPortNr = 1 + (maybe 0 snd $ fromArity)
toPortNr = 1 + (maybe 0 fst $ toArity)
updateFromArity Nothing = Just (0,1)
updateFromArity (Just (n,m)) = Just (n,m+1)
updateToArity Nothing = Just (1,0)
updateToArity (Just (n,m)) = Just (n+1,m)
-- | Add an edge to the network, with specific connection ports.
addEdgeWithPorts :: InfoKind e g =>
NodeNr -> PortNr -> NodeNr -> PortNr
-> Network g n e -> Network g n e
addEdgeWithPorts fromNodeNr fromPortNr toNodeNr toPortNr network
| any (sameFromAndTo edge) edgesList || -- prohibit double edges
any (sameFromAndTo (reverseEdge edge)) edgesList = -- prohibit edges in opposite direction
network
| otherwise =
let edgeNr = getUnusedEdgeNr network
networkPlusEdge = network { networkEdges = IntMap.insert edgeNr edge (networkEdges network) }
in networkPlusEdge
where
edge = constructEdge fromNodeNr fromPortNr toNodeNr toPortNr [] blank
-- edge = Edge { edgeFrom = fromNodeNr, edgeTo = toNodeNr, edgeVia = []
-- , edgeInfo = blank, edgeFromPort = fromPortNr
-- , edgeToPort = toPortNr }
edgesList = IntMap.elems (networkEdges network)
addEdges :: InfoKind e g => [(NodeNr,NodeNr)] -> Network g n e -> Network g n e
addEdges edgeTuples network =
foldr (\(fromNr, toNr) net -> addEdge fromNr toNr net) network edgeTuples
-- | Insert a new 'via' control point in the middle of an edge.
newViaEdge :: EdgeNr -> ViaNr -> DoublePoint
-> Network g n e -> Network g n e
newViaEdge edgeNr viaNr point network =
network { networkEdges = IntMap.adjust (\e->e{ edgeVia= take viaNr (edgeVia e)
++[point]
++drop viaNr (edgeVia e) })
edgeNr
(networkEdges network) }
-- | Remove node with given index, raises exception if node number does not exist.
-- This function also removes all edges that start or end in this node.
removeNode :: NodeNr -> Network g n e -> Network g n e
removeNode nodeNr network =
let involvedEdges = [ i
| (i, edge) <- getEdgeAssocs network
, edgeFrom edge == nodeNr || edgeTo edge == nodeNr
]
networkWithoutEdges = foldr removeEdge network involvedEdges
networkWithoutNode = networkWithoutEdges { networkNodes = IntMap.delete nodeNr (networkNodes networkWithoutEdges) }
in networkWithoutNode
-- | Remove an edge from the network. The probability table of the target node is updated:
-- the corresponding dimension is removed and all values are zeroed.
-- An exception is raised if edge number does not exist.
removeEdge :: EdgeNr -> Network g n e -> Network g n e
removeEdge edgeNr network =
setNodeArity fromNodeNr (Just (fi,fo-1)) $
setNodeArity toNodeNr (Just (ti-1,to)) $
network { networkEdges = IntMap.delete edgeNr (networkEdges network) }
where
(fi,fo) = maybe (0,1) id $ getNodeArity network fromNodeNr
(ti,to) = maybe (1,0) id $ getNodeArity network toNodeNr
edge = getEdge edgeNr network
fromNodeNr = getEdgeFrom edge
toNodeNr = getEdgeTo edge
-- | Remove all edges from the network. The probability tables of all node are zeroed.
removeAllEdges :: Network g n e -> Network g n e
removeAllEdges network =
let networkWithoutEdges = network { networkEdges = IntMap.empty }
in networkWithoutEdges
-- | Remove a control point from an edge.
removeVia :: EdgeNr -> ViaNr -> Network g n e -> Network g n e
removeVia edgeNr viaNr network =
let remove n e = e { edgeVia = take n (edgeVia e)
++ drop (n+1) (edgeVia e) } in
network { networkEdges = IntMap.adjust (remove viaNr)
edgeNr (networkEdges network) }
setPalette :: Palette n -> Network g n e -> Network g n e
setPalette palette network = network { networkPalette = palette }
setCanvasSize :: (Double, Double) -> Network g n e -> Network g n e
setCanvasSize canvasSize network = network { networkCanvasSize = canvasSize }
setGlobalInfo :: g -> Network g n e -> Network g n e
setGlobalInfo info network = network { networkInfo = info }
{-----------------------------------
Local functions
-----------------------------------}
sameFromAndTo :: Edge e -> Edge e -> Bool
sameFromAndTo edge1 edge2 =
edgeFrom edge1 == edgeFrom edge2 && edgeTo edge1 == edgeTo edge2
reverseEdge :: Edge e -> Edge e
reverseEdge edge =
edge { edgeFrom = edgeTo edge, edgeTo = edgeFrom edge }
-- | Update node with given number by applying the function to it
-- Dangerous (wrt network consistency, do not export)
updateNode :: NodeNr -> (Node n -> Node n) -> Network g n e -> Network g n e
updateNode nodeNr nodeFunction network =
let node = getNode nodeNr network in
network { networkNodes = IntMap.insert nodeNr (nodeFunction node)
(networkNodes network) }
updateEdge :: EdgeNr -> (Edge e -> Edge e) -> Network g n e -> Network g n e
updateEdge edgeNr edgeFunction network =
network { networkEdges = IntMap.adjust edgeFunction edgeNr
(networkEdges network) }
updateVia :: EdgeNr -> ViaNr -> DoublePoint -> Network g n e -> Network g n e
updateVia edgeNr viaNr v network =
network { networkEdges =
IntMap.adjust (\e-> e { edgeVia = take viaNr (edgeVia e)
++[v]++drop (viaNr+1) (edgeVia e) })
edgeNr (networkEdges network) }