Skip to content

Commit a160a5d

Browse files
committed
Add option to control depth in PC algorithm
1 parent 474437c commit a160a5d

2 files changed

Lines changed: 21 additions & 11 deletions

File tree

causallearn/search/ConstraintBased/PC.py

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def pc(
3030
verbose: bool = False,
3131
show_progress: bool = True,
3232
node_names: List[str] | None = None,
33+
max_k: int = None,
3334
**kwargs
3435
):
3536
if data.shape[0] < data.shape[1]:
@@ -41,11 +42,11 @@ def pc(
4142
return mvpc_alg(data=data, node_names=node_names, alpha=alpha, indep_test=indep_test, correction_name=correction_name, stable=stable,
4243
uc_rule=uc_rule, uc_priority=uc_priority, background_knowledge=background_knowledge,
4344
verbose=verbose,
44-
show_progress=show_progress, **kwargs)
45+
show_progress=show_progress, max_k=max_k, **kwargs)
4546
else:
4647
return pc_alg(data=data, node_names=node_names, alpha=alpha, indep_test=indep_test, stable=stable, uc_rule=uc_rule,
4748
uc_priority=uc_priority, background_knowledge=background_knowledge, verbose=verbose,
48-
show_progress=show_progress, **kwargs)
49+
show_progress=show_progress, max_k=max_k, **kwargs)
4950

5051

5152
def pc_alg(
@@ -59,6 +60,7 @@ def pc_alg(
5960
background_knowledge: BackgroundKnowledge | None = None,
6061
verbose: bool = False,
6162
show_progress: bool = True,
63+
max_k=None,
6264
**kwargs
6365
) -> CausalGraph:
6466
"""
@@ -103,7 +105,7 @@ def pc_alg(
103105
indep_test = CIT(data, indep_test, **kwargs)
104106
cg_1 = SkeletonDiscovery.skeleton_discovery(data, alpha, indep_test, stable,
105107
background_knowledge=background_knowledge, verbose=verbose,
106-
show_progress=show_progress, node_names=node_names)
108+
show_progress=show_progress, node_names=node_names, max_k=max_k)
107109

108110
if background_knowledge is not None:
109111
orient_by_background_knowledge(cg_1, background_knowledge)
@@ -142,14 +144,15 @@ def mvpc_alg(
142144
data: ndarray,
143145
node_names: List[str] | None,
144146
alpha: float,
145-
indep_test: str,
147+
indep_test: Any,
146148
correction_name: str,
147149
stable: bool,
148150
uc_rule: int,
149151
uc_priority: int,
150152
background_knowledge: BackgroundKnowledge | None = None,
151153
verbose: bool = False,
152154
show_progress: bool = True,
155+
max_k: int | None = None,
153156
**kwargs,
154157
) -> CausalGraph:
155158
"""
@@ -197,14 +200,14 @@ def mvpc_alg(
197200
start = time.time()
198201
indep_test = CIT(data, indep_test, **kwargs)
199202
## Step 1: detect the direct causes of missingness indicators
200-
prt_m = get_parent_missingness_pairs(data, alpha, indep_test, stable)
203+
prt_m = get_parent_missingness_pairs(data, alpha, indep_test, stable, max_k=max_k)
201204
# print('Finish detecting the parents of missingness indicators. ')
202205

203206
## Step 2:
204207
## a) Run PC algorithm with the 1st step skeleton;
205208
cg_pre = SkeletonDiscovery.skeleton_discovery(data, alpha, indep_test, stable,
206209
background_knowledge=background_knowledge,
207-
verbose=verbose, show_progress=show_progress, node_names=node_names)
210+
verbose=verbose, show_progress=show_progress, node_names=node_names, max_k=max_k)
208211
if background_knowledge is not None:
209212
orient_by_background_knowledge(cg_pre, background_knowledge)
210213

@@ -251,7 +254,7 @@ def mvpc_alg(
251254

252255
#######################################################################################################################
253256
## *********** Functions for Step 1 ***********
254-
def get_parent_missingness_pairs(data: ndarray, alpha: float, indep_test, stable: bool = True) -> Dict[str, list]:
257+
def get_parent_missingness_pairs(data: ndarray, alpha: float, indep_test, stable: bool = True, max_k: int | None = None) -> Dict[str, list]:
255258
"""
256259
Detect the parents of missingness indicators
257260
If a missingness indicator has no parent, it will not be included in the result
@@ -272,7 +275,7 @@ def get_parent_missingness_pairs(data: ndarray, alpha: float, indep_test, stable
272275
## Get the index of parents of missingness indicators
273276
# If the missingness indicator has no parent, then it will not be collected in prt_m
274277
for missingness_i in missingness_index:
275-
parent_of_missingness_i = detect_parent(missingness_i, data, alpha, indep_test, stable)
278+
parent_of_missingness_i = detect_parent(missingness_i, data, alpha, indep_test, stable, max_k=max_k)
276279
if not isempty(parent_of_missingness_i):
277280
parent_missingness_pairs['prt'].append(parent_of_missingness_i)
278281
parent_missingness_pairs['m'].append(missingness_i)
@@ -299,7 +302,7 @@ def get_missingness_index(data: ndarray) -> List[int]:
299302
return missingness_index
300303

301304

302-
def detect_parent(r: int, data_: ndarray, alpha: float, indep_test, stable: bool = True) -> ndarray:
305+
def detect_parent(r: int, data_: ndarray, alpha: float, indep_test: Any, stable: bool = True, max_k: int | None = None) -> ndarray:
303306
"""Detect the parents of a missingness indicator
304307
:param r: the missingness indicator
305308
:param data_: data set (numpy ndarray)
@@ -334,15 +337,19 @@ def detect_parent(r: int, data_: ndarray, alpha: float, indep_test, stable: bool
334337

335338
no_of_var = data.shape[1]
336339
cg = CausalGraph(no_of_var)
337-
cg.set_ind_test(CIT(data, indep_test.method))
340+
cg.set_ind_test(indep_test)
341+
338342

339343
node_ids = range(no_of_var)
340344
pair_of_variables = list(permutations(node_ids, 2))
341345

342346
depth = -1
343347
while cg.max_degree() - 1 > depth:
344348
depth += 1
349+
if max_k is not None and depth > max_k:
350+
break
345351
edge_removal = []
352+
346353
for (x, y) in pair_of_variables:
347354

348355
## *********** Adaptation 2 ***********
@@ -495,3 +502,4 @@ def matrix_diff(cg1: CausalGraph, cg2: CausalGraph) -> (float, List[Tuple[int, i
495502
diff_ls.append((i, j))
496503
count += 1
497504
return count / 2, diff_ls
505+

causallearn/utils/PCUtils/SkeletonDiscovery.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def skeleton_discovery(
2121
background_knowledge: BackgroundKnowledge | None = None,
2222
verbose: bool = False,
2323
show_progress: bool = True,
24-
node_names: List[str] | None = None,
24+
node_names: List[str] | None = None, max_k=None,
2525
) -> CausalGraph:
2626
"""
2727
Perform skeleton discovery
@@ -63,6 +63,8 @@ def skeleton_discovery(
6363
pbar = tqdm(total=no_of_var) if show_progress else None
6464
while cg.max_degree() - 1 > depth:
6565
depth += 1
66+
if max_k is not None and depth > max_k:
67+
break
6668
edge_removal = []
6769
if show_progress:
6870
pbar.reset()

0 commit comments

Comments
 (0)