Skip to content
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

[ENH] Add the ability to find Proper Possibly Directed Paths #112

Merged
merged 30 commits into from
Aug 26, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ccfa3b4
Added function skeleton
aryan26roy Jun 8, 2024
4260d19
Merge branch 'main' into ppdp_aryan
adam2392 Jul 8, 2024
97144ba
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 8, 2024
66e89d8
Merge branch 'main' into ppdp_aryan
adam2392 Jul 15, 2024
a5caac1
Some more code
aryan26roy Jul 27, 2024
8f316ce
merge conflict resolved
aryan26roy Jul 27, 2024
415085e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 27, 2024
bf09476
fixed issue
aryan26roy Jul 27, 2024
6c4806b
Merge branch 'ppdp_aryan' of github.com:aryan26roy/pywhy-graphs into …
aryan26roy Jul 27, 2024
06fe76f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jul 27, 2024
e940486
added more code
aryan26roy Aug 7, 2024
31c6069
completed the implementation
aryan26roy Aug 10, 2024
fd5736c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 10, 2024
d235b2f
Added more tests
aryan26roy Aug 10, 2024
0acd084
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 10, 2024
f0ef279
Switched to set<tuple>
aryan26roy Aug 15, 2024
7296014
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 15, 2024
7f01c98
Made some functions private
aryan26roy Aug 15, 2024
6ad4765
Added docstring
aryan26roy Aug 15, 2024
b15843b
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 15, 2024
ae6ae4e
added changelog
aryan26roy Aug 15, 2024
266bd2f
Merge branch 'ppdp_aryan' of github.com:aryan26roy/pywhy-graphs into …
aryan26roy Aug 15, 2024
6ebbc4a
changed docstring
aryan26roy Aug 16, 2024
7bc6f75
Update pywhy_graphs/algorithms/generic.py
aryan26roy Aug 20, 2024
1828221
incorporated review suggestions
aryan26roy Aug 23, 2024
7a25931
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Aug 23, 2024
76ca2f7
improved docstring
aryan26roy Aug 23, 2024
d54e482
Merge branch 'ppdp_aryan' of github.com:aryan26roy/pywhy-graphs into …
aryan26roy Aug 23, 2024
440c8bb
cleanup
aryan26roy Aug 23, 2024
0802ef0
Merge branch 'main' into ppdp_aryan
adam2392 Aug 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions pywhy_graphs/algorithms/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"dag_to_mag",
"is_maximal",
"all_vstructures",
"possibly_directed_path",
]


Expand Down Expand Up @@ -855,3 +856,99 @@ def all_vstructures(G: nx.DiGraph, as_edges: bool = False):
else:
vstructs.add((p1, node, p2)) # type: ignore
return vstructs


def check_back_arrow(G: ADMG, X, Y: set):
aryan26roy marked this conversation as resolved.
Show resolved Hide resolved

out = set()

for elem in Y:
if not (
G.has_edge(X, elem, G.bidirected_edge_name) or G.has_edge(elem, X, G.directed_edge_name)
):
out.update(elem)

return out


def get_X_neighbors(G, X: set):

out = []

for elem in X:
elem_neighbors = set(G.neighbors(elem))
elem_possible_neighbors = check_back_arrow(G, elem, elem_neighbors)
to_remove = X.intersection(elem_possible_neighbors)
elem_neighbors = elem_possible_neighbors - to_remove

if len(elem_neighbors) != 0:
for nbh in elem_neighbors:
temp = dict()
temp[0] = elem
temp[1] = nbh
out.append(temp)
return out


def recursively_find_pd_paths(G, X, paths, Y):

counter = 0
new_paths = []

for i in range(len(paths)):
cur_elem = paths[i][list(paths[i].keys())[-1]]

if cur_elem in Y:
new_paths.append(paths[i])
continue

nbr_temp = G.neighbors(cur_elem)
nbr_possible = check_back_arrow(G, cur_elem, nbr_temp)

if len(nbr_possible) == 0:
new_paths.append(paths[i].copy())

possible_end = nbr_possible.intersection(Y)

if len(possible_end) != 0:
for elem in possible_end:
temp_path = paths[i].copy()
temp_path[len(temp_path)] = elem
new_paths.append(temp_path)

remaining_nodes = nbr_possible - possible_end
remaining_nodes = (
remaining_nodes
- remaining_nodes.intersection(paths[i].values())
- remaining_nodes.intersection(X)
)

temp_arr = []
for elem in remaining_nodes:
temp_paths = paths[i].copy()
temp_paths[len(temp_paths)] = elem
temp_arr.append(temp_paths)

new_paths.extend(recursively_find_pd_paths(G, X, temp_arr, Y))

return new_paths


def possibly_directed_path(G, X: Optional[Set] = None, Y: Optional[Set] = None):
aryan26roy marked this conversation as resolved.
Show resolved Hide resolved

