I have two weighted DAGs (directed acyclic graphs) and need to merge them into one, so I can get a topological ordering (it could be more than two in some cases). The problem is
I had a similar problem that I solved like so:
I transformed my DAG into a map with a map of nodes (keyed by node id, value a collection of nodes, probably one to start) and a map of edges (keyed by source, target pair, value is a collection of edges, probably one to start). I called this normalize. The original graph was a collection of "roots" each node had a collection of children
I could then merge two of these together by merging nodes by key and edges by key. ie: if a node did exist then cons the new node to the existing node value, if a node does not exist, then add it. same with the edges.
This worked pretty clean but it didn't avoid cycles.
Here is some code (clojure) that I used:
(def empty-graph
{:nodes {}
:edges {}})
(defn merge-graphs
[a b]
{:nodes (merge-with concat (get a :nodes) (get b :nodes))
:edges (merge-with concat (get a :edges) (get b :edges))})
(defn normalize-graph
[graph]
{:nodes (->>
graph
(mapcat
(fn [root]
(->>
root
(tree-seq (fn [_] true) (fn [node] (get node :children)))
(mapcat
(fn [edge]
(concat
(if-let [source (get edge "source")]
[[source [source]]]
[])
(if-let [target (get edge "target")]
[[target [target]]]
[])))))))
(into {}))
:edges (->>
graph
(mapcat
(fn [root]
(->>
root
(tree-seq (fn [_] true) (fn [node] (get node :children)))
(filter (fn [edge] (and (not (nil? (get edge "source"))) (not (nil? (get edge "target")))))) ;this sucks but is necessary
(map
(fn [edge]
(let [compact (dissoc edge :children)]
[[(get edge "source") (get edge "target")] [compact]]))))))
(into {}))})