def basic_different_dec_cardinality() -> MACID: macid = MACID([('D1', 'D2'), ('D1', 'U1'), ('D1', 'U2'), ('D2', 'U2'), ('D2', 'U1')], { 0: { 'D': ['D1'], 'U': ['U1'] }, 1: { 'D': ['D2'], 'U': ['U2'] } }) cpd_d1 = DecisionDomain('D1', [0, 1]) cpd_d2 = DecisionDomain('D2', [0, 1, 2]) cpd_u1 = TabularCPD('U1', 4, np.array([[0, 0, 1, 0, 0, 0], [0, 1, 0, 1, 0, 0], [0, 0, 0, 0, 1, 0], [1, 0, 0, 0, 0, 1]]), evidence=['D1', 'D2'], evidence_card=[2, 3]) cpd_u2 = TabularCPD('U2', 4, np.array([[0, 0, 0, 0, 1, 0], [1, 0, 1, 1, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1]]), evidence=['D1', 'D2'], evidence_card=[2, 3]) macid.add_cpds(cpd_d1, cpd_d2, cpd_u1, cpd_u2) return macid
def get_insufficient_recall_cid() -> CID: cid = CID([('A', 'U'), ('B', 'U')], decision_nodes=['A', 'B'], utility_nodes=['U']) cid.add_cpds(DecisionDomain('A', [0, 1]), DecisionDomain('B', [0, 1]), FunctionCPD('U', lambda a, b: a * b, evidence=['A', 'B'])) return cid
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')], { 1: { 'D': ['D1'], 'U': ['U1'] }, 2: { 'D': ['D2'], 'U': ['U2'] } }) d1_domain = ['e', 'c'] d2_domain = ['e', 'c'] cpd_d1 = DecisionDomain('D1', d1_domain) cpd_d2 = DecisionDomain('D2', d2_domain) agent1_payoff = np.array([[2, 3], [5, 1]]) agent2_payoff = np.array([[2, 5], [3, 5]]) cpd_u1 = FunctionCPD('U1', lambda d1, d2: agent1_payoff[d2_domain.index(d2), d1_domain.index(d1)], evidence=['D1', 'D2']) cpd_u2 = FunctionCPD('U2', lambda d1, d2: agent2_payoff[d2_domain.index(d2), d1_domain.index(d1)], evidence=['D1', 'D2']) macid.add_cpds(cpd_d1, cpd_d2, cpd_u1, cpd_u2) return macid
def taxi_competition() -> MACID: """ A MACIM for the "Taxi Competition" 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')], { 1: { 'D': ['D1'], 'U': ['U1'] }, 2: { 'D': ['D2'], 'U': ['U2'] } }) d1_domain = ['e', 'c'] d2_domain = ['e', 'c'] cpd_d1 = DecisionDomain('D1', d1_domain) cpd_d2 = DecisionDomain('D2', d2_domain) agent1_payoff = np.array([[2, 3], [5, 1]]) agent2_payoff = np.array([[2, 5], [3, 1]]) cpd_u1 = FunctionCPD('U1', lambda d1, d2: agent1_payoff[d2_domain.index(d2), d1_domain.index(d1)], evidence=['D1', 'D2']) cpd_u2 = FunctionCPD('U2', lambda d1, d2: agent2_payoff[d2_domain.index(d2), d1_domain.index(d1)], evidence=['D1', 'D2']) macid.add_cpds(cpd_d1, cpd_d2, cpd_u1, cpd_u2) return macid
def battle_of_the_sexes() -> MACID: """ This macim is a representation of the battle of the sexes game (also known as Bach or Stravinsky). It 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')], { 'M': { 'D': ['D_F'], 'U': ['U_F'] }, 'F': { 'D': ['D_M'], 'U': ['U_M'] } }) d_f_domain = ['O', 'F'] d_m_domain = ['O', 'F'] cpd_d_f = DecisionDomain('D_F', d_f_domain) cpd_d_m = DecisionDomain('D_M', d_m_domain) agent_f_payoff = np.array([[3, 0], [0, 2]]) agent_m_payoff = np.array([[2, 0], [0, 3]]) cpd_u_f = FunctionCPD( 'U_F', lambda d_f, d_m: agent_f_payoff[d_f_domain.index(d_f), d_m_domain.index(d_m)], evidence=['D_F', 'D_M']) cpd_u_m = FunctionCPD( 'U_M', lambda d_f, d_m: agent_m_payoff[d_f_domain.index(d_f), d_m_domain.index(d_m)], evidence=['D_F', 'D_M']) macid.add_cpds(cpd_d_f, cpd_d_m, cpd_u_f, cpd_u_m) return macid
def matching_pennies() -> MACID: """ This macim is a representation of the matching pennies game. It is 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')], { 1: { 'D': ['D1'], 'U': ['U1'] }, 2: { 'D': ['D2'], 'U': ['U2'] } }) d1_domain = ['H', 'T'] d2_domain = ['H', 'T'] cpd_d1 = DecisionDomain('D1', d1_domain) cpd_d2 = DecisionDomain('D2', d2_domain) agent1_payoff = np.array([[1, -1], [-1, 1]]) agent2_payoff = np.array([[-1, 1], [1, -1]]) cpd_u1 = FunctionCPD('U1', lambda d1, d2: agent1_payoff[d1_domain.index(d1), d2_domain.index(d2)], evidence=['D1', 'D2']) cpd_u2 = FunctionCPD('U2', lambda d1, d2: agent2_payoff[d1_domain.index(d1), d2_domain.index(d2)], evidence=['D1', 'D2']) macid.add_cpds(cpd_d1, cpd_d2, cpd_u1, cpd_u2) return macid
def prisoners_dilemma() -> MACID: """ This macim is a representation of the canonical prisoner's dilemma. It 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')], { 1: { 'D': ['D1'], 'U': ['U1'] }, 2: { 'D': ['D2'], 'U': ['U2'] } }) d1_domain = ['c', 'd'] d2_domain = ['c', 'd'] cpd_d1 = DecisionDomain('D1', d1_domain) cpd_d2 = DecisionDomain('D2', d2_domain) agent1_payoff = np.array([[-1, -3], [0, -2]]) agent2_payoff = np.transpose(agent1_payoff) cpd_u1 = FunctionCPD('U1', lambda d1, d2: agent1_payoff[d1_domain.index(d1), d2_domain.index(d2)], evidence=['D1', 'D2']) cpd_u2 = FunctionCPD('U2', lambda d1, d2: agent2_payoff[d1_domain.index(d1), d2_domain.index(d2)], evidence=['D1', 'D2']) macid.add_cpds(cpd_d1, cpd_d2, cpd_u1, cpd_u2) return macid
def make_decision(self, node: str, agent: Union[str, int] = 0) -> None: """"Turn a chance or utility node into a decision node.""" if hasattr(self, "cpds") and isinstance(self.get_cpds(node), DecisionDomain): pass elif hasattr(self, "cpds") and not isinstance(self.get_cpds(node), DecisionDomain): cpd_new = DecisionDomain(node, self.get_cpds(node).state_names[node]) self.decision_nodes_agent[agent].append(node) self.whose_node[node] = agent self.add_cpds(cpd_new) else: raise Exception(f"node {node} has not yet been assigned a domain.")
def c2d() -> MACID: macid = MACID([('C1', 'U1'), ('C1', 'U2'), ('C1', 'D1'), ('D1', 'U1'), ('D1', 'U2'), ('D1', 'D2'), ('D2', 'U1'), ('D2', 'U2'), ('C1', 'D2')], { 0: { 'D': ['D1'], 'U': ['U1'] }, 1: { 'D': ['D2'], 'U': ['U2'] } }) cpd_c1 = TabularCPD('C1', 2, np.array([[.5], [.5]])) cpd_d1 = DecisionDomain('D1', [0, 1]) cpd_d2 = DecisionDomain('D2', [0, 1]) cpd_u1 = TabularCPD('U1', 4, np.array([[0, 0, 0, 0, 1, 0, 0, 0], [1, 0, 1, 0, 0, 1, 0, 0], [0, 1, 0, 1, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 1]]), evidence=['C1', 'D1', 'D2'], evidence_card=[2, 2, 2]) cpd_u2 = TabularCPD('U2', 6, np.array([[0, 0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 1, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 1, 0, 1], [0, 0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0]]), evidence=['C1', 'D1', 'D2'], evidence_card=[2, 2, 2]) macid.add_cpds(cpd_c1, cpd_d1, cpd_d2, cpd_u1, cpd_u2) return macid
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
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') ], decision_nodes=['D'], utility_nodes=['U']) cpd_a = UniformRandomCPD('A', [0, 1]) cpd_z = UniformRandomCPD('Z', [0, 1]) cpd_x = FunctionCPD('X', lambda a, z: a * z, evidence=['A', 'Z']) cpd_d = DecisionDomain('D', [0, 1]) cpd_y = FunctionCPD('Y', lambda x, z: x + z, evidence=['X', 'Z']) cpd_u = FunctionCPD('U', lambda d, y: -(d - y)**2, evidence=['D', 'Y']) cid.add_cpds(cpd_a, cpd_d, cpd_z, cpd_x, cpd_y, cpd_u) return cid
def basic2agent_3() -> MACID: macid = MACID( [ ('D1', 'D2'), # KM_NE should = {'D1': 1, 'D2': 0, 'D3': 1} ('D1', 'D3'), ('D2', 'D3'), ('D1', 'U1'), ('D1', 'U2'), ('D1', 'U3'), ('D2', 'U1'), ('D2', 'U2'), ('D2', 'U3'), ('D3', 'U1'), ('D3', 'U2'), ('D3', 'U3') ], { 0: { 'D': ['D1'], 'U': ['U1'] }, 1: { 'D': ['D2'], 'U': ['U2'] }, 2: { 'D': ['D3'], 'U': ['U3'] } }) cpd_d1 = DecisionDomain('D1', [0, 1]) cpd_d2 = DecisionDomain('D2', [0, 1]) cpd_d3 = DecisionDomain('D3', [0, 1]) cpd_u1 = TabularCPD('U1', 7, np.array([[0, 0, 0, 1, 0, 0, 0, 1], [1, 1, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]]), evidence=['D1', 'D2', 'D3'], evidence_card=[2, 2, 2]) cpd_u2 = TabularCPD('U2', 7, np.array([[0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 1, 0], [0, 1, 1, 1, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0]]), evidence=['D1', 'D2', 'D3'], evidence_card=[2, 2, 2]) cpd_u3 = TabularCPD('U3', 7, np.array([[0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 1, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 0], [0, 1, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0], [1, 0, 0, 0, 0, 1, 0, 0]]), evidence=['D1', 'D2', 'D3'], evidence_card=[2, 2, 2]) macid.add_cpds(cpd_d1, cpd_d2, cpd_d3, cpd_u1, cpd_u2, cpd_u3) return macid