예제 #1
0
def get_3node_cbn() -> CausalBayesianNetwork:
    cbn = CausalBayesianNetwork([("S", "D"), ("S", "U"), ("D", "U")])
    cpd_s = UniformRandomCPD("S", [-1, 1])
    cpd_u = FunctionCPD("U", lambda s, d: s * d)  # type: ignore
    cpd_d = FunctionCPD("D", lambda s: s + 1)  # type: ignore
    cbn.add_cpds(cpd_d, cpd_s, cpd_u)
    return cbn
예제 #2
0
def get_sequential_cid() -> CID:
    """
    This CID is a subtle case of sufficient recall, as the decision rule for D1 influences
    the expected utility of D2, but D2 can still be chosen without knowing D1, since
    D1 does not influence any utility nodes descending from D2.
    """
    cid = CID(
        [
            ("S1", "D1"),
            ("D1", "U1"),
            ("S1", "U1"),
            ("D1", "S2"),
            ("S2", "D2"),
            ("D2", "U2"),
            ("S2", "U2"),
        ],
        decisions=["D1", "D2"],
        utilities=["U1", "U2"],
    )

    cid.add_cpds(
        UniformRandomCPD("S1", [0, 1]),
        DecisionDomain("D1", [0, 1]),
        FunctionCPD("U1", lambda s1, d1: int(s1 == d1)),  # type: ignore
        FunctionCPD("S2", lambda d1: d1),  # type: ignore
        DecisionDomain("D2", [0, 1]),
        FunctionCPD("U2", lambda s2, d2: int(s2 == d2)),  # type: ignore
    )
    return cid
예제 #3
0
def get_introduced_bias() -> CID:

    cid = CID(
        [
            ("A", "X"),  # defining the graph's nodes and edges
            ("Z", "X"),
            ("Z", "Y"),
            ("X", "D"),
            ("X", "Y"),
            ("D", "U"),
            ("Y", "U"),
        ],
        decisions=["D"],
        utilities=["U"],
    )

    cpd_a = UniformRandomCPD("A", [0, 1])
    cpd_z = UniformRandomCPD("Z", [0, 1])
    cpd_x = FunctionCPD("X", lambda a, z: a * z)  # type: ignore
    cpd_d = DecisionDomain("D", [0, 1])
    cpd_y = FunctionCPD("Y", lambda x, z: x + z)  # type: ignore
    cpd_u = FunctionCPD("U", lambda d, y: -((d - y) ** 2))  # type: ignore

    cid.add_cpds(cpd_a, cpd_d, cpd_z, cpd_x, cpd_y, cpd_u)
    return cid
예제 #4
0
def basic_different_dec_cardinality() -> MACID:
    """A basic MACIM where the cardinality of each agent's decision node
    is different. It has one subgame perfect NE.
    """
    macid = MACID(
        [("D1", "D2"), ("D1", "U1"), ("D1", "U2"), ("D2", "U2"), ("D2", "U1")],
        agent_decisions={
            0: ["D1"],
            1: ["D2"]
        },
        agent_utilities={
            0: ["U1"],
            1: ["U2"]
        },
    )

    cpd_d1 = DecisionDomain("D1", [0, 1])
    cpd_d2 = DecisionDomain("D2", [0, 1, 2])

    agent1_payoff = np.array([[3, 1, 0], [1, 2, 3]])
    agent2_payoff = np.array([[1, 2, 1], [1, 0, 3]])

    cpd_u1 = FunctionCPD("U1",
                         lambda d1, d2: agent1_payoff[d1, d2])  # type: ignore
    cpd_u2 = FunctionCPD("U2",
                         lambda d1, d2: agent2_payoff[d1, d2])  # type: ignore

    macid.add_cpds(cpd_d1, cpd_d2, cpd_u1, cpd_u2)

    return macid
예제 #5
0
def modified_taxi_competition() -> MACID:
    """Modifying the payoffs in the taxi competition example
    so that there is a tie break (if taxi 1 chooses to stop
    in front of the expensive hotel, taxi 2 is indifferent
    between their choices.)

    - There are now two SPNE

                              D1
        +----------+----------+----------+
        |  taxi 1  | expensive|  cheap   |
        +----------+----------+----------+
        |expensive |     2    |   3      |
    D2  +----------+----------+----------+
        | cheap    |     5    |   1      |
        +----------+----------+----------+

                              D1
        +----------+----------+----------+
        |  taxi 2  | expensive|  cheap   |
        +----------+----------+----------+
        |expensive |     2    |   5      |
    D2  +----------+----------+----------+
        | cheap    |     3    |   5      |
        +----------+----------+----------+

    """
    macid = MACID(
        [("D1", "D2"), ("D1", "U1"), ("D1", "U2"), ("D2", "U2"), ("D2", "U1")],
        agent_decisions={
            1: ["D1"],
            2: ["D2"]
        },
        agent_utilities={
            1: ["U1"],
            2: ["U2"]
        },
    )

    d1_domain = ["e", "c"]
    d2_domain = ["e", "c"]
    agent1_payoff = np.array([[2, 3], [5, 1]])
    agent2_payoff = np.array([[2, 5], [3, 5]])

    macid.add_cpds(
        DecisionDomain("D1", d1_domain),
        DecisionDomain("D2", d2_domain),
        FunctionCPD(
            "U1",
            lambda d1, d2: agent1_payoff[d2_domain.index(d2),
                                         d1_domain.index(d1)]),  # type: ignore
        FunctionCPD(
            "U2",
            lambda d1, d2: agent2_payoff[d2_domain.index(d2),
                                         d1_domain.index(d1)]),  # type: ignore
    )
    return macid
예제 #6
0
def taxi_competition() -> MACID:
    """MACIM representation of the Taxi Competition game.

    "Taxi Competition" is an example introduced in
    "Equilibrium Refinements for Multi-Agent Influence Diagrams: Theory and Practice"
    by Hammond, Fox, Everitt, Abate & Wooldridge, 2021:

                              D1
        +----------+----------+----------+
        |  taxi 1  | expensive|  cheap   |
        +----------+----------+----------+
        |expensive |     2    |   3      |
    D2  +----------+----------+----------+
        | cheap    |     5    |   1      |
        +----------+----------+----------+

                              D1
        +----------+----------+----------+
        |  taxi 2  | expensive|  cheap   |
        +----------+----------+----------+
        |expensive |     2    |   5      |
    D2  +----------+----------+----------+
        | cheap    |     3    |   1      |
        +----------+----------+----------+

    There are 3 pure startegy NE and 1 pure SPE.
    """
    macid = MACID(
        [("D1", "D2"), ("D1", "U1"), ("D1", "U2"), ("D2", "U2"), ("D2", "U1")],
        agent_decisions={
            1: ["D1"],
            2: ["D2"]
        },
        agent_utilities={
            1: ["U1"],
            2: ["U2"]
        },
    )

    d1_domain = ["e", "c"]
    d2_domain = ["e", "c"]
    agent1_payoff = np.array([[2, 3], [5, 1]])
    agent2_payoff = np.array([[2, 5], [3, 1]])

    macid.add_cpds(
        DecisionDomain("D1", d1_domain),
        DecisionDomain("D2", d2_domain),
        FunctionCPD(
            "U1",
            lambda d1, d2: agent1_payoff[d2_domain.index(d2),
                                         d1_domain.index(d1)]),  # type: ignore
        FunctionCPD(
            "U2",
            lambda d1, d2: agent2_payoff[d2_domain.index(d2),
                                         d1_domain.index(d1)]),  # type: ignore
    )
    return macid
