{-# LANGUAGE TypeFamilies #-}
module TypedGraph.Morphism.Core where
import Abstract.Morphism as M
import Abstract.Valid
import Data.List (nub)
import Data.Maybe (fromMaybe, isJust)
import Graph.Graph
import Graph.GraphMorphism (GraphMorphism)
import qualified Graph.GraphMorphism as GM
import TypedGraph.Graph
data TypedGraphMorphism a b = TypedGraphMorphism {
getDomain :: TypedGraph a b
, getCodomain :: TypedGraph a b
, mapping :: GraphMorphism (Maybe a) (Maybe b)
} deriving (Eq, Show)
buildTypedGraphMorphism :: TypedGraph a b -> TypedGraph a b -> GraphMorphism (Maybe a) (Maybe b) -> TypedGraphMorphism a b
buildTypedGraphMorphism = TypedGraphMorphism
instance Morphism (TypedGraphMorphism a b) where
type Obj (TypedGraphMorphism a b) = TypedGraph a b
domain = getDomain
codomain = getCodomain
compose t1 t2 = TypedGraphMorphism (domain t1) (codomain t2) $ compose (mapping t1) (mapping t2)
id t = TypedGraphMorphism t t (M.id $ domain t)
isMonomorphism = isMonomorphism . mapping
isEpimorphism = isEpimorphism . mapping
isIsomorphism = isIsomorphism . mapping
instance Valid (TypedGraphMorphism a b) where
validate (TypedGraphMorphism dom cod m) =
mconcat
[ withContext "domain" (validate dom)
, withContext "codomain" (validate cod)
, ensure (dom == compose m cod) "Morphism doesn't preserve typing"
]
nodeIdsFromDomain :: TypedGraphMorphism a b -> [NodeId]
nodeIdsFromDomain = nodeIds . domain . getDomain
edgeIdsFromDomain :: TypedGraphMorphism a b -> [EdgeId]
edgeIdsFromDomain = edgeIds . domain . getDomain
edgesFromDomain :: TypedGraphMorphism a b -> [Edge (Maybe b)]
edgesFromDomain = edges . domain . getDomain
nodeIdsFromCodomain :: TypedGraphMorphism a b -> [NodeId]
nodeIdsFromCodomain = nodeIds . domain . getCodomain
edgeIdsFromCodomain :: TypedGraphMorphism a b -> [EdgeId]
edgeIdsFromCodomain = edgeIds . domain . getCodomain
edgesFromCodomain :: TypedGraphMorphism a b -> [Edge (Maybe b)]
edgesFromCodomain = edges . domain . getCodomain
applyNode :: TypedGraphMorphism a b -> NodeId -> Maybe NodeId
applyNode tgm = GM.applyNode (mapping tgm)
applyEdge :: TypedGraphMorphism a b -> EdgeId -> Maybe EdgeId
applyEdge tgm = GM.applyEdge (mapping tgm)
graphDomain :: TypedGraphMorphism a b -> Graph (Maybe a) (Maybe b)
graphDomain = untypedGraph . domain
graphCodomain :: TypedGraphMorphism a b -> Graph (Maybe a) (Maybe b)
graphCodomain = untypedGraph . codomain
applyNodeUnsafe :: TypedGraphMorphism a b -> NodeId -> NodeId
applyNodeUnsafe m n = fromMaybe (error "Error, apply node in a non total morphism") $ applyNode m n
applyEdgeUnsafe :: TypedGraphMorphism a b -> EdgeId -> EdgeId
applyEdgeUnsafe m e = fromMaybe (error "Error, apply edge in a non total morphism") $ applyEdge m e
orphanTypedNodeIds :: TypedGraphMorphism a b -> [NodeId]
orphanTypedNodeIds tgm = GM.orphanNodeIds (mapping tgm)
orphanTypedEdgeIds :: TypedGraphMorphism a b -> [EdgeId]
orphanTypedEdgeIds tgm = GM.orphanEdgeIds (mapping tgm)
orphanTypedEdges :: TypedGraphMorphism a b -> [Edge (Maybe b)]
orphanTypedEdges tgm = GM.orphanEdges (mapping tgm)
invert :: TypedGraphMorphism a b -> TypedGraphMorphism a b
invert tgm =
TypedGraphMorphism { getDomain = codomain tgm
, getCodomain = domain tgm
, mapping = GM.invertGraphMorphism (mapping tgm)
}
createEdgeOnDomain :: EdgeId -> NodeId -> NodeId -> EdgeId -> EdgeId -> TypedGraphMorphism a b -> TypedGraphMorphism a b
createEdgeOnDomain e1 s1 t1 tp e2 tgm =
tgm { getDomain = GM.createEdgeOnDomain e1 s1 t1 tp (domain tgm)
, mapping = GM.createEdgeOnDomain e1 s1 t1 e2 (mapping tgm)
}
createEdgeOnCodomain :: EdgeId -> NodeId -> NodeId -> EdgeId -> TypedGraphMorphism a b -> TypedGraphMorphism a b
createEdgeOnCodomain e2 s2 t2 tp tgm =
tgm { getCodomain = GM.createEdgeOnDomain e2 s2 t2 tp (codomain tgm)
, mapping = GM.createEdgeOnCodomain e2 s2 t2 (mapping tgm)
}
createNodeOnDomain :: NodeId -> NodeId -> NodeId -> TypedGraphMorphism a b -> TypedGraphMorphism a b
createNodeOnDomain n1 tp n2 tgm =
tgm { getDomain = GM.createNodeOnDomain n1 tp (domain tgm)
, mapping = GM.createNodeOnDomain n1 n2 (mapping tgm)
}
createNodeOnCodomain :: NodeId -> NodeId -> TypedGraphMorphism a b -> TypedGraphMorphism a b
createNodeOnCodomain n2 tp tgm =
tgm { getCodomain = GM.createNodeOnDomain n2 tp (codomain tgm)
, mapping = GM.createNodeOnCodomain n2 (mapping tgm)
}
updateNodeRelation :: NodeId -> NodeId -> NodeId -> TypedGraphMorphism a b -> TypedGraphMorphism a b
updateNodeRelation n1 n2 tp tgm =
TypedGraphMorphism { getDomain = GM.updateNodeRelation n1 tp (domain tgm)
, getCodomain = GM.updateNodeRelation n2 tp (codomain tgm)
, mapping = GM.updateNodeRelation n1 n2 (mapping tgm)
}
untypedUpdateNodeRelation :: NodeId -> NodeId -> TypedGraphMorphism a b -> TypedGraphMorphism a b
untypedUpdateNodeRelation n1 n2 tgm =
TypedGraphMorphism { getDomain = domain tgm
, getCodomain = codomain tgm
, mapping = GM.updateNodeRelation n1 n2 (mapping tgm)
}
updateEdgeRelation :: EdgeId -> EdgeId -> TypedGraphMorphism a b -> TypedGraphMorphism a b
updateEdgeRelation e1 e2 tgm =
tgm { mapping = GM.updateEdgeRelation e1 e2 (mapping tgm) }
removeNodeFromDomain :: NodeId -> TypedGraphMorphism a b -> TypedGraphMorphism a b
removeNodeFromDomain n tgm =
tgm { getDomain = GM.removeNodeFromDomain n (domain tgm)
, mapping = GM.removeNodeFromDomain n (mapping tgm)
}
removeEdgeFromDomain :: EdgeId -> TypedGraphMorphism a b -> TypedGraphMorphism a b
removeEdgeFromDomain e tgm =
tgm { getDomain = GM.removeEdgeFromDomain e (domain tgm)
, mapping = GM.removeEdgeFromDomain e (mapping tgm)
}
removeNodeFromCodomain :: NodeId -> TypedGraphMorphism a b -> TypedGraphMorphism a b
removeNodeFromCodomain n tgm =
tgm { getCodomain = GM.removeNodeFromDomain n (codomain tgm)
, mapping = GM.removeNodeFromCodomain n (mapping tgm)
}
removeEdgeFromCodomain :: EdgeId -> TypedGraphMorphism a b -> TypedGraphMorphism a b
removeEdgeFromCodomain e tgm =
tgm { getCodomain = GM.removeEdgeFromDomain e (codomain tgm)
, mapping = GM.removeEdgeFromCodomain e (mapping tgm) }
isPartialInjective :: TypedGraphMorphism a b -> TypedGraphMorphism a b -> Bool
isPartialInjective nac q = GM.isPartialInjective (mapping nac) (mapping q)
idMap :: TypedGraph a b -> TypedGraph a b -> TypedGraphMorphism a b
idMap gm1 gm2 =
buildTypedGraphMorphism gm1 gm2 edgesUpdate
where
initialGraph = GM.empty (domain gm1) (domain gm2)
nodesUpdate = foldr (\n -> GM.updateNodes n n) initialGraph (nodeIds (domain gm1))
edgesUpdate = foldr (\e -> GM.updateEdges e e) nodesUpdate (edgeIds (domain gm2))
reflectIdsFromTypeGraph :: TypedGraphMorphism a b -> TypedGraphMorphism a b
reflectIdsFromTypeGraph tgm =
let
gmDomain = domain tgm
gmCodomain = codomain tgm
newNodes gm = map (GM.applyNodeUnsafe gm) (nodeIds (domain gm))
newEdges gm = map (\x -> (GM.applyEdgeUnsafe gm (edgeId x), GM.applyNodeUnsafe gm (sourceId x), GM.applyNodeUnsafe gm (targetId x))) (edges $ domain gm)
newDomain = foldr (\(e,s,t) -> GM.createEdgeOnDomain e s t e) (foldr (\x -> GM.createNodeOnDomain x x) (GM.empty empty (codomain gmDomain)) (newNodes gmDomain)) (newEdges gmDomain)
newCodomain = foldr (\(e,s,t) -> GM.createEdgeOnDomain e s t e) (foldr (\x -> GM.createNodeOnDomain x x) (GM.empty empty (codomain gmCodomain)) (newNodes gmCodomain)) (newEdges gmCodomain)
newMaps = GM.buildGraphMorphism (domain newDomain) (domain newCodomain) (map (\(NodeId x) -> (x,x)) (nodeIds $ domain newDomain)) (map (\(EdgeId x) -> (x,x)) (edgeIds $ domain newDomain))
in buildTypedGraphMorphism newDomain newCodomain newMaps
reflectIdsFromCodomain :: TypedGraphMorphism a b -> TypedGraphMorphism a b
reflectIdsFromCodomain tgm =
let
typedA = domain tgm
typedB = codomain tgm
typeGraph = codomain typedA
typedB' = GM.empty empty typeGraph
nodes = nodeIdsFromDomain tgm
edges = edgesFromDomain tgm
initial = buildTypedGraphMorphism typedB' typedB (GM.empty (domain typedB') (domain typedB))
addNodes = foldr (\n -> createNodeOnDomain (applyNodeUnsafe tgm n) (GM.applyNodeUnsafe typedA n) (applyNodeUnsafe tgm n)) initial nodes
addEdges = foldr (\e ->
createEdgeOnDomain (applyEdgeUnsafe tgm (edgeId e))
(applyNodeUnsafe tgm (sourceId e))
(applyNodeUnsafe tgm (targetId e))
(GM.applyEdgeUnsafe typedA (edgeId e))
(applyEdgeUnsafe tgm (edgeId e))) addNodes edges
in addEdges
reflectIdsFromDomains :: (TypedGraphMorphism a b, TypedGraphMorphism a b) -> (TypedGraphMorphism a b, TypedGraphMorphism a b)
reflectIdsFromDomains (m,e) =
let
typedL = domain m
typedD = domain e
typedG = codomain m
typeGraph = codomain typedL
m' = invert m
e' = invert e
newNodes = nub (typedNodes typedL ++ typedNodes typedD)
newEdges = nub (typedEdges typedL ++ typedEdges typedD)
typedG' = foldr (\(e,s,ta,ty) -> GM.createEdgeOnDomain e s ta ty)
(foldr (uncurry GM.createNodeOnDomain) (GM.empty empty typeGraph) newNodes)
newEdges
nodeR n = if isJust (applyNode m' n) then (n, applyNodeUnsafe m' n) else (n, applyNodeUnsafe e' n)
edgeR e = if isJust (applyEdge m' e) then (e, applyEdgeUnsafe m' e) else (e, applyEdgeUnsafe e' e)
nodeRelation = map nodeR (nodeIdsFromDomain m')
edgeRelation = map edgeR (edgeIdsFromDomain m')
initial = buildTypedGraphMorphism typedG typedG' (GM.empty (domain typedG) (domain typedG'))
h' = foldr (uncurry updateEdgeRelation) (foldr (uncurry untypedUpdateNodeRelation) initial nodeRelation) edgeRelation
in (compose m h', compose e h')