-
Notifications
You must be signed in to change notification settings - Fork 68
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Vertex-labelled adjacency maps #183
Comments
I suppose I could get away with it by storing the vertex-label in the data Vertex key label = Vertex key label -- A vertex key with label
instance Ord key => Ord (Vertex key label)
compare (Vertex k1 _) (Vertex k2 _) = compare k1 k2
-- Example
data MyEdgeLabel = ThisEdge | ThatEdge
data MyVertexlabel = ThisVertex | ThatVertex
type MyGraph = AdjacencyMap MyEdgeLabel (Vertex Int MyVertexLabel) |
I'm thinking perhaps we can call it a "property graph" and then redefine the edge-labelled graph using it:
So we would have a new module: -- Algebra.Graph.Property.AdjacencyMap
newtype AdjacencyMap v e a = AM {
adjacencyMap :: Map a (v, Map a e) } deriving (Eq, NFData) with the edge-labelled module either becoming redundant or redefined using the new one: -- Algebra.Graph.Property.AdjacencyMap
type AdjacencyMap e a = Algebra.Graph.Property.AdjacencyMap () e a @snowleopard What do you think? |
@srid My personal approach to labelling vertices has always been like you describe in your 2nd comment: -- A data type of vertices
data Vertex = ...
-- A data type corresponding to vertex labels
data Label = ...
-- A way to extract the label of a vertex
vertexLabel :: Vertex -> Label
-- Custom equality and comparison instances that ignore vertex labels
instance Eq Vertex where ...
instance Ord Vertex where ...
-- Actual graph datatypes: with and without edge labels
type Graph1 = AdjacencyMap Vertex
type Graph2 = Labelled.AdjacencyMap EdgeLabel Vertex I like this approach because it does not involve any additional complexity in the library itself. On the other hand, adding "property graphs" which you describe involves quite a lot of work for duplicating all existing graph modules: empty/non-empty, edge-labelled/edge-unlabelled, ADT-based/Map-based, etc. My preference would be to avoid this, as long as the solution with extracting labels from vertices works for you. Does it? You say it "seems slightly hacky" -- could you elaborate on this? |
Mainly having to wrap vertices with -- | A labelled type that ignores the label value in Eq and Ord instances
newtype Labelled a label = Labelled { unLabelled :: (a, label) }
deriving Generic
emptyLabel :: Monoid label => a -> Labelled a label
emptyLabel x = Labelled (x, mempty)
labelled :: a -> label -> Labelled a label
labelled x l = Labelled (x, l)
unLabel :: Labelled a label -> a
unLabel = fst . unLabelled
getLabel :: Labelled a label -> label
getLabel = snd . unLabelled
instance Eq a => Eq (Labelled a label) where
(==) = on (==) unLabel
instance Ord a => Ord (Labelled a label) where
compare = on compare unLabel |
@srid Great, thanks! Feel free to reopen if you come up with some API improvements for supporting vertex-labelled graphs. |
I just found myself needing this again, but this time I'm not so sure about the An adjacency map is defined as What do you think @snowleopard ? I would like this to simplify error handling in neuron. |
@srid I don't think that's right: the memory complexity of this representation is However, there is indeed some repetition: a vertex is stored To avoid this, you can just use the approach I described in this comment: store a graph and the labelling function separately as |
@snowleopard The problem with that approach is that, when you are using graph to represent items that can only be known at runtime,
I would like the guarantee that for every vertex there exists a label, and vice-versa. Just as it is the case with edge-labels in the current edge-labelled adjacency maps. |
Hmm, if all you have is a partial In your earlier version (with potential duplication), where the vertices had the type This doesn't seem to be a problem related only to graphs. Imagine that you'd like to store a list of labelled
Maybe there are others? I think if you find a good answer for lists, it should lead to a similar solution for graphs. |
Not a partial Map; I actually know all the vertices ahead (in my application, they refer to the note files in a directory; where filename is vertex, and note content is the 'label'). Consider an unique list of objects In my application,
Lists are easy. I'd simply use |
@srid Thanks for further details, I think I understand your problem better now.
I think here is where I'm confused. It's not enough that you know that the function is bijective. You need to prove this to the compiler by exhibiting the inverse mapping Furthermore, assuming your belief that the function is bijective is justified, you shouldn't be worried about extracting |
@srid Actually, I now see the value of the vertex-and-edge-labelled graph representation that you proposed in the very first comment: (I mean, it looks fine as a possible representation but we don't have a corresponding algebra yet.) ((Also, I'm a bit terrified about adding yet another representation to the library.)) |
Yea, fair enough. Meanwhile I have a come up with a solution using type famillies: From https://github.com/srid/neuron/blob/f58e408/src/Data/Graph/Labelled/Type.hs#L11-L27 data LabelledGraph v e = LabelledGraph
{ graph :: LAM.AdjacencyMap e (VertexID v),
-- Guaranteed to contain exact vertices in graph, using smart constructors
vertices :: Map (VertexID v) v
} with the associated class: -- | Instances of this class can be used as a vertex in a graph.
class Vertex v where
type VertexID v :: Type
-- | Get the vertex ID associated with this vertex.
--
-- This relation is expected to be bijective.
vertexID :: v -> VertexID v This works, but I find myself writing adapters to graph functions to transform the input and output, eg: topSort :: (Vertex v, Ord (VertexID v)) => LabelledGraph v e -> Either (NonEmpty v) [v]
topSort g =
bimap (fmap (getVertex g)) (fmap (getVertex g))
$ Algo.topSort
$ LAM.skeleton
$ graph g |
@srid Many thanks for sharing your approach. What is |
|
Yes, I see. This seems like a reasonable trade-off. |
I just learned that the library supports edge-labelled adjacency maps:
What do you think of having a edge and vertex labelled adjacency map? Something like:
I could look into writing this if this is worth adding to the library.
The text was updated successfully, but these errors were encountered: