-
Notifications
You must be signed in to change notification settings - Fork 69
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
Add support for acyclic graphs and associated operations #154
Comments
This is related to this issue: #85, where a possible implementation of One problem with deriving such "level vertex sets" is that the solution is not unique, as demonstrated by this simple example: In an ideal world, I'd like to have the following signature of scc :: Graph a -> Acyclic.Graph (NonEmpty.Graph a) Now we could write functions like this: topSort :: Acyclic.Graph a -> [a]
batchLexicographic :: Acyclic.Graph a -> [Set a]
batchWeighted :: Acyclic.Graph (a, Int) -> [Set a] Where the latter does an optimal batching that aims to minimise the overall "execution time" of tasks associated with vertices. There are other interesting batching strategies, including dynamic ones. To sum up: I'd like to have this functionality, but I think it shouldn't be a part of |
Ah, of course it's not unique, my mistake! Then we can't give a "best" most general type for So yes, this should be some kind of additional "batching" functionality, which might not even belong in this package. Not sure if you want to keep this open for future reference, happy to close and I'll just implement my own thing. |
Let's keep this issue open, but I'll give it a more general title. |
topSort
to be a list of independent sets
Hi, Please go through the code in the following repository and let me know if it could work as a possible representation for acyclic graphs. https://github.com/adithyaov/acyclic-graphs The function I am currently looking at other possible implementation related to phantom types (gdp), but I haven't made much progress there. |
@adithyaov Thanks! I'm not sure if your solution has any advantages compared to simply wrapping graphs into an newtype Acyclic a = Acyclic a
scc' :: Graph a -> Acyclic (NonEmpty.Graph a)
scc' = Acyclic . scc This is a lot simpler than what you propose, but seems to achieve the same goal: you introduce an abstract data type for acyclic graphs, which can only be created using functions like Later on, if you extract the list of edges from this graph, the compiler gives you no static guarantees that there are no cycles in the obtained netlist. So, for example, if you want to apply |
@snowleopard , I think that there is a guarantee of acyclicity. If there is any Acyclic type, it is guaranteed that the property holds as we are not simply applying the wrapper but constructing the graph from the ground up using the specific Following is the algebra that is followed, |
@adithyaov Yes, this is an internal invariant of the data structure, but one can say that
And this is true of this list too:
Sure. As soon as you want to process the resulting acyclic graph by an algorithm that relies on the absence of cycles you run into partiality. A typical example is |
@snowleopard , It seems I am a slow learner, I believe I started off on the wrong foot.
Is considering the structure a good place to start? For example, maybe constructing the acyclic graphs like how one constructs a Tree (I don't have a concrete way of doing this yet but I'll try working on this)? One could create a different implementation of If possible could you also point me in a new direction with some abstract ideas you have in mind? It seems working with the order of vertices is not getting me far enough. It seems someone has created something similar to what's required, |
@adithyaov I haven't had much time to think about type-safe acyclic graphs yet, so I don't yet have any good answers to your questions. Please try various different approaches and let us know how they work! In addition to the link you shared, here are some relevant Twitter discussions: https://twitter.com/phadej/status/1105201407576166400?s=19 |
The more I think about this, the less sure I am that there should be a representation of acyclic graphs, that is fundamentally different from alga. The problem is that acyclic graphs aren't instances of The complexity of inserting many new edges is still an open problem, but the algorithm in that paper takes O(n^2.75) time, so it is still slower than just building an (possibly cyclic) graph and then applying
data Acyclic a = Acyclic { getGraph :: AdjacencyMap a, getTopologicalOrder :: [a]}
topSort :: AdjacencyMap a -> Maybe (Acyclic a)
sccSort :: AdjacencyMap a -> Acyclic (NonEmpty.AdjacencyMap a) -- see below
batchLexicographic :: Acyclic a -> [Set a]
batchWeighted :: Acyclic (a, Int) -> [Set a] Here one could add a monad to append new vertices as in the twitter thread.
sccSort :: Ord a => AdjacencyMap a -> [NonEmpty.AdjacencyMap a]
sccSort m = map expand sccs
where
sccs = sccList m -- custom implementation of KL.scc
expand xs = fromJust $ NonEmpty.toNonEmpty $ induce (`Set.member` s) m
where
s = Set.fromList xs |
@anfelor I agree that the "abstract data type + smart constructors" approach is a nice solution from the pragmatic point of view: it's simple and it solves most of the API problems. I'm happy for this approach to be tried first. We could also add an "acyclic graph monad" (discussed here), as one of the ways to create such abstract acyclic graphs (let's use this term?), at least some small examples, where potential performance issues are not important.
Note that it is in principle possible to define an algebra of graphs, which operates on ordered vertices, as @adithyaov sketches in his above comment. With this approach both graph expressions fromOrdered :: Ord a => Graph a -> Acyclic a Having said that, I'm not sure whether this algebra has any interesting properties from the mathematical point of view. I haven't had a chance to check which laws hold and which do not. |
From a first glance at the laws, it looks like this should work for all partial orders fromOrdered :: (a -> a -> Bool) -> Graph a -> Acyclic a which admits a partial order as the first argument should work (as a "graph homomorphism" :)). But how to we get that partial order? Especially in the case of #85, I feel like we would need to compute an acyclic graph first and would get the partial order through reachability, not the other way around? |
@anfelor Indeed, a partial order is sufficient. Interesting!
From the perspective of the algebra of graphs, the answer doesn't matter: we just operate on a partially ordered set of vertices. But from the user's perspective, the answer does matter. In some cases you do have a partial order upfront, and it does act as a proof that the resulting graph is acyclic. Of course, if a graph can in fact have cycles, then you cannot supply such a partial order without first performing a topological sort. Here is a simple example where we do have a partial order in advance:
Here we could just use data Extension = C | O | Exe deriving (Eq, Ord)
type File = (FilePath, Extension)
partialOrder :: File -> File -> Bool
partialOrder (_, x) (_, y) = x < y I find this a convincing example, where we can have a type-safe construction of an acyclic graph thanks to some domain knowledge. |
This seems to be a special case of partial orders: Those that are induced by a total order on keys (Extension in your example). But in general, transitivity could be a problem if for example some source files depend on .h files which depend on their source file in return. Still, using a partial order as a guarantee of acyclicity is a good idea; in the general case topSort even returns a total order of the vertices! |
"Topological sorting" is under-specified wrt the ordering of the independent vertices at each "level" of the sort. We could reflect this in the output type, by returning something like
[Set Vertex]
, where each set is an independent set. You can then recover the current sort by flattening the inner sets in any order.This has two advantages:
The text was updated successfully, but these errors were encountered: