causal-learn icon indicating copy to clipboard operation
causal-learn copied to clipboard

Add required edges by background knowledge

Open lmz123321 opened this issue 3 years ago • 1 comments

Hi, Thanks for your great work.

I hope to add an edge that is not detected during the PC skeleton phase according to background knowledge. I read the codes in causal-learn/causallearn/utils/PCUtils/BackgroundKnowledgeOrientUtils.py and it looks like only edges already detected in the skeleton phase can be defined as 'required'.

Is there any method to solve my problem? Can I direct add the following lines in BackgroundKnowledgeOrientUtils.py?

def orient_by_background_knowledge(cg: CausalGraph, background_knowledge: BackgroundKnowledge):
    """
    orient the direction of edges using background background_knowledge after running skeleton_discovery in PC algorithm

    Parameters
    ----------
    cg: a CausalGraph object. Where cg.G.graph[j,i]=1 and cg.G.graph[i,j]=-1 indicates  i -> j ,
                    cg.G.graph[i,j] = cg.G.graph[j,i] = -1 indicates i -- j,
                    cg.G.graph[i,j] = cg.G.graph[j,i] = 1 indicates i <-> j.
    background_knowledge: artificial background background_knowledge

    Returns
    -------

    """
    if type(cg) != CausalGraph or (type(background_knowledge) != BackgroundKnowledge and type(background_knowledge) != CustomBackgroundKnowledge):
        raise TypeError(
            'cg must be type of CausalGraph and background_knowledge must be type of BackgroundKnowledge. cg = ' + str(
                type(cg)) + ' background_knowledge = ' + str(type(background_knowledge)))
    for edge in cg.G.get_graph_edges():
        if cg.G.is_undirected_from_to(edge.get_node1(), edge.get_node2()):
            if background_knowledge.is_forbidden(edge.get_node2(), edge.get_node1()):
                cg.G.remove_edge(edge)
                cg.G.add_directed_edge(edge.get_node1(), edge.get_node2())
            elif background_knowledge.is_forbidden(edge.get_node1(), edge.get_node2()):
                cg.G.remove_edge(edge)
                cg.G.add_directed_edge(edge.get_node2(), edge.get_node1())
            elif background_knowledge.is_required(edge.get_node2(), edge.get_node1()):
                cg.G.remove_edge(edge)
                cg.G.add_directed_edge(edge.get_node2(), edge.get_node1())
            elif background_knowledge.is_required(edge.get_node1(), edge.get_node2()):
                cg.G.remove_edge(edge)
                cg.G.add_directed_edge(edge.get_node1(), edge.get_node2())

    # custom change to add required edges
    for node1 in cg.G.get_nodes():
        for node2 in cg.G.get_nodes():
            if not cg.G.is_undirected_from_to(node1,node2):
                if background_knowledge.is_required(node1,node2):
                    cg.G.add_directed_edge(node1,node2)
                elif background_knowledge.is_required(node2,node1):
                    cg.G.add_directed_edge(node2,node1)

lmz123321 avatar Sep 19 '22 09:09 lmz123321

Hi, unfortunately, we don't have the functionality you described for now. We're not sure about the custom changes now, but we will try and see whether it's plausible.

kunwuz avatar Sep 25 '22 02:09 kunwuz