def voi(cid: CID, decision: str, variable: str) -> float: # TODO test this method new = cid.copy() new.add_edge(variable, decision) new.impute_optimal_policy() ev1: float = new.expected_utility({}) new = cid.copy() new.remove_edge(variable, decision) new.impute_optimal_policy() ev2: float = new.expected_utility({}) return ev1 - ev2
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