예제 #7
0
def robot_warehouse() -> MACID:
    r"""
    Implementation of AAMAS robot warehouse example

    - Robot 1 collects packages, and can choose to
    hurry or not (D1)
    - Hurrying can be quicker (Q) but lead to
    breakages (B)
    - Robot 2 tidies up, and can choose to repair
    (R) breakages or not (D2)
    - Conducting repairs can obstruct (O) robot 1
    - Robot 1 rewarded for speed and lack of
    breakages (U1), robot 2 is rewarded for things
    being in a state of repair (U2)

    """
    macid = MACID(
        [
            ("D1", "Q"),
            ("D1", "B"),
            ("Q", "U1"),
            ("B", "U1"),
            ("B", "R"),
            ("B", "D2"),
            ("D2", "R"),
            ("D2", "O"),
            ("O", "U1"),
            ("R", "U2"),
        ],
        agent_decisions={
            1: ["D1"],
            2: ["D2"],
        },
        agent_utilities={
            1: ["U1"],
            2: ["U2"],
        },
    )

    macid.add_cpds(
        DecisionDomain("D1", domain=[0, 1]),
        DecisionDomain("D2", domain=[0, 1]),
        # Q copies the value of D1 with 90% probability
        StochasticFunctionCPD("Q", lambda d1: {d1: 0.9}, domain=[0, 1]),
        # B copies the value of D1 with 30% probability
        StochasticFunctionCPD("B", lambda d1: {d1: 0.3}, domain=[0, 1]),
        # R = not B or D2
        FunctionCPD("R", lambda b, d2: int(not b or d2)),
        # O copies the value of D2 with 60% probability
        StochasticFunctionCPD("O", lambda d2: {d2: 0.6}, domain=[0, 1]),
        # U1 = (Q and not O) - B
        FunctionCPD("U1", lambda q, b, o: int(q and not o) - int(b)),
        # U2 = R
        FunctionCPD("U2", lambda r: r),  # type: ignore
    )
    return macid
예제 #8
0
 def test_introduced_bias_reversed_sign(self) -> None:
     cbn = CausalBayesianNetwork([("A", "D"), ("A", "Y")])
     cbn.add_cpds(
         UniformRandomCPD("A", [0, 1]),
         FunctionCPD("D", lambda a: 0),
         FunctionCPD("Y", lambda a: a),
     )
     assert introduced_total_effect(cbn, "A", "D", "Y") == pytest.approx(-1)
     cbn.add_cpds(FunctionCPD("Y", lambda a: -a))
     assert introduced_total_effect(
         cbn, "A", "D", "Y", adapt_marginalized=True) == pytest.approx(-1)
예제 #9
0
def get_2dec_cid() -> CID:
    cid = CID(
        [("S1", "S2"), ("S1", "D1"), ("D1", "S2"), ("S2", "U"), ("S2", "D2"),
         ("D2", "U")],
        decisions=["D1", "D2"],
        utilities=["U"],
    )
    cpd_s1 = UniformRandomCPD("S1", [0, 1])
    cpd_d1 = DecisionDomain("D1", [0, 1])
    cpd_d2 = DecisionDomain("D2", [0, 1])
    cpd_s2 = FunctionCPD("S2", lambda s1, d1: int(s1 == d1))  # type: ignore
    cpd_u = FunctionCPD("U", lambda s2, d2: int(s2 == d2))  # type: ignore
    cid.add_cpds(cpd_s1, cpd_d1, cpd_s2, cpd_d2, cpd_u)
    return cid
예제 #10
0
def battle_of_the_sexes() -> MACID:
    """MACIM representation of the battle of the sexes game.

    The battle of the sexes game (also known as Bach or Stravinsky)
    is a simultaneous symmetric two-player game with payoffs
    corresponding to the following normal form game -
    the row player is Female and the column player is Male:

        +----------+----------+----------+
        |          |Opera     | Football |
        +----------+----------+----------+
        |  Opera   | 3, 2     |   0, 0   |
        +----------+----------+----------+
        | Football | 0, 0     | 2, 3     |
        +----------+----------+----------+

    This game has two pure NE: (Opera, Football) and (Football, Opera)
    """
    macid = MACID(
        [("D_F", "U_F"), ("D_F", "U_M"), ("D_M", "U_M"), ("D_M", "U_F")],
        agent_decisions={
            "M": ["D_F"],
            "F": ["D_M"]
        },
        agent_utilities={
            "M": ["U_F"],
            "F": ["U_M"]
        },
    )

    d_f_domain = ["O", "F"]
    d_m_domain = ["O", "F"]
    agent_f_payoff = np.array([[3, 0], [0, 2]])
    agent_m_payoff = np.array([[2, 0], [0, 3]])

    macid.add_cpds(
        DecisionDomain("D_F", d_f_domain),
        DecisionDomain("D_M", d_m_domain),
        FunctionCPD(
            "U_F",
            lambda d_f, d_m: agent_f_payoff[d_f_domain.index(
                d_f), d_m_domain.index(d_m)]  # type: ignore
        ),
        FunctionCPD(
            "U_M",
            lambda d_f, d_m: agent_m_payoff[d_f_domain.index(
                d_f), d_m_domain.index(d_m)]  # type: ignore
        ),
    )
    return macid
