Esempio n. 1
0
def admits_ri(cid: CID, decision: str, node: str) -> bool:
    r"""Check if a CID admits a response incentive on a node.

     - A CID G admits a response incentive on X ∈ V \ {D} if
    and only if the reduced graph G* min has a directed path X --> D.
    ("Agent Incentives: a Causal Perspective" by Everitt, Carey, Langlois, Ortega, and Legg, 2020)
    """
    if len(cid.agents) > 1:
        raise ValueError(
            f"This CID has {len(cid.agents)} agents. This incentive is currently only valid for CIDs with one agent."
        )

    if node not in cid.nodes:
        raise KeyError(f"{node} is not present in the cid")
    if decision not in cid.nodes:
        raise KeyError(f"{decision} is not present in the cid")
    if not cid.sufficient_recall():
        raise ValueError(
            "Response inventives are only implemented for graphs with sufficient recall"
        )
    if node == decision:
        return False

    req_graph = requisite_graph(cid)
    try:
        next(find_all_dir_paths(req_graph, node, decision))
    except StopIteration:
        return False
    else:
        return True
Esempio n. 2
0
def admits_voc(cid: CID, node: str) -> bool:
    """Check if a CID admits positive value of control for a node.

    A CID G admits positive value of control for a node X ∈ V
    if and only if X is not a decision node and there is a directed path X --> U
    in the reduced graph G∗.
    """
    if len(cid.agents) > 1:
        raise ValueError(
            f"This CID has {len(cid.agents)} agents. This incentive is currently only valid for CIDs with one agent."
        )

    if node not in cid.nodes:
        raise KeyError(f"{node} is not present in the cid")
    if not cid.sufficient_recall():
        raise ValueError("VoC only implemented graphs with sufficient recall")
    if node in cid.decisions:
        return False

    req_graph = requisite_graph(cid)
    agent_utilities = cid.utilities

    for util in agent_utilities:
        if node == util or util in nx.descendants(req_graph, node):
            return True

    return False
Esempio n. 3
0
def admits_dir_voc(cid: CID, node: str) -> bool:
    r"""Check if a CID admits direct positive value of control for a node.

    - A 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 requisite graph G∗.
    - The path X --> U may or may not pass through decisions.
    - The agent has a direct value of control incentive on D if the path does not pass through a decision.
    - The agent has an indirect value of control incentive on D if the path does pass through any decision
    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 len(cid.agents) > 1:
        raise ValueError(
            f"This CID has {len(cid.agents)} agents. This incentive is currently only valid for CIDs with one agent."
        )

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

    agent_utilities = cid.utilities
    req_graph = requisite_graph(cid)

    if not admits_voc(cid, node):
        return False

    req_graph_node_descendants = set(nx.descendants(req_graph, node))
    for util in agent_utilities:
        if util != node and util not in req_graph_node_descendants:
            continue
        for path in find_all_dir_paths(req_graph, node, util):
            if not set(path).intersection(cid.decisions):
                return True

    return False
Esempio n. 4
0
def admits_voi(cid: CID, decision: str, node: str) -> bool:
    r"""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)
    """
    if len(cid.agents) > 1:
        raise ValueError(
            f"This CID has {len(cid.agents)} agents. This incentive is currently only valid for CIDs with one agent."
        )

    if node not in cid.nodes:
        raise KeyError(f"{node} is not present in the cid")
    if decision not in cid.nodes:
        raise KeyError(f"{decision} is not present in the cid")
    if not cid.sufficient_recall():
        raise ValueError("Voi only implemented graphs with sufficient recall")
    if node in nx.descendants(cid, decision) or node == decision:
        return False

    cid2 = cid.copy_without_cpds()
    cid2.add_edge(node, decision)
    req_graph = requisite_graph(cid2)
    return node in req_graph.get_parents(decision)
Esempio n. 5
0
    def test_trim_example(cid_trim_example: CID) -> None:
        assert len(cid_trim_example.edges) == 12
        assert set(cid_trim_example.get_parents("D2")) == {
            "Y1", "Y2", "D1", "Z1", "Z2"
        }

        req_graph = requisite_graph(cid_trim_example)
        assert len(req_graph.edges) == 7
        assert set(req_graph.get_parents("D2")) == {"Y2"}
Esempio n. 6
0
def admits_indir_voc(cid: CID, decision: str, node: str) -> bool:
    r"""Check if a single-decision CID admits indirect positive value of control for a 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 len(cid.agents) > 1:
        raise ValueError(
            f"This CID has {len(cid.agents)} agents. This incentive is currently only valid for CIDs with one agent."
        )

    if node not in cid.nodes:
        raise KeyError(f"{node} is not present in the cid")
    if decision not in cid.nodes:
        raise KeyError(f"{decision} is not present in the cid")
    if not cid.sufficient_recall():
        raise ValueError("VoC only implemented graphs with sufficient recall")

    agent_utilities = cid.utilities
    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, node):
        return False

    req_graph_node_descendants = set(nx.descendants(req_graph, node))
    for util in agent_utilities:
        if util != node and util not in req_graph_node_descendants:
            continue
        if not is_active_backdoor_trail(req_graph, node, util, con_nodes):
            continue
        if any(decision in path
               for path in find_all_dir_paths(req_graph, node, util)):
            return True

    return False