if isinstance(X, set):
x_neighbors = get_X_neighbors(G, X)
else:
nbr_temp = G.neighbors(X)
nbr_possible = check_back_arrow(nbr_temp)
x_neighbors = []

for elem in nbr_possible:
temp = dict()
temp[0] = X
temp[1] = elem
x_neighbors.append(temp)
aryan26roy marked this conversation as resolved.
Show resolved Hide resolved

path_list = recursively_find_pd_paths(G, X, x_neighbors, Y)

return path_list
206 changes: 205 additions & 1 deletion pywhy_graphs/algorithms/tests/test_generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pytest

import pywhy_graphs
from pywhy_graphs import ADMG
from pywhy_graphs import ADMG, PAG
from pywhy_graphs.algorithms import all_vstructures


Expand Down Expand Up @@ -496,3 +496,207 @@ def test_all_vstructures():
# Assert that the returned values are as expected
assert len(v_structs_edges) == 0
assert len(v_structs_tuples) == 0


def test_possibly_directed():
# X <- Y <-> Z <-> H; Z -> X

admg = ADMG()
admg.add_edge("Y", "X", admg.directed_edge_name)
admg.add_edge("X", "Z", admg.directed_edge_name)
admg.add_edge("Z", "H", admg.directed_edge_name)

Y = {"H"}
X = {"Y"}

correct = [{0: "Y", 1: "X", 2: "Z", 3: "H"}]
out = pywhy_graphs.possibly_directed_path(admg, X, Y)
assert correct[0] == out[0]

admg = ADMG()
admg.add_edge("A", "X", admg.directed_edge_name)
admg.add_edge("Y", "X", admg.directed_edge_name)
admg.add_edge("X", "Z", admg.directed_edge_name)
admg.add_edge("Z", "H", admg.directed_edge_name)

Y = {"H"}
X = {"Y", "A"}

correct = [{0: "A", 1: "X", 2: "Z", 3: "H"}, {0: "Y", 1: "X", 2: "Z", 3: "H"}]
out = pywhy_graphs.possibly_directed_path(admg, X, Y)
assert correct[0] == out[0]
assert correct[1] == out[1]

admg = ADMG()
admg.add_edge("X", "A", admg.directed_edge_name)
admg.add_edge("Y", "X", admg.directed_edge_name)
admg.add_edge("X", "Z", admg.directed_edge_name)
admg.add_edge("Z", "H", admg.directed_edge_name)

Y = {"H"}
X = {"Y", "A"}

correct = [{0: "Y", 1: "X", 2: "Z", 3: "H"}]
out = pywhy_graphs.possibly_directed_path(admg, X, Y)
assert correct[0] == out[0]

admg = ADMG()
admg.add_edge("X", "A", admg.directed_edge_name)
admg.add_edge("Y", "X", admg.directed_edge_name)
admg.add_edge("X", "Z", admg.directed_edge_name)
admg.add_edge("Z", "H", admg.directed_edge_name)
admg.add_edge("K", "Z", admg.directed_edge_name)

Y = {"H", "K"}
X = {"Y", "A"}

correct = [{0: "Y", 1: "X", 2: "Z", 3: "H"}]
out = pywhy_graphs.possibly_directed_path(admg, X, Y)
assert correct[0] == out[0]

admg = ADMG()
admg.add_edge("A", "X", admg.directed_edge_name)
admg.add_edge("Y", "X", admg.directed_edge_name)
admg.add_edge("X", "Z", admg.directed_edge_name)
admg.add_edge("Z", "H", admg.directed_edge_name)
admg.add_edge("Z", "K", admg.directed_edge_name)

Y = {"H", "K"}
X = {"Y", "A"}

correct = [
{0: "A", 1: "X", 2: "Z", 3: "H"},
{0: "A", 1: "X", 2: "Z", 3: "K"},
{0: "Y", 1: "X", 2: "Z", 3: "H"},
{0: "Y", 1: "X", 2: "Z", 3: "K"},
]
out = pywhy_graphs.possibly_directed_path(admg, X, Y)
assert correct[0] == out[0]
assert correct[1] == out[1]
assert correct[2] == out[2]
assert correct[3] == out[3]

admg = ADMG()
admg.add_edge("A", "G", admg.directed_edge_name)
admg.add_edge("G", "C", admg.directed_edge_name)
admg.add_edge("C", "H", admg.directed_edge_name)
admg.add_edge("Y", "X", admg.directed_edge_name)
admg.add_edge("X", "Z", admg.directed_edge_name)
admg.add_edge("Z", "K", admg.directed_edge_name)

Y = {"H", "K"}
X = {"Y", "A"}