예제 #11
0
def get_5node_cid_with_scaled_utility() -> CID:
    cid = CID(
        [("S1", "D"), ("S1", "U1"), ("S2", "D"), ("S2", "U2"), ("D", "U1"),
         ("D", "U2")],
        decisions=["D"],
        utilities=["U1", "U2"],
    )
    cpd_s1 = UniformRandomCPD("S1", [0, 1])
    cpd_s2 = UniformRandomCPD("S2", [0, 1])
    cpd_u1 = FunctionCPD("U1", lambda s1, d: 10 * int(s1 == d))  # type: ignore
    cpd_u2 = FunctionCPD("U2", lambda s2, d: 2 * int(s2 == d))  # type: ignore
    cpd_d = DecisionDomain("D", [0, 1])
    cid.add_cpds(cpd_d, cpd_s1, cpd_s2, cpd_u1, cpd_u2)
    return cid
예제 #12
0
def matching_pennies() -> MACID:
    """MACIM representation of the matching pennies game.

    The matching pennies game is a symmetric two-player game
    with payoffs corresponding to the following normal form game -
    the row player is agent 1 and the column player is agent 2:

        +----------+----------+----------+
        |          |Heads     | Tails    |
        +----------+----------+----------+
        |  Heads   | +1, -1   | -1, +1   |
        +----------+----------+----------+
        |  Tails   | -1, +1   | +1, -1   |
        +----------+----------+----------+

    This game has no pure NE, but has a mixed NE where
    each player chooses Heads or Tails with equal probability.
    """
    macid = MACID(
        [("D1", "U1"), ("D1", "U2"), ("D2", "U2"), ("D2", "U1")],
        agent_decisions={
            1: ["D1"],
            2: ["D2"]
        },
        agent_utilities={
            1: ["U1"],
            2: ["U2"]
        },
    )

    d1_domain = ["H", "T"]
    d2_domain = ["H", "T"]
    agent1_payoff = np.array([[1, -1], [-1, 1]])
    agent2_payoff = np.array([[-1, 1], [1, -1]])

    macid.add_cpds(
        DecisionDomain("D1", d1_domain),
        DecisionDomain("D2", d2_domain),
        FunctionCPD(
            "U1",
            lambda d1, d2: agent1_payoff[d1_domain.index(d1),
                                         d2_domain.index(d2)]),  # type: ignore
        FunctionCPD(
            "U2",
            lambda d1, d2: agent2_payoff[d1_domain.index(d1),
                                         d2_domain.index(d2)]),  # type: ignore
    )
    return macid
예제 #13
0
    def pure_decision_rules(self, decision: str) -> Iterator[FunctionCPD]:
        """Return a list of the decision rules available at the given decision"""

        domain = self.get_cpds(decision).domain
        parents = self.get_parents(decision)
        parent_cardinalities = [
            self.get_cardinality(parent) for parent in parents
        ]

        # We begin by representing each possible decision rule as a tuple of outcomes, with
        # one element for each possible decision context
        number_of_decision_contexts = int(np.product(parent_cardinalities))
        functions_as_tuples = itertools.product(
            domain, repeat=number_of_decision_contexts)

        def arg2idx(pv: Dict[str, Outcome]) -> int:
            """Convert a decision context into an index for the function list"""
            idx = 0
            for i, parent in enumerate(parents):
                name_to_no: Dict[Outcome, int] = self.get_cpds(
                    parent).name_to_no[parent]
                idx += name_to_no[pv[parent.lower()]] * int(
                    np.product(parent_cardinalities[:i]))
            assert 0 <= idx <= number_of_decision_contexts
            return idx

        for func_list in functions_as_tuples:

            def produce_function(
                    early_eval_func_list: tuple = func_list) -> Callable:
                # using a default argument is a trick to get func_list to evaluate early
                return lambda **parent_values: early_eval_func_list[arg2idx(
                    parent_values)]

            yield FunctionCPD(decision, produce_function(), domain=domain)
