Extension Problem
The main component of this framework is composed of algorithms for the extension problem. That is, given a partially directed graph, compute a consistent DAG extension if possible, otherwise return a negative answer.
Some algorithms have been implemented in two variants. The first variant uses HashSets internally while the second one uses the LightGraphs library internally. It is noteworthy that the implementation using HashSets has a better performance in general and thus not all algorithms have been implemented using the LightGraphs library internally.
Implementation using the LightGraphs Library
Since the performance of the implementation with LightGraphs is inferior to the performance of the HashSets implementation, not all algorithms have been implemented using LightGraphs. Available algorithms are:
pdag2dag_lg- An algorithm with worst-case time complexity $O(\Delta^2 |E|)$.fastpdag2dag_lg- An algorithm with worst-case time complexity $O(\Delta |E|)$.
All of the other functions that are listed below are called internally in these algorithms.
PdagExtendability.pdag2dag_lg — Methodpdag2dag_lg(g::SimpleDiGraph)::SimpleDiGraphConvert a partially directed acyclic graph (PDAG) into a fully directed acyclic graph (DAG). If this is not possible, an empty graph is returned.
Undirected edges are represented as two directed edges.
References
D. Dor, M. Tarsi (1992). A simple algorithm to construct a consistent extension of a partially oriented graph. Technicial Report R-185, Cognitive Systems Laboratory, UCLA
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> dag = pdag2dag_lg(g)
{3, 2} directed simple Int64 graph
julia> collect(edges(dag))
2-element Array{LightGraphs.SimpleGraphs.SimpleEdge{Int64},1}:
Edge 1 => 2
Edge 2 => 3PdagExtendability.sink_lg — Methodsink_lg(g::SimpleDiGraph)::Int64Find a sink in a partially directed graph. The sink has no outgoing edges and all vertices connected to it via an undirected edge are adjacent to all adjacent vertices of the sink. If no sink is found, -1 is returned.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> x = sink_lg(g)
3PdagExtendability.Graph — TypeThe datastructure to store a partially directed graph, using the LightGraphs library internally.
PdagExtendability.init_auxvectors_lg! — Methodinit_auxvectors_lg!(g::Graph)Initialize the auxilliary vectors for the given graph g.
Examples
julia> g = init_lg(SimpleDiGraph(3))
Graph(
{3, 0} directed simple Int64 graph,
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
)
julia> init_auxvectors_lg!(g)PdagExtendability.init_lg — Functioninit_lg(g::SimpleDiGraph, emptygraph::Bool = false)::GraphAllocate memory for the HybridGraph datastructure representing a graph with n vertices. The datastructure holds a copy of the input graph g. This can be disabled by setting emptygraph to true.
Examples
julia> g = init_lg(SimpleDiGraph(3))
Graph(
{3, 0} directed simple Int64 graph,
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
)PdagExtendability.insert_arc_lg! — Functioninsert_arc_lg!(graph::Graph, u::Int64, v::Int64, update::Bool = false)Insert an arc (a directed edge) from u to v into the given graph. If update is set to true, the edge is inserted into graph.g and the values for alpha and beta are updated as well.
Examples
julia> g = init_lg(3)
...
julia> insert_arc_lg!(g, 1, 2)
julia> is_adjacent_lg(g, 1, 2)
truePdagExtendability.insert_edge_lg! — Functioninsert_edge_lg!(graph::Graph, u::Int64, v::Int64, update::Bool = false)Insert an undirected edge between u and v into the given graph. If update is set to true, the edge is inserted into graph.g and the values for alpha and beta are updated as well.
Examples
julia> g = init_lg(3)
...
julia> insert_edge_lg!(g, 2, 3)
julia> is_adjacent_lg(g, 2, 3)
truePdagExtendability.is_adjacent_lg — Methodis_adjacent_lg(graph::Graph, u::Int64, v::Int64)::BoolCheck whether vertices u and v are adjacent in the given graph.
Examples
julia> g = init_lg(3)
...
julia> is_adjacent_lg(g, 1, 2)
false
julia> insert_arc_lg!(g, 1, 2)
julia> is_adjacent_lg(g, 1, 2)
truePdagExtendability.is_directed_lg — Methodis_directed_lg(graph::Graph, u::Int64, v::Int64)::BoolCheck whether the given graph contains a directed edge from u to v.
Examples
julia> g = init_lg(3)
...
julia> insert_arc_lg!(g, 1, 2)
julia> is_directed_lg(g, 1, 2)
truePdagExtendability.is_ps_lg — Methodis_ps_lg(g::Graph, s::Int64)::BoolDetermine whether s is a potential sink in g.
Examples
julia> g = init_lg(3)
...
julia> is_ps_lg(g, 1)
true
julia> insert_arc_lg!(g, 1, 2)
julia> is_ps_lg(g, 1)
falsePdagExtendability.is_undirected_lg — Methodis_undirected_lg(graph::Graph, u::Int64, v::Int64)::BoolCheck whether the given graph contains an undirected edge between u and v.
Examples
julia> g = init_lg(3)
...
julia> insert_arc_lg!(g, 1, 2)
julia> is_undirected_lg(g, 1, 2)
falsePdagExtendability.list_ps_lg — Methodlist_ps_lg(graph::Graph)::Vector{Int64}List potential sinks in the given graph.
Examples
julia> g = init_lg(3)
...
julia> list_ps_lg(g)
3-element Vector{Int64}:
1
2
3
julia> insert_arc_lg!(g, 1, 2)
julia> insert_edge_lg!(g, 2, 3)
julia> list_ps_lg(g)
1-element Vector{Int64}:
3PdagExtendability.pop_ps_lg! — Methodpop_ps_lg!(graph::Graph, s::Int64)::Vector{Int64}Mark s as deleted and delete all edges (directed and undirected) incident to s. Return a list of neighbors of s that became potential sinks after the removal.
Examples
julia> g = init_lg(3)
...
julia> insert_arc_lg!(g, 1, 2)
julia> insert_edge_lg!(g, 2, 3)
julia> list_ps_lg(g)
1-element Vector{Int64}:
3
julia> pop_ps_lg!(g, 3)
1-element Vector{Int64}:
2PdagExtendability.print_graph_lg — Functionprint_graph_lg(graph::Graph, io::Core.IO = stdout)Print the components of a given graph.
Examples
julia> g = init_lg(1)
...
julia> print_graph_lg(g)
Vertex 1:
Alpha = 0 Beta = 0
δ+(G1) = 0 δ-(G1) = 0 δ+(G2) = 0 δ-(G2) = 0
Adj =
In =
Out = PdagExtendability.remove_arc_lg! — Methodremove_arc_lg!(graph::Graph, u::Int64, v::Int64)Remove an arc (a directed edge) from u to v from the given graph.
Examples
julia> g = init_lg(3)
...
julia> insert_arc_lg!(g, 1, 2)
julia> is_adjacent_lg(g, 1, 2)
true
julia> remove_arc_lg!(g, 1, 2)
julia> is_adjacent_lg(g, 1, 2)
falsePdagExtendability.remove_edge_lg! — Methodremove_edge_lg!(graph::Graph, u::Int64, v::Int64)Remove an undirected edge between u and v from the given graph.
Examples
julia> g = init_lg(3)
...
julia> insert_edge_lg!(g, 2, 3)
julia> is_adjacent_lg(g, 2, 3)
true
julia> remove_edge_lg!(g, 2, 3)
julia> is_adjacent_lg(g, 2, 3)
falsePdagExtendability.update_alphabeta_lg! — Methodupdate_alphabeta_lg!(g::Graph, u::Int64, v::Int64, val::Int64, is_uv_dir::Bool)Update values for alpha and beta in g. Either add to (positive value for val) or subtract from (negative value for val) alpha and beta. The parameter isuvdir indicates whether the edge between u and v is directed from u to v.
Is called internally whenever an edge (both directed and undirected) is inserted or removed. Do not call this function by hand.
PdagExtendability.extendgraph_lg — Methodextendgraph_lg(graph::Graph)::SimpleDiGraphCompute the extension of the given graph.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> graph = standardsetup_lg(g)
...
julia> extendgraph_lg(graph)
{3, 2} directed simple Int64 graphPdagExtendability.fastpdag2dag_lg — Functionfastpdag2dag_lg(g::SimpleDiGraph, optimize::Bool = false)::SimpleDiGraphConvert a partially directed acyclic graph (PDAG) into a fully directed acyclic graph (DAG). If this is not possible, an empty graph is returned.
Undirected edges are represented as two directed edges.
If the parameter optimize is omitted or set to false, the algorithm runs in time O(Δm) with Δ being the maximum degree of g and m the number of edges in g. Setting optimize to true will yield an algorithm in time O(dm), where d is the degeneracy of the skeleton.
References
M. Wienöbst, M. Bannach, M. Liśkiewicz (2021). Extendability of Causal Graphical Models: Algorithms and Computational Complexity. 37th Conference on Uncertainty in Artificial Intelligence, 2021 (UAI 2021).
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> dag = fastpdag2dag_lg(g)
{3, 2} directed simple Int64 graph
julia> collect(edges(dag))
2-element Array{LightGraphs.SimpleGraphs.SimpleEdge{Int64},1}:
Edge 1 => 2
Edge 2 => 3PdagExtendability.optimizedsetup_lg — Methodoptimizedsetup_lg(g::SimpleDiGraph)::GraphSet up the datastructure for the algorithm with time complexity O(dm).
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> optimizedsetup_lg(g)
Graph(
{3, 3} directed simple Int64 graph,
[0, 0, 0],
[0, 0, 0],
[1, 0, 0],
[0, 1, 1],
[0, 1, 0],
[0, 1, 1]
)PdagExtendability.standardsetup_lg — Methodstandardsetup_lg(g::SimpleDiGraph)::GraphSet up the datastructure for the algorithm with time complexity O(Δm).
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> standardsetup_lg(g)
Graph(
{3, 3} directed simple Int64 graph,
[0, 0, 0],
[0, 0, 0],
[1, 0, 0],
[0, 1, 1],
[0, 1, 0],
[0, 1, 1]
)PdagExtendability.deg_struct_lg — Methoddeg_struct_lg(g::SimpleDiGraph)::Tuple{Vector{Int64}, Vector{Set{Int64}}}Compute the degree structure for the graph g. Return a tuple consisting of an array holding the degree for each vertex in g and an array where each index represents a degree (index 1 for degree 0, index 2 for degree 1, ..., index n for degree n-1) and holds a set of vertices which have that degree.
For example, if we have three vertices 1, 2, 3 with degree 1, 2, and 1, respectively, we obtain the following degree structure: ([1, 2, 1], [Set(), Set([1, 3]), Set([2])])
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> deg_struct_lg(g)
([1, 2, 1], Set{Int64}[Set(), Set([3, 1]), Set([2])])PdagExtendability.degeneracy_ordering_lg — Methoddegeneracy_ordering_lg(g::SimpleDiGraph)::Tuple{Vector{Int64}, Dict{Int64, Int64}}Compute a degeneracy ordering for the skeleton of the graph g.
References
David W. Matula, Leland L. Beck (1983). Smallest-last ordering and clustering and graph coloring algorithms. Journal of the ACM, 30(3):417–427.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> degeneracy_ordering_lg(g)
([1, 2, 3], Dict(2 => 2, 3 => 3, 1 => 1))PdagExtendability.pop_min_deg_vertex_lg! — Methodpop_min_deg_vertex_lg!(degs::Vector{Set{Int64}})::Int64Find the vertex with minimum degree in the given degree structure. The vertex will be removed from the structure before returning it. In case there are multiple vertices with the same minimum degree, there is no specific order of choosing them, i.e., any of those vertices is returned.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> (_, degs) = deg_struct_lg(g)
([1, 2, 1], Set{Int64}[Set(), Set([3, 1]), Set([2])])
julia> pop_min_deg_vertex_lg!(degs)
3PdagExtendability.update_deg_lg! — Methodupdate_deg_lg!(v::Int64, aux::Vector{Int64}, degs::Vector{Set{Int64}})Update the degree of a vertex after an adjacent vertex has been removed, i.e., reduce the degree by one and move it into the correct set in the degree structure.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> (aux, degs) = deg_struct_lg(g)
([1, 2, 1], Set{Int64}[Set(), Set([3, 1]), Set([2])])
julia> update_deg_lg!(3, aux, degs)
julia> (aux, degs)
([1, 2, 0], Set{Int64}[Set([3]), Set([1]), Set([2])])Implementation using HashSets
Basically, there are three different algorithms implemented using HashSets internally:
pdag2dag_hs- An algorithm with worst-case time complexity $O(\Delta^2 |E|)$.altpdag2dag_hs- An alternative implementation forpdag2dag_hswith worst-case time complexity $O(\Delta^2 |E|)$.fastpdag2dag_hs- An algorithm with worst-case time complexity $O(\Delta |E|)$.pdag2mpdag2dag- An algorithm with worst-case time complexity $O(\Delta |E|^2)$ (as the worst-case complexity already suggests, this algorithm is way slower than the other algorithms).
Again, all of the other functions listed below are called internally in these algorithms.
PdagExtendability.DtGraph — TypeThe datastructure to store a partially directed graph, using HashSets internally.
PdagExtendability.degree_hs — Methoddegree_hs(graph::DtGraph, u::Int64)::Int64Compute the degree of vertix u in the given graph.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> dtgraph = setup_hs(g)
DtGraph(
3,
Set([2, 3, 1]),
Set{Int64}[],
Set{Int64}[Set(), Set([1]), Set()],
Set{Int64}[Set([2]), Set(), Set()],
Set{Int64}[Set(), Set(), Set()]
)
julia> degree_hs(dtgraph, 1)
1PdagExtendability.insert_edge_hs! — Methodinsert_edge_hs!(graph::DtGraph, u::Int64, v::Int64, isdir::Bool)Insert an edge between u and v into the given graph. The parameter isdir indicates whether the edge is directed or not.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> dtgraph = setup_hs(g)
DtGraph(
3,
Set([2, 3, 1]),
Set{Int64}[],
Set{Int64}[Set(), Set([1]), Set()],
Set{Int64}[Set([2]), Set(), Set()],
Set{Int64}[Set(), Set(), Set()]
)
julia> insert_edge_hs!(dtgraph, 2, 3, true)
julia> isadjacent_hs(dtgraph, 3, 2)
truePdagExtendability.isadjacent_hs — Methodisadjacent_hs(graph::DtGraph, u::Int64, v::Int64)::BoolCheck whether vertices u and v are adjacent in the given graph.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> dtgraph = setup_hs(g)
DtGraph(
3,
Set([2, 3, 1]),
Set{Int64}[],
Set{Int64}[Set(), Set([1]), Set()],
Set{Int64}[Set([2]), Set(), Set()],
Set{Int64}[Set(), Set(), Set()]
)
julia> isadjacent_hs(dtgraph, 1, 2)
truePdagExtendability.print_graph_hs — Functionprint_graph_hs(graph::DtGraph, io::Core.IO = stdout)Print a given graph.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> dtgraph = setup_hs(g)
DtGraph(
3,
Set([2, 3, 1]),
Set{Int64}[],
Set{Int64}[Set(), Set([1]), Set()],
Set{Int64}[Set([2]), Set(), Set()],
Set{Int64}[Set(), Set(), Set()]
)
julia> print_graph_hs(dtgraph)
Vertex 1:
Ingoing =
Outgoing = 2
Undirected =
Vertex 2:
Ingoing = 1
Outgoing =
Undirected =
Vertex 3:
Ingoing =
Outgoing =
Undirected = PdagExtendability.remove_vertex_hs! — Functionremove_vertex_hs!(graph::DtGraph, x::Int64, useheuristic::Bool = false)Remove vertex x from the given graph. Note that this only removes x from sets of other nodes and the sets of x itself are left unchanged, so do not use index x anymore after the removal.
The parameter useheuristic indicates whether to update the priority queue with vertices' degrees.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> dtgraph = setup_hs(g)
DtGraph(
3,
Set([2, 3, 1]),
Set{Int64}[],
Set{Int64}[Set(), Set([1]), Set()],
Set{Int64}[Set([2]), Set(), Set()],
Set{Int64}[Set(), Set(), Set()]
)
julia> remove_vertex_hs!(dtgraph, 2)
julia> dtgraph
DtGraph(
2,
Set([3, 1]),
Set{Int64}[],
Set{Int64}[Set(), Set([1]), Set()],
Set{Int64}[Set(), Set(), Set()],
Set{Int64}[Set(), Set(), Set()]
)PdagExtendability.setup_hs — Functionsetup_hs(g::SimpleDiGraph, useheuristic::Bool = false)::DtGraphInitialize the datastructure from a given graph g.
The parameter useheuristic indicates whether to maintain a priority queue with vertices' degrees which is used to prefer vertices with low degrees over ones with higher degrees.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> setup_hs(g)
DtGraph(
3,
Set([2, 3, 1]),
Set{Int64}[],
Set{Int64}[Set(), Set([1]), Set()],
Set{Int64}[Set([2]), Set(), Set()],
Set{Int64}[Set(), Set(), Set()]
)
julia> setup_hs(g, true)
DtGraph(
3,
Set{Int64}(),
Set{Int64}[Set([3]), Set([2, 1]), Set()],
Set{Int64}[Set(), Set([1]), Set()],
Set{Int64}[Set([2]), Set(), Set()],
Set{Int64}[Set(), Set(), Set()]
)PdagExtendability.pdag2dag_hs — Functionpdag2dag_hs(g::SimpleDiGraph, useheuristic::Bool = false)::SimpleDiGraphConvert a partially directed acyclic graph (PDAG) into a fully directed acyclic graph (DAG). If this is not possible, an empty graph is returned.
Undirected edges are represented as two directed edges.
Set useheuristic to true if the algorithm should consider vertices with lower degrees first.
References
D. Dor, M. Tarsi (1992). A simple algorithm to construct a consistent extension of a partially oriented graph. Technicial Report R-185, Cognitive Systems Laboratory, UCLA
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> dag = pdag2dag_hs(g)
{3, 2} directed simple Int64 graph
julia> collect(edges(dag))
2-element Array{LightGraphs.SimpleGraphs.SimpleEdge{Int64},1}:
Edge 1 => 2
Edge 2 => 3PdagExtendability.sink_hs — Functionsink_hs(graph::DtGraph, useheuristic::Bool = false)::Int64Find a sink in a partially directed graph. The sink has no outgoing edges and all vertices connected to it via an undirected edge are adjacent to all adjacent vertices of the sink. If no sink is found, -1 is returned.
Set useheuristic to true if the algorithm should consider vertices with lower degrees first.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> x = sink_hs(setup_hs(g))
3PdagExtendability.altpdag2dag_hs — Methodaltpdag2dag_hs(g::SimpleDiGraph)::SimpleDiGraphAlternative implementation of pdag2dag_hs. Computes a list of sinks at the beginning. As long as there are sinks left, removes a sink from the graph and checks for all previous neighbors if they became a sink now. If so, adds them to the list of sinks. Terminates when no sinks are left or all vertices are removed.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> dag = altpdag2dag_hs(g)
{3, 2} directed simple Int64 graph
julia> collect(edges(dag))
2-element Vector{LightGraphs.SimpleGraphs.SimpleEdge{Int64}}:
Edge 1 => 2
Edge 2 => 3PdagExtendability.is_sink_hs — Methodis_sink_hs(graph::DtGraph, x::Int64)::BoolCheck whether vertex x is a sink in the given graph.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> setup = setup_hs(g)
...
julia> is_sink_hs(setup, 1)
false
julia> is_sink_hs(setup, 3)
truePdagExtendability.list_sinks_hs — Methodlist_sinks_hs(graph::DtGraph)::Vector{Int64}Compute a list of sinks in the given graph.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> setup = setup_hs(g)
...
julia> list_sinks_hs(setup)
1-element Vector{Int64}:
3PdagExtendability.DirectedGraph — TypeThe datastructure to store a directed graph.
PdagExtendability.HybridGraph — TypeThe datastructure to store a partially directed graph.
PdagExtendability.init_hs — Methodinit_hs(n::Int64)::HybridGraphAllocate memory for the HybridGraph datastructure representing a graph with n vertices.
Examples
julia> g = init_hs(3)
HybridGraph(
DirectedGraph(
Set{Int64}[Set(), Set(), Set()],
[0, 0, 0],
[0, 0, 0],
Set{Int64}[Set(), Set(), Set()],
Set{Int64}[Set(), Set(), Set()]
),
DirectedGraph(
Set{Int64}[Set(), Set(), Set()],
[0, 0, 0],
[0, 0, 0],
Set{Int64}[Set(), Set(), Set()],
Set{Int64}[Set(), Set(), Set()]
),
[0, 0, 0],
[0, 0, 0]
)PdagExtendability.insert_arc_hs! — Methodinsert_arc_hs!(g::DirectedGraph, u::Int64, v::Int64)Insert an arc (a directed edge) from u to v into g.
Is called internally and should not be called by hand! For inserting arcs, use the function insertarchs! directly on the HybridGraph datastructure instead. ```
PdagExtendability.insert_arc_hs! — Methodinsert_arc_hs!(g::HybridGraph, u::Int64, v::Int64)Insert an arc (a directed edge) from u to v into g.
Examples
julia> g = init_hs(3)
...
julia> insert_arc_hs!(g, 1, 2)
julia> is_adjacent_hs(g, 1, 2)
truePdagExtendability.insert_edge_hs! — Methodinsert_edge_hs!(g::HybridGraph, u::Int64, v::Int64)Insert an undirected edge between u and v into g.
Examples
julia> g = init_hs(3)
...
julia> insert_edge_hs!(g, 2, 3)
julia> is_adjacent_hs(g, 2, 3)
truePdagExtendability.is_adjacent_hs — Methodis_adjacent_hs(g::HybridGraph, u::Int64, v::Int64)::BoolCheck whether vertices u and v are adjacent in graph g.
Examples
julia> g = init_hs(3)
...
julia> is_adjacent_hs(g, 1, 2)
false
julia> insert_arc_hs!(g, 1, 2)
julia> is_adjacent_hs(g, 1, 2)
truePdagExtendability.is_ps_hs — Methodis_ps_hs(g::HybridGraph, s::Int64)::BoolDetermine whether s is a potential sink in g.
Examples
julia> g = init_hs(3)
...
julia> is_ps_hs(g, 1)
true
julia> insert_arc_hs!(g, 1, 2)
julia> is_ps_hs(g, 1)
falsePdagExtendability.list_ps_hs — Methodlist_ps_hs(g::HybridGraph)::Vector{Int64}List potential sinks in g.
Examples
julia> g = init_hs(3)
...
julia> list_ps_hs(g)
3-element Array{Int64,1}:
1
2
3
julia> insert_arc_hs!(g, 1, 2)
julia> insert_edge_hs!(g, 2, 3)
julia> list_ps_hs(g)
1-element Array{Int64,1}:
3PdagExtendability.pop_ps_hs! — Methodpop_ps_hs!(g::HybridGraph, s::Int64)::Vector{Int64}Mark s as deleted and delete all edges (directed and undirected) incident to s. Return a list of neighbors of s that became potential sinks after the removal.
Examples
julia> g = init_hs(3)
...
julia> insert_arc_hs!(g, 1, 2)
julia> insert_edge_hs!(g, 2, 3)
julia> list_ps_hs(g)
1-element Array{Int64,1}:
3
julia> pop_ps_hs!(g, 3)
1-element Array{Int64,1}:
2PdagExtendability.print_graph_hs — Functionprint_graph_hs(g::HybridGraph, io::Core.IO = stdout)Print the components of a HybridGraph g.
Examples
julia> g = init_hs(1)
...
julia> print_graph_hs(g)
Vertex 1:
Alpha = 0 Beta = 0
δ+(G1) = 0 δ-(G1) = 0 δ+(G2) = 0 δ-(G2) = 0
Adj(G1) = Adj(G2) =
In(G1) = In(G2) =
Out(G1) = Out(G2) = PdagExtendability.remove_arc_hs! — Methodremove_arc_hs!(g::DirectedGraph, u::Int64, v::Int64)Remove an arc (a directed edge) from u to v from g.
Is called internally and should not be called by hand! For removing arcs, use the function removearchs! directly on the HybridGraph datastructure instead.
PdagExtendability.remove_arc_hs! — Methodremove_arc_hs!(g::HybridGraph, u::Int64, v::Int64)Remove an arc (a directed edge) from u to v from g.
Examples
julia> g = init_hs(3)
...
julia> insert_arc_hs!(g, 1, 2)
julia> is_adjacent_hs(g, 1, 2)
true
julia> remove_arc_hs!(g, 1, 2)
julia> is_adjacent_hs(g, 1, 2)
falsePdagExtendability.remove_edge_hs! — Methodremove_edge_hs!(g::HybridGraph, u::Int64, v::Int64)Remove an undirected edge between u and v from g.
Examples
julia> g = init_hs(3)
...
julia> insert_edge_hs!(g, 2, 3)
julia> is_adjacent_hs(g, 2, 3)
true
julia> remove_edge_hs!(g, 2, 3)
julia> is_adjacent_hs(g, 2, 3)
falsePdagExtendability.update_alphabeta_hs! — Methodupdate_alphabeta_hs!(g::HybridGraph, u::Int64, v::Int64, val::Int64)Update values for alpha and beta in g. Either add to (positive value for val) or subtract from (negative value for val) alpha and beta.
Is called internally whenever an edge (both directed and undirected) is inserted or removed. Do not call this function by hand.
PdagExtendability.extendgraph_hs — Methodextendgraph_hs(g::SimpleDiGraph, hg::HybridGraph)::SimpleDiGraphCompute the extension of graph hg.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> hg = standardsetup_hs(g)
...
julia> extendgraph_hs(g, hg)
{3, 2} directed simple Int64 graphPdagExtendability.fastpdag2dag_hs — Functionfastpdag2dag_hs(g::SimpleDiGraph, optimize::Bool = false)::SimpleDiGraphConvert a partially directed acyclic graph (PDAG) into a fully directed acyclic graph (DAG). If this is not possible, an empty graph is returned.
Undirected edges are represented as two directed edges.
If the parameter optimize is omitted or set to false, the algorithm runs in time O(Δm) with Δ being the maximum degree of g and m the number of edges in g. Setting optimize to true will yield an algorithm in time O(dm), where d is the degeneracy of the skeleton.
References
M. Wienöbst, M. Bannach, M. Liśkiewicz (2021). Extendability of Causal Graphical Models: Algorithms and Computational Complexity. 37th Conference on Uncertainty in Artificial Intelligence, 2021 (UAI 2021).
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> dag = fastpdag2dag_hs(g)
{3, 2} directed simple Int64 graph
julia> collect(edges(dag))
2-element Array{LightGraphs.SimpleGraphs.SimpleEdge{Int64},1}:
Edge 1 => 2
Edge 2 => 3
julia> dag = fastpdag2dag_hs(g, true)
{3, 2} directed simple Int64 graph
julia> collect(edges(dag))
2-element Array{LightGraphs.SimpleGraphs.SimpleEdge{Int64},1}:
Edge 1 => 2
Edge 2 => 3PdagExtendability.optimizedsetup_hs — Methodoptimizedsetup_hs(g::SimpleDiGraph)::HybridGraphSet up the datastructure for the algorithm with time complexity O(dm).
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> optimizedsetup_hs(g)
HybridGraph(
DirectedGraph(
Set{Int64}[Set(), Set([3]), Set([2])],
[0, 1, 1],
[0, 1, 1],
Set{Int64}[Set(), Set([3]), Set([2])],
Set{Int64}[Set(), Set([3]), Set([2])]
),
DirectedGraph(
Set{Int64}[Set([2]), Set([1]), Set()],
[1, 0, 0],
[0, 1, 0],
Set{Int64}[Set(), Set([1]), Set()],
Set{Int64}[Set([2]), Set(), Set()]
),
[0, 0, 0],
[0, 0, 0]
)PdagExtendability.standardsetup_hs — Methodstandardsetup_hs(g::SimpleDiGraph)::HybridGraphSet up the datastructure for the algorithm with time complexity O(Δm).
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> standardsetup_hs(g)
HybridGraph(
DirectedGraph(
Set{Int64}[Set(), Set([3]), Set([2])],
[0, 1, 1],
[0, 1, 1],
Set{Int64}[Set(), Set([3]), Set([2])],
Set{Int64}[Set(), Set([3]), Set([2])]
),
DirectedGraph(
Set{Int64}[Set([2]), Set([1]), Set()],
[1, 0, 0],
[0, 1, 0],
Set{Int64}[Set(), Set([1]), Set()],
Set{Int64}[Set([2]), Set(), Set()]
),
[0, 0, 0],
[0, 0, 0]
)PdagExtendability.deg_struct_hs — Methoddeg_struct_hs(g::DtGraph)::Tuple{Vector{Int64}, Vector{Set{Int64}}}Compute the degree structure for the graph g. Return a tuple consisting of an array holding the degree for each vertex in g and an array where each index represents a degree (index 1 for degree 0, index 2 for degree 1, ..., index n for degree n-1) and holds a set of vertices which have that degree.
For example, if we have three vertices 1, 2, 3 with degree 1, 2, and 1, respectively, we obtain the following degree structure: ([1, 2, 1], [Set(), Set([1, 3]), Set([2])])
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> deg_struct_hs(setup_hs(g))
([1, 2, 1], Set{Int64}[Set(), Set([3, 1]), Set([2])])PdagExtendability.degeneracy_ordering_hs — Methoddegeneracy_ordering_hs(g::SimpleDiGraph)::Tuple{Vector{Int64}, Vector{Int64}}Compute a degeneracy ordering for the skeleton of the graph g. The second component of the tuple holds a mapping for each vertex of the ordering that maps the vertex to its index in the ordering.
References
David W. Matula, Leland L. Beck (1983). Smallest-last ordering and clustering and graph coloring algorithms. Journal of the ACM, 30(3):417–427.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> degeneracy_ordering_hs(g)
([1, 2, 3], [1, 2, 3])PdagExtendability.pop_min_deg_vertex_hs! — Methodpop_min_deg_vertex_hs!(degs::Vector{Set{Int64}})::Int64Find the vertex with minimum degree in the given degree structure. The vertex will be removed from the structure before returning it. In case there are multiple vertices with the same minimum degree, there is no specific order of choosing them, i.e., any of those vertices is returned.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> (_, degs) = deg_struct_hs(g)
([1, 2, 1], Set{Int64}[Set(), Set([3, 1]), Set([2])])
julia> pop_min_deg_vertex_hs!(degs)
3PdagExtendability.update_deg_hs! — Methodupdate_deg_hs!(v::Int64, aux::Vector{Int64}, degs::Vector{Set{Int64}})Update the degree of a vertex after an adjacent vertex has been removed, i.e., reduce the degree by one and move it into the correct set in the degree structure.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> (aux, degs) = deg_struct_hs(g)
([1, 2, 1], Set{Int64}[Set(), Set([3, 1]), Set([2])])
julia> update_deg_hs!(3, aux, degs)
julia> (aux, degs)
([1, 2, 0], Set{Int64}[Set([3]), Set([1]), Set([2])])PdagExtendability.countvstructs — Methodcountvstructs(g::DtGraph)::UIntCount the numer of v-structures in the given graph g.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 3, 2)
true
julia> countvstructs(setup_hs(g))
1PdagExtendability.pdag2mpdag2dag — Methodpdag2mpdag2dag(g::SimpleDiGraph)::SimpleDiGraphConvert a PDAG into a DAG. First, Meek's rules are applied exhaustively to the input graph, then it is checked whether cycles or new v-structures were formed (if so, the input is not extendable) and lastly, the resulting MPDAG is converted into a DAG in linear time.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> collect(edges(pdag2mpdag2dag(g)))
2-element Vector{LightGraphs.SimpleGraphs.SimpleEdge{Int64}}:
Edge 1 => 2
Edge 2 => 3Algorithms for Specific Types of Input Graphs
The following algorithms are implemented using HashSets internally as well, but they work only for specific types of input graphs.
dir2dag- An algorithm with worst-case time complexity $O(|V|+|E|)$ that works only for fully directed input graphs.undir2dag- An algorithm with worst-case time complexity $O(|V|+|E|)$ that works only for fully undirected input graphs.mpdag2dag- An algorithm with worst-case time complexity $O(|V|+|E|)$ that works only for maximally oriented partially directed acyclic graphs (MPDAGs).
PdagExtendability.dir2dag — Methoddir2dag(g::SimpleDiGraph)::SimpleDiGraphConvert a directed graph into a fully directed acyclic graph (DAG). If this is not possible, an empty graph is returned.
Note that this function only works for fully directed input graphs as it simply checks acyclicity of the input graph.
Important: The input must not contain two edges between the same two nodes (i.e., if an edge u->v exists, v->u is forbidden) because the internal data structure treats such edges as undirected edges.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 1, 3)
true
julia> collect(edges(dir2dag(g)))
2-element Vector{LightGraphs.SimpleGraphs.SimpleEdge{Int64}}:
Edge 1 => 2
Edge 1 => 3PdagExtendability.iscyclic! — Methodiscyclic!(g::DtGraph)::BoolChecks whether g is cyclic by computing a topological sorting using Kahn's algorithm. Note that g must be fully directed (undirected edges are forbidden).
Important: The input must not contain two edges between the same two nodes (i.e., if an edge u->v exists, v->u is forbidden) because the internal data structure treats such edges as undirected edges.
References
Kahn, Arthur B. (1962). Topological sorting of large networks. Communications of the ACM, 5
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 1, 3)
true
julia> iscyclic!(setup_hs(g))
falsePdagExtendability.ispeo — Methodispeo(g::DtGraph, ordering::Tuple{Vector{Int64}, Vector{Int64}})::BoolCheck whether ordering is a perfect elimination order. That is, for each vertex v, v and all neighbors coming after v form a clique.
Examples
julia> g = SimpleGraph(3)
{3, 0} undirected simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 1, 3)
true
julia> add_edge!(g, 2, 3)
julia> dt = setup_hs(graph2digraph(g))
...
julia> ispeo(dt, mcs(dt))
truePdagExtendability.mcs — Methodmcs(g::DtGraph)::Tuple{Vector{Int64}, Vector{Int64}}Compute a vertex ordering using maximum cardinality search.
References
Tarjan, Robert E.; Yannakakis, Mihalis (1984). Simple Linear-Time Algorithms to Test Chordality of Graphs, Test Acyclicity of Hypergraphs, and Selectively Reduce Acyclic Hypergraphs. SIAM Journal on Computing, 13(3), 566–579.
Examples
julia> g = SimpleGraph(3)
{3, 0} undirected simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 1, 3)
true
julia> add_edge!(g, 2, 3)
julia> dt = setup_hs(graph2digraph(g))
...
julia> mcs(dt)
([3, 1, 2], [2, 3, 1])PdagExtendability.undir2dag — Methodundir2dag(g::SimpleDiGraph)::SimpleDiGraphConvert an undirected graph into a fully directed acyclic graph (DAG). If this is not possible, an empty graph is returned.
Examples
julia> g = SimpleGraph(3)
{3, 0} undirected simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 1, 3)
true
julia> add_edge!(g, 2, 3)
true
julia> collect(edges(undir2dag(graph2digraph(g))))
3-element Vector{LightGraphs.SimpleGraphs.SimpleEdge{Int64}}:
Edge 2 => 1
Edge 2 => 3
Edge 3 => 1PdagExtendability.buckets — Methodbuckets(g::DtGraph)::Vector{Set{Int64}}Compute all buckets, i.e., maximum undirected subcomponents, of g.
References
M. Wienöbst, M. Bannach, M. Liśkiewicz (2021). Extendability of Causal Graphical Models: Algorithms and Computational Complexity. 37th Conference on Uncertainty in Artificial Intelligence, 2021 (UAI 2021).
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> buckets(setup_hs(g))
Set{Int64}[]
julia> add_edge!(g, 3, 2)
true
julia> buckets(setup_hs(g))
1-element Vector{Set{Int64}}:
Set([2, 3])PdagExtendability.dfs! — Methoddfs!(g::DtGraph, u::Int64, visited::BitArray)::Set{Int64}Start a depth first search at vertex u in graph g and visit only previously unvisited vertices. visited specifies for each vertex whether it was visited before or not and is updated accordingly. The result is a set of vertices reachable from u via undirected edges only.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> dfs!(setup_hs(g), 1, falses(3))
Set{Int64} with 1 element:
1
julia> dfs!(setup_hs(g), 2, falses(3))
Set{Int64} with 2 elements:
2
3PdagExtendability.directedge! — Methoddirectedge!(g::DtGraph, u::Int64, v::Int64)Direct an undirected edge u-v in g from u to v (u->v).
Examples
julia> g = SimpleDiGraph(2)
{2, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 1)
true
julia> dtgr = setup_hs(g)
DtGraph(
2,
Set([2, 1]),
Set{Int64}[],
Set{Int64}[Set(), Set()],
Set{Int64}[Set(), Set()],
Set{Int64}[Set([2]), Set([1])]
)
julia> directedge!(dtgr, 1, 2)
julia> dtgr
DtGraph(
2,
Set([2, 1]),
Set{Int64}[],
Set{Int64}[Set(), Set([1])],
Set{Int64}[Set([2]), Set()],
Set{Int64}[Set(), Set()]
)PdagExtendability.dtgraph2digraph — MethodExamples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> g == dtgraph2digraph(setup_hs(g))
truePdagExtendability.hascycledfs! — Methodhascycledfs!(g::DtGraph, stack::Vector{Int64}, visited::Vector{UInt8})::BoolCalled by hasdircycle to perform a depth first search checking for cycles in a graph.
PdagExtendability.hasdircycle — Methodhasdircycle(g::DtGraph)::BoolCheck whether the graph g contains a directed cycle.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 1)
true
julia> hasdircycle(setup_hs(g))
truePdagExtendability.ismpdag — Methodismpdag(g::SimpleDiGraph)::BoolCheck whether a given graph g is an MPDAG.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 1)
true
julia> ismpdag(g)
falsePdagExtendability.pdag2mpdag — Methodpdag2mpdag(g; nocopy = false)::DtGraphApply the four Meek Rules to the input PDAG in order to obtain an MPDAG. The input must either be of type SimpleDiGraph or DtGraph. In case the input has type DtGraph, the paramter nocopy can be set to true in order to manipulate it directly instead of creating a copy.
References
Meek, C. (1995). Causal Inference and Causal Explanation with Background Knowledge. In Proceedings of the Eleventh Conference on Uncertainty in Artificial Intelligence, UAI’95.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> add_edge!(g, 3, 2)
true
julia> collect(edges(dtgraph2digraph(pdag2mpdag(g))))
2-element Vector{LightGraphs.SimpleGraphs.SimpleEdge{Int64}}:
Edge 1 => 2
Edge 2 => 3PdagExtendability.amo — Methodamo(g::DtGraph)::Tuple{Vector{Int64}, Vector{Int64}}Compute an acyclic moral orientation (AMO) for a bucket in linear time. If the input graph does not admit an AMO, the result will not be an AMO but an arbitrary ordering. The result can be checked via isamo.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> amo(setup_hs(g))
([1, 2, 3], [1, 2, 3])PdagExtendability.isamo — Methodisamo(g::DtGraph, ordering::Tuple{Vector{Int64}, Vector{Int64}})::BoolCheck whether a given ordering is an acyclic moral orientation for a bucket.
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> dtgraph = setup_hs(g)
DtGraph(
3,
Set([2, 3, 1]),
Set{Int64}[],
Set{Int64}[Set(), Set([1]), Set([2])],
Set{Int64}[Set([2]), Set([3]), Set()],
Set{Int64}[Set(), Set(), Set()]
)
julia> isamo(dtgraph, amo(dtgraph))
truePdagExtendability.mpdag2dag — Methodmpdag2dag(g::SimpleDiGraph)::SimpleDiGraphConvert a given MPDAG to a consistent DAG extension. Only works for MPDAGs.
References
M. Wienöbst, M. Bannach, M. Liśkiewicz (2021). Extendability of Causal Graphical Models: Algorithms and Computational Complexity. 37th Conference on Uncertainty in Artificial Intelligence, 2021 (UAI 2021).
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> collect(edges(mpdag2dag(g)))
2-element Vector{LightGraphs.SimpleGraphs.SimpleEdge{Int64}}:
Edge 1 => 2
Edge 2 => 3PdagExtendability.subgraph — Methodsubgraph(g::DtGraph, bucket::Set{Int64}, m::Dict)::DtGraphCompute the subgraph induces by the vertices in bucket. m is a mapping for vertices which is necessary if the vertices in bucket are not labelled from 1 to length(bucket). It has to map each vertex in bucket to a number between 1 and length(bucket).
Examples
julia> g = SimpleDiGraph(3)
{3, 0} directed simple Int64 graph
julia> add_edge!(g, 1, 2)
true
julia> add_edge!(g, 2, 3)
true
julia> s = subgraph(setup_hs(g), Set([1, 2]), Dict(1 => 1, 2 => 2))
DtGraph(
2,
Set([2, 1]),
Set{Int64}[],
Set{Int64}[Set(), Set([1])],
Set{Int64}[Set([2]), Set()],
Set{Int64}[Set(), Set()]
)
julia> collect(edges(dtgraph2digraph(s)))
1-element Vector{LightGraphs.SimpleGraphs.SimpleEdge{Int64}}:
Edge 1 => 2Debugging
In practice, the algorithm pdag2dag_hs may sometimes be faster than fastpdag2dag_hs, although it has a worse time complexity. Thus, one might be interested to find out how many iterations were actually needed by the algorithm pdag2dag_hs to compute the result.
The debug version pdag2dag_debug_hs logs the average number of iterations needed to find a potential sink in the input graph. Note that a potential sink is searched $|V|$-times, i.e., the total number of iterations can be approximated by multiplying the outputted average with $|V|$.
pdag2dag_debug_lg does the same for the implementation using the LightGraphs library internally.
PdagExtendability.is_sink_debug_hs — Methodis_sink_debug_hs(graph::DtGraph, x::Int64)::Tuple{Bool, Int64}Debug version of is_sink_hs. The debug version counts the number of iterations needed to check whether x is a sink.
PdagExtendability.pdag2dag_debug_hs — Functionpdag2dag_debug_hs(g::SimpleDiGraph, useheuristic::Bool = false)::SimpleDiGraphDebug version of pdag2dag_hs. The debug version logs the average number of iterations needed to find a sink.
PdagExtendability.sink_debug_hs — Functionsink_debug_hs(graph::DtGraph, useheuristic::Bool = false)::Tuple{Int64, Int64, Vector{Int64}}Debug version of sink_hs. The debug version counts the number of iterations needed to find a sink and computes the average number of iterations needed in the whole algorithm.
PdagExtendability.pdag2dag_debug_lg — Methodpdag2dag_debug_lg(g::SimpleDiGraph)::SimpleDiGraphDebug version of pdag2dag_lg.
PdagExtendability.sink_debug_lg — Methodsink_debug_lg(g::SimpleDiGraph)::Tuple{Int64, Int64}Debug version of sink_lg.