correct = [{0: "A", 1: "G", 2: "C", 3: "H"}, {0: "Y", 1: "X", 2: "Z", 3: "K"}]
out = pywhy_graphs.possibly_directed_path(admg, X, Y)
assert correct[0] == out[0]
assert correct[1] == out[1]

admg = ADMG()
admg.add_edge("A", "G", admg.directed_edge_name)
admg.add_edge("G", "C", admg.directed_edge_name)
admg.add_edge("C", "H", admg.directed_edge_name)
admg.add_edge("Z", "C", admg.directed_edge_name)
admg.add_edge("Y", "X", admg.directed_edge_name)
admg.add_edge("X", "Z", admg.directed_edge_name)
admg.add_edge("Z", "K", admg.directed_edge_name)

Y = {"H", "K"}
X = {"Y", "A"}

correct = [
{0: "A", 1: "G", 2: "C", 3: "H"},
{0: "Y", 1: "X", 2: "Z", 3: "K"},
{0: "Y", 1: "X", 2: "Z", 3: "C", 4: "H"},
]
out = pywhy_graphs.possibly_directed_path(admg, X, Y)
assert correct[0] == out[0]
assert correct[1] == out[1]
assert correct[2] == out[2]

admg = ADMG()
admg.add_edge("A", "G", admg.directed_edge_name)
admg.add_edge("A", "H", admg.directed_edge_name)
admg.add_edge("K", "G", admg.directed_edge_name)
admg.add_edge("K", "H", admg.directed_edge_name)

Y = {"G", "H"}
X = {"A", "K"}

correct = [{0: "K", 1: "G"}, {0: "K", 1: "H"}, {0: "A", 1: "G"}, {0: "A", 1: "H"}]
out = pywhy_graphs.possibly_directed_path(admg, X, Y)
assert correct[0] == out[0]
assert correct[1] == out[1]
assert correct[2] == out[2]
assert correct[3] == out[3]

admg = ADMG()
admg.add_edge("A", "G", admg.directed_edge_name)
admg.add_edge("G", "C", admg.directed_edge_name)
admg.add_edge("C", "H", admg.directed_edge_name)
admg.add_edge("Z", "C", admg.bidirected_edge_name)
admg.add_edge("Y", "X", admg.directed_edge_name)
admg.add_edge("X", "Z", admg.directed_edge_name)
admg.add_edge("Z", "K", admg.directed_edge_name)

Y = {"H", "K"}
X = {"Y", "A"}

correct = [
{0: "A", 1: "G", 2: "C", 3: "H"},
{0: "Y", 1: "X", 2: "Z", 3: "K"},
]
out = pywhy_graphs.possibly_directed_path(admg, X, Y)
assert correct[0] == out[0]
assert correct[1] == out[1]

admg = ADMG()
admg.add_edge("A", "G", admg.directed_edge_name)
admg.add_edge("G", "C", admg.directed_edge_name)
admg.add_edge("C", "H", admg.directed_edge_name)
admg.add_edge("Z", "C", admg.bidirected_edge_name)
admg.add_edge("Y", "X", admg.directed_edge_name)
admg.add_edge("X", "Z", admg.directed_edge_name)
admg.add_edge("Z", "K", admg.directed_edge_name)

Y = {"H", "K"}
X = {"Y", "A"}

correct = [
{0: "A", 1: "G", 2: "C", 3: "H"},
{0: "Y", 1: "X", 2: "Z", 3: "K"},
{0: "Y", 1: "X", 2: "Z", 3: "C", 4: "H"},
{0: "A", 1: "G", 2: "C", 3: "Z", 4: "K"},
]
out = pywhy_graphs.possibly_directed_path(admg, X, Y)
assert correct[0] == out[0]
assert correct[1] == out[1]
assert correct[2] == out[2]

admg = PAG()
admg.add_edge("A", "G", admg.directed_edge_name)
admg.add_edge("G", "C", admg.directed_edge_name)
admg.add_edge("C", "H", admg.directed_edge_name)
admg.add_edge("Z", "C", admg.circle_edge_name)
admg.add_edge("C", "Z", admg.circle_edge_name)
admg.add_edge("Y", "X", admg.directed_edge_name)
admg.add_edge("X", "Z", admg.directed_edge_name)
admg.add_edge("Z", "K", admg.directed_edge_name)

Y = {"H", "K"}
X = {"Y", "A"}

correct = [
[
{0: "Y", 1: "X", 2: "Z", 3: "K"},
{0: "Y", 1: "X", 2: "Z", 3: "C", 4: "H"},
{0: "A", 1: "G", 2: "C", 3: "H"},
{0: "A", 1: "G", 2: "C", 3: "Z", 4: "K"},
]
]

out = pywhy_graphs.possibly_directed_path(admg, X, Y)
assert correct[0] == out[0]
assert correct[1] == out[1]
assert correct[2] == out[2]
assert correct[3] == out[3]
Loading