예제 #14
0
def get_fork_cbn() -> CausalBayesianNetwork:
    cbn = CausalBayesianNetwork([("A", "C"), ("B", "C")])
    cpd_a = UniformRandomCPD("A", [1, 2])
    cpd_b = UniformRandomCPD("B", [3, 4])
    cpd_c = FunctionCPD("C", lambda a, b: a * b)  # type: ignore
    cbn.add_cpds(cpd_a, cpd_b, cpd_c)
    return cbn
예제 #15
0
def prisoners_dilemma() -> MACID:
    """MACIM representation of the canonical prisoner's dilemma.

    The prisoner's dilemma is a simultaneous symmetric two-player game
    with payoffs corresponding to the following normal form game -
    the row player is agent 1 and the column player is agent 2:

        +----------+----------+----------+
        |          |Cooperate | Defect   |
        +----------+----------+----------+
        |Cooperate | -1, -1   | -3, 0    |
        +----------+----------+----------+
        |  Defect  | 0, -3    | -2, -2   |
        +----------+----------+----------+

    This game has one pure NE: (defect, defect)
    """
    macid = MACID(
        [("D1", "U1"), ("D1", "U2"), ("D2", "U2"), ("D2", "U1")],
        agent_decisions={
            1: ["D1"],
            2: ["D2"]
        },
        agent_utilities={
            1: ["U1"],
            2: ["U2"]
        },
    )

    d1_domain = ["c", "d"]
    d2_domain = ["c", "d"]
    agent1_payoff = np.array([[-1, -3], [0, -2]])
    agent2_payoff = np.transpose(agent1_payoff)

    macid.add_cpds(
        DecisionDomain("D1", d1_domain),
        DecisionDomain("D2", d2_domain),
        FunctionCPD(
            "U1",
            lambda d1, d2: agent1_payoff[d1_domain.index(d1),
                                         d2_domain.index(d2)]),  # type: ignore
        FunctionCPD(
            "U2",
            lambda d1, d2: agent2_payoff[d1_domain.index(d1),
                                         d2_domain.index(d2)]),  # type: ignore
    )
    return macid
예제 #16
0
 def test_introduced_bias_y_nodep_x(
         imputed_cid_introduced_bias: CID) -> None:
     # Modified model where Y doesn't depend on X
     cid = imputed_cid_introduced_bias
     cid.add_cpds(FunctionCPD("Y", lambda x, z: z))  # type: ignore
     cid.impute_conditional_expectation_decision("D", "Y")
     assert introduced_total_effect(cid, "A", "D", "Y", 0,
                                    1) == pytest.approx(1 / 3)
예제 #17
0
def get_insufficient_recall_cid() -> CID:
    cid = CID([("A", "U"), ("B", "U")], decisions=["A", "B"], utilities=["U"])
    cid.add_cpds(
        DecisionDomain("A", [0, 1]),
        DecisionDomain("B", [0, 1]),
        FunctionCPD("U", lambda a, b: a * b),  # type: ignore
    )
    return cid
예제 #18
0
def two_agents_three_actions() -> MACID:
    """This macim is a representation of a
    game where two players must decide between
    threee different actions simultaneously
    - the row player is agent 1 and the
    column player is agent 2 - the normal form
    representation of the payoffs is as follows:
        +----------+----------+----------+----------+
        |          |  L       |     C    |     R    |
        +----------+----------+----------+----------+
        | T        | 4, 3     | 5, 1     | 6, 2     |
        +----------+----------+----------+----------+
        | M        | 2, 1     | 8, 4     |  3, 6    |
        +----------+----------+----------+----------+
        | B        | 3, 0     | 9, 6     |  2, 8    |
        +----------+----------+----------+----------+
    - The game has one pure NE (T,L)
    """
    macid = MACID(
        [("D1", "U1"), ("D1", "U2"), ("D2", "U2"), ("D2", "U1")],
        agent_decisions={
            1: ["D1"],
            2: ["D2"]
        },
        agent_utilities={
            1: ["U1"],
            2: ["U2"]
        },
    )

    d1_domain = ["T", "M", "B"]
    d2_domain = ["L", "C", "R"]
    cpd_d1 = DecisionDomain("D1", d1_domain)
    cpd_d2 = DecisionDomain("D2", d2_domain)

    agent1_payoff = np.array([[4, 5, 6], [2, 8, 3], [3, 9, 2]])
    agent2_payoff = np.array([[3, 1, 2], [1, 4, 6], [0, 6, 8]])

    cpd_u1 = FunctionCPD("U1", lambda d1, d2: agent1_payoff[d1_domain.index(
        d1), d2_domain.index(d2)])  # type: ignore
    cpd_u2 = FunctionCPD("U2", lambda d1, d2: agent2_payoff[d1_domain.index(
        d1), d2_domain.index(d2)])  # type: ignore

    macid.add_cpds(cpd_d1, cpd_d2, cpd_u1, cpd_u2)
    return macid
예제 #19
0
def get_3node_cid() -> CID:
    cid = CID([("S", "D"), ("S", "U"), ("D", "U")],
              decisions=["D"],
              utilities=["U"])
    cpd_s = UniformRandomCPD("S", [-1, 1])
    cpd_u = FunctionCPD("U", lambda s, d: s * d)  # type: ignore
    cpd_d = DecisionDomain("D", [-1, 1])
    cid.add_cpds(cpd_d, cpd_s, cpd_u)
    return cid
예제 #20
0
def get_quantitative_voi_cid() -> CID:
    cid = CID([("S", "X"), ("X", "D"), ("D", "U"), ("S", "U")],
              decisions=["D"],
              utilities=["U"])
    cpd_s = UniformRandomCPD("S", [-1, 1])
    # X takes the value of S with probability 0.8
    cpd_x = StochasticFunctionCPD("X", lambda s: {s: 0.8}, domain=[-1, 1])
    cpd_d = DecisionDomain("D", [-1, 0, 1])
    cpd_u = FunctionCPD("U", lambda s, d: int(s) * int(d))  # type: ignore
    cid.add_cpds(cpd_s, cpd_x, cpd_d, cpd_u)
    return cid
예제 #21
0
def two_agent_one_pne() -> MACID:
    """This macim is a simultaneous two player game
    and has a parameterisation that
    corresponds to the following normal
    form game - where the row player is agent 1, and the
    column player is agent 2
        +----------+----------+----------+
        |          | Act(0)   | Act(1)   |
        +----------+----------+----------+
        | Act(0)   | 1, 2     | 3, 0     |
        +----------+----------+----------+
        | Act(1)   | 0, 3     | 2, 2     |
        +----------+----------+----------+
    """
    macid = MACID(
        [("D1", "U1"), ("D1", "U2"), ("D2", "U2"), ("D2", "U1")],
        agent_decisions={
            1: ["D1"],
            2: ["D2"]
        },
        agent_utilities={
            1: ["U1"],
            2: ["U2"]
        },
    )

    cpd_d1 = DecisionDomain("D1", [0, 1])
    cpd_d2 = DecisionDomain("D2", [0, 1])

    agent1_payoff = np.array([[1, 3], [0, 2]])
    agent2_payoff = np.array([[2, 0], [3, 2]])

    cpd_u1 = FunctionCPD("U1",
                         lambda d1, d2: agent1_payoff[d1, d2])  # type: ignore
    cpd_u2 = FunctionCPD("U2",
                         lambda d1, d2: agent2_payoff[d1, d2])  # type: ignore

    macid.add_cpds(cpd_d1, cpd_d2, cpd_u1, cpd_u2)
    return macid
