示例#1
0
文件: generate.py 项目: Jamesfox1/cid
def _add_sufficient_recall(cid: CID, dec1: str, dec2: str,
                           utility_node: str) -> None:
    """Add edges to a cid until `dec2` has sufficient recall of `dec1` (to optimize utility)

    this is done by adding edges from non-collider nodes until recall is adequate
    """

    if dec2 in cid._get_ancestors_of(dec1):
        raise ValueError('{} is an ancestor of {}'.format(dec2, dec1))

    cid2 = cid.copy()
    cid2.add_edge('pi', dec1)

    while cid2.is_active_trail('pi',
                               utility_node,
                               observed=cid.get_parents(dec2) + [dec2]):
        path = find_active_path(cid2, 'pi', utility_node,
                                cid.get_parents(dec2) + [dec2])
        if path is None:
            raise Exception(
                "couldn't find path even though there should be an active trail"
            )
        while True:
            i = random.randrange(1, len(path) - 1)
            # print('consider {}--{}--{}'.format(path[i-1], path[i], path[i+1]),end='')
            collider = ((path[i - 1], path[i]) in cid2.edges) and (
                (path[i + 1], path[i]) in cid2.edges)
            if not collider:
                if dec2 not in cid2._get_ancestors_of(path[i]):
                    # print('add {}->{}'.format(path[i], dec2), end=' ')
                    cid.add_edge(path[i], dec2)
                    cid2.add_edge(path[i], dec2)
                    break
示例#2
0
def admits_voi(cid: CID, decision: str, node: str) -> bool:
    """Return True if cid admits value of information for node.
    - A CID admits value of information for a node X if:
    i) X is not a descendant of the decision node, D.
    ii) X is d-connected to U given Fa_D \ {X}, where U ∈ U ∩ Desc(D)
    ("Agent Incentives: a Causal Perspective" by Everitt, Carey, Langlois, Ortega, and Legg, 2020)
    """
    agent_utilities = cid.all_utility_nodes

    if node not in cid.nodes:
        raise Exception(f"{node} is not present in the cid")
    if decision not in cid.nodes:
        raise Exception(f"{decision} is not present in the cid")

    # condition (i)
    elif node == decision or node in nx.descendants(cid, decision):
        return False
    # condition (ii)
    descended_agent_utilities = [
        util for util in agent_utilities
        if util in nx.descendants(cid, decision)
    ]
    d_family = [decision] + cid.get_parents(decision)
    con_nodes = [i for i in d_family if i != node]
    voi = any([
        cid.is_active_trail(node, u_node, con_nodes)
        for u_node in descended_agent_utilities
    ])
    return voi
示例#3
0
def admits_indir_voc(cid: CID, decision: str, node: str) -> bool:
    """
    Return True if a single-decision cid admits indirect positive value of control for node.
    - A single-decision CID G admits positive value of control for a node X ∈ V \ {D} if and
    only if there is a directed path X --> U in the reduced graph G∗.
    - The path X --> U may or may not pass through D.
    - The agent has a direct value of control incentive on D if the path does not pass through D.
    - The agent has an indirect value of control incentive on D if the path does pass through D
    and there is also a backdoor path X--U that begins backwards from X (...<- X) and is
    active when conditioning on Fa_D \ {X}
    """
    if node not in cid.nodes:
        raise Exception(f"{node} is not present in the cid")
    if decision not in cid.nodes:
        raise Exception(f"{decision} is not present in the cid")

    agent_utilities = cid.all_utility_nodes
    req_graph = requisite_graph(cid)
    d_family = [decision] + cid.get_parents(decision)
    con_nodes = [i for i in d_family if i != node]
    if not admits_voc(cid, decision, node):
        return False

    for util in agent_utilities:
        if node == util or util in nx.descendants(req_graph, node):
            backdoor_exists = is_active_backdoor_trail(req_graph, node, util,
                                                       con_nodes)
            x_u_paths = find_all_dir_paths(req_graph, node, util)
            if any(decision in paths
                   for paths in x_u_paths) and backdoor_exists:
                return True

    return False
示例#4
0
文件: generate.py 项目: Jamesfox1/cid
def random_cid(n_all: int,
               n_decisions: int,
               n_utilities: int,
               edge_density: float = 0.4,
               add_sr_edges: bool = True,
               add_cpds: bool = True,
               seed: int = None) -> CID:
    """Generates a random Cid with the specified number of nodes and edges"""

    all_names, decision_names, utility_names = get_node_names(
        n_all, n_decisions, n_utilities)
    edges = get_edges(all_names,
                      utility_names,
                      edge_density,
                      seed=seed,
                      allow_u_edges=False)
    cid = CID(edges, decision_names, utility_names)

    for uname in utility_names:
        for edge in edges:
            assert uname != edge[0]

    for i, d1 in enumerate(decision_names):
        for j, d2 in enumerate(decision_names[i + 1:]):
            assert d2 not in cid._get_ancestors_of(d1)

    if add_sr_edges:
        add_sufficient_recalls(cid)

    if add_cpds:
        for node in cid.nodes:
            if node in cid.all_decision_nodes:
                cid.add_cpds(DecisionDomain(node, [0, 1]))
            elif not cid.get_parents(node):  # node is a root node
                cid.add_cpds(UniformRandomCPD(node, [0, 1]))
            else:
                cid.add_cpds(
                    RandomlySampledFunctionCPD(node, cid.get_parents(node)))
    return cid