예제 #22
0
    def impute_conditional_expectation_decision(self, decision: str,
                                                y: str) -> None:
        """Imputes a policy for decision = the expectation of y conditioning on d's parents"""
        # TODO: Move to analyze, as this is not really a core feature?
        copy = self.copy()

        @lru_cache(maxsize=1000)
        def cond_exp_policy(**pv: Outcome) -> float:
            if y.lower() in pv:
                return pv[y.lower()]  # type: ignore
            else:
                return copy.expected_value([y], pv)[0]

        self.add_cpds(
            FunctionCPD(decision,
                        cond_exp_policy,
                        label="cond_exp({})".format(y)))
예제 #23
0
    def intervene(self, intervention: Dict[str, Outcome]) -> None:
        """Given a dictionary of interventions, replace the CPDs for the relevant nodes.

        Soft interventions can be achieved by using self.add_cpds() directly.

        Parameters
        ----------
        intervention: Interventions to apply. A dictionary mapping node => value.
        """
        self._fix_lowercase_variables(intervention)
        for variable in intervention:
            for p in self.get_parents(variable):  # remove ingoing edges
                self.remove_edge(p, variable)
            self.add_cpds(
                FunctionCPD(variable,
                            lambda: intervention[variable],
                            domain=self.model.domain.get(variable, None)))
예제 #24
0
    def impute_optimal_decision(self, decision: str) -> None:
        """Impute an optimal policy to the given decision node"""
        # self.add_cpds(random.choice(self.optimal_pure_decision_rules(d)))
        self.impute_random_decision(decision)
        domain = self.get_cpds(decision).domain
        utility_nodes = self.agent_utilities[self.decision_agent[decision]]
        descendant_utility_nodes = list(
            set(utility_nodes).intersection(nx.descendants(self, decision)))
        copy = self.copy(
        )  # using a copy "freezes" the policy so it doesn't adapt to future interventions

        @lru_cache(maxsize=1000)
        def opt_policy(**parent_values: Outcome) -> Outcome:
            eu = {}
            for d in domain:
                parent_values[decision] = d
                eu[d] = sum(
                    copy.expected_value(descendant_utility_nodes,
                                        parent_values))
            return max(eu, key=eu.get)  # type: ignore

        self.add_cpds(
            FunctionCPD(decision, opt_policy, domain=domain, label="opt"))
예제 #25
0
def get_minimal_cid() -> CID:
    cid = CID([("A", "B")], decisions=["A"], utilities=["B"])
    cpd_a = DecisionDomain("A", [0, 1])
    cpd_b = FunctionCPD("B", lambda a: a)  # type: ignore
    cid.add_cpds(cpd_a, cpd_b)
    return cid
예제 #26
0
def get_minimal_cbn() -> CausalBayesianNetwork:
    cbn = CausalBayesianNetwork([("A", "B")])
    cpd_a = UniformRandomCPD("A", [0, 1])
    cpd_b = FunctionCPD("B", lambda a: a)  # type: ignore
    cbn.add_cpds(cpd_a, cpd_b)
    return cbn
예제 #27
0
 def test_initialize_function_cpd(self) -> None:
     cid = get_minimal_cid()
     cpd_a = FunctionCPD("A", lambda: 2)
     cpd_a.initialize_tabular_cpd(cid)
     self.assertTrue(cpd_a.get_values(), np.array([[1]]))
     self.assertEqual(cpd_a.get_cardinality(["A"])["A"], 1)
     self.assertEqual(cpd_a.get_state_names("A", 0), 2)
     cpd_b = FunctionCPD("B", lambda a: a)  # type: ignore
     cpd_b.initialize_tabular_cpd(cid)
     self.assertTrue(cpd_a.get_values(), np.array([[1]]))
     self.assertEqual(cpd_a.get_cardinality(["A"])["A"], 1)
     self.assertEqual(cpd_a.get_state_names("A", 0), 2)