def test_nothing_happens_with_do_nothing(buy_missing, n_processes, initial_balance): world = generate_world( [DoNothingAgent], buy_missing_products=buy_missing, n_processes=n_processes, name=unique_name( f"scml2020tests/single/doing_nothing/" f"{'Buy' if buy_missing else 'Fine'}_p{n_processes}_b{initial_balance}", add_time=True, rand_digits=4, ), initial_balance=initial_balance, bankruptcy_limit=initial_balance, compact=COMPACT, no_logs=NOLOGS, ) world.run() assert len(world.contracts_per_step) == 0 for a, f, p in world.afp: if is_system_agent(a.id): continue if ( a.awi.my_input_product == 0 or a.awi.my_input_product == a.awi.n_processes - 1 ): assert f.current_balance <= initial_balance, ( f"{a.name} (process {a.awi.my_input_product} of {a.awi.n_processes})'s balance " f"should go down" )
def test_agents_go_bankrupt(n_processes): buy_missing = True world = generate_world( [RandomAgent], buy_missing_products=buy_missing, n_processes=n_processes, name=unique_name( f"scml2020tests/single/bankrupt/" f"{'Buy' if buy_missing else 'Fine'}_p{n_processes}", add_time=True, rand_digits=4, ), initial_balance=0, bankruptcy_limit=0, n_steps=10, compact=COMPACT, no_logs=NOLOGS, ) world.run() # assert len(world.signed_contracts) + len(world.cancelled_contracts) == 0 for a, f, p in world.afp: if is_system_agent(a.id): continue if ( a.awi.my_input_product == 0 or a.awi.my_input_product == a.awi.n_processes - 1 ): assert f.current_balance <= 0, ( f"{a.name} (process {a.awi.my_input_product} of {a.awi.n_processes})'s balance " f"should go down" ) assert f.is_bankrupt, ( f"{a.name} (process {a.awi.my_input_product} of {a.awi.n_processes}) should " f"be bankrupt (balance = {f.current_balance}, inventory={f.current_inventory})" )
def test_ufun_penalty_scales_are_correct(penalties_scale): from scml.oneshot.ufun import OneShotUFun world = SCML2020OneShotWorld( **SCML2020OneShotWorld.generate( MyIndNeg, n_agents_per_process=3, n_processes=2, n_steps=30, penalties_scale=penalties_scale, ), compact=True, no_logs=True, ) for _ in range(30): old_trading = world.trading_prices.copy() world.step() for _, a in world.agents.items(): if is_system_agent(a.id): continue u: OneShotUFun = a.ufun if penalties_scale == "trading": assert (u.output_penalty_scale # == a.awi.trading_prices[a.awi.my_output_product] == old_trading[a.awi.my_output_product]) assert (u.input_penalty_scale # == a.awi.trading_prices[a.awi.my_input_product] == old_trading[a.awi.my_input_product]) else: assert (u.output_penalty_scale == a.awi.catalog_prices[ a.awi.my_output_product]) assert (u.input_penalty_scale == a.awi.catalog_prices[ a.awi.my_input_product])
def test_production_cost_increase(): from scml.scml2020 import SCML2021World from scml.scml2020.agents import DecentralizingAgent NPROCESSES = 5 costs = [[] for _ in range(NPROCESSES)] for _ in range(100): world = SCML2021World( **SCML2021World.generate( DecentralizingAgent, n_agents_per_process=10, n_processes=NPROCESSES, ), compact=True, no_logs=True, ) for aid in world.agent_profiles.keys(): if is_system_agent(aid): continue profile = world.agent_profiles[aid] costs[profile.input_products[0]].append( profile.costs[:, profile.input_products[0]].mean()) mean_costs = [sum(_) / len(_) for _ in costs] assert all([ b > (0.5 * (i + 2) / (i + 1)) * a for i, (a, b) in enumerate(zip(mean_costs[:-1], mean_costs[1:])) ]), f"non-ascending costs {mean_costs}"
def test_basic_awi_info_suppliers_consumers(): world = SCML2020OneShotWorld( **SCML2020OneShotWorld.generate( agent_types=RandomOneShotAgent, n_steps=10, n_processes=2, compact=True, no_logs=True, ) ) for aid in world.agents: if is_system_agent(aid): continue a = world.agents[aid] assert a.id in a.awi.all_suppliers[a.awi.my_output_product] assert a.id in a.awi.all_consumers[a.awi.my_input_product] assert a.awi.my_consumers == world.agent_consumers[aid] assert a.awi.my_suppliers == world.agent_suppliers[aid] l = a.awi.my_input_product assert all( _.endswith(str(l - 1)) or a.awi.is_system(_) for _ in a.awi.my_suppliers ) assert all( _.endswith(str(l + 1)) or a.awi.is_system(_) for _ in a.awi.my_consumers )
def test_ufun_min_max(): world = SCML2020OneShotWorld( **SCML2020OneShotWorld.generate(agent_types=[RandomOneShotAgent], n_steps=10), construct_graphs=True, ) world.step() for aid, agent in world.agents.items(): if is_system_agent(aid): continue ufun = agent.make_ufun() mn, mx = ufun.min_utility, ufun.max_utility assert mx > mn or mx == mn == 0
def test_do_nothing_goes_bankrupt(): world = generate_world([MyOneShotDoNothing], 2, 1000, 4, cash_availability=0.001) world.run() for aid, agent in world.agents.items(): if is_system_agent(aid): continue assert world.is_bankrupt[ aid], f"Agent {aid} is not bankrupt with balance {world.current_balance(aid)} and initial_balance of {world.initial_balances[aid]}" assert agent.awi.is_bankrupt()
def benchmark_data(self, agent, world, seller=True): #TODO: data for benchmarkign purposes, info_callabck, # will be rendered when display is true # how to compare different companies, Ratio Analysis # https://www.investopedia.com/ask/answers/032315/how-does-ratio-analysis-make-it-easier-compare-different-companies.asp # price-to-earnings ratio and net profit margin # Margin Ratios and Return Ratios # https://corporatefinanceinstitute.com/resources/knowledge/finance/profitability-ratios/ profitability = [] initial_balances = [] factories = [ _ for _ in world.factories if not is_system_agent(_.agent_id) ] for i, factory in enumerate(factories): initial_balances.append(factory.initial_balance) normalize = all(_ != 0 for _ in initial_balances) for _ in world.agents: if world.agents[_].action_callback == "system": continue if world.agents[_] in world.heuristic_agents: if normalize: profitability.append(( agent.state.f[2] - agent.state.f[0] ) / agent.state.f[0] - ([ f.current_balance for f in factories if f.agent_id == world.agents[_].id ][0] - [ f.initial_balance for f in factories if f.agent_id == world.agents[_].id ][0]) / [ f.initial_balance for f in factories if f.agent_id == world.agents[_].id ][0]) else: profitability.append(( agent.state.f[2] - agent.state.f[0] ) - ([ f.current_balance for f in factories if f.agent_id == world.agents[_].id ][0] - [ f.initial_balance for f in factories if f.agent_id == world.agents[_].id ][0])) return {"profitability": profitability}
def test_ufun_min_max_in_world(): for _ in range(20): world = SCML2020OneShotWorld( **SCML2020OneShotWorld.generate(agent_types=[RandomOneShotAgent], n_steps=10), construct_graphs=False, compact=True, no_logs=True, ) world.step() for aid, agent in world.agents.items(): if is_system_agent(aid): continue ufun = agent.make_ufun(add_exogenous=True) ufun.find_limit(True) ufun.find_limit(False) mn, mx = ufun.min_utility, ufun.max_utility assert mx >= mn
def test_quantity_distribution(n_processes): from pprint import pformat for _ in range(20): world = generate_world( [MyOneShotAgent], n_processes=n_processes, compact=True, no_logs=True, ) for contracts in world.exogenous_contracts.values(): for c in contracts: for p in c.partners: if is_system_agent(p): continue lines = world.agent_profiles[p].n_lines assert ( lines >= c.agreement["quantity"] >= 0 ), f"Contract: {str(c)} has negative or more quantity than n. lines {lines}\n{pformat(world.info)}"
def generate_world( agent_types, n_processes=3, n_steps=10, n_agents_per_process=2, n_lines=10, initial_balance=None, buy_missing_products=True, **kwargs, ): kwargs["no_logs"] = True kwargs["compact"] = True world = SCML2021World(**SCML2021World.generate( agent_types, n_processes=n_processes, n_steps=n_steps, n_lines=n_lines, n_agents_per_process=n_agents_per_process, initial_balance=initial_balance, buy_missing_products=buy_missing_products, **kwargs, )) for s1, s2 in zip(world.suppliers[:-1], world.suppliers[1:]): assert len(set(s1).intersection(set(s2))) == 0 for s1, s2 in zip(world.consumers[:-1], world.consumers[1:]): assert len(set(s1).intersection(set(s2))) == 0 for p in range(n_processes): assert len(world.suppliers[p + 1]) == n_agents_per_process assert len(world.consumers[p]) == n_agents_per_process for a in world.agents.keys(): if is_system_agent(a): continue assert len(world.agent_inputs[a]) == 1 assert len(world.agent_outputs[a]) == 1 assert len(world.agent_processes[a]) == 1 assert len( world.agent_suppliers[a]) == (n_agents_per_process if world.agent_inputs[a][0] != 0 else 1) assert len(world.agent_consumers[a]) == ( n_agents_per_process if world.agent_outputs[a][0] != n_processes else 1) return world
def __init__(self, configuration=None, *args, **kwargs): # maddpg drived agents, heuristic agents, script drived agents, interative agents # self.agents = [] # SELLER, BUYER self.system_entities = [] # communication channel dimensionality self.dim_c = 2 # negotiation management dimensionality self.dim_m = DIM_M # seller self.dim_b = DIM_B # buyer # simulation timestep self.dt = 0.1 # world done self.__done = False # set up the scml2020world if configuration is None: configuration = SCML2020World.generate(*args, **kwargs) self.configuration = copy.deepcopy(configuration) super().__init__(**self.configuration) # set action_callback for agent which hasnot it for agent in self.agents.values(): if not hasattr(agent, 'action_callback'): if is_system_agent(agent.id): agent.action_callback = 'system' self.system_entities.append(agent) else: agent.action_callback = 'heuristic' if not hasattr(agent, 'interactive'): agent.interactive = False if not hasattr(agent, 'state'): agent.state = AgentState()
def test_trading_prices_updated(n_agents, n_processes, n_steps): from scml.scml2020 import SCML2021World from negmas.helpers import force_single_thread eps = 1e-3 world = SCML2021World( **SCML2021World.generate( [MyRandomAgent] * n_processes, n_agents_per_process=n_agents, n_processes=n_processes, n_steps=n_steps, ), compact=True, no_logs=True, ) catalog_prices = world.catalog_prices diffs = np.zeros_like(catalog_prices) # we start at catlaog prices for aid, agent in world.agents.items(): assert np.abs(agent.awi.trading_prices - catalog_prices).max() < eps force_single_thread(True) for _ in range(n_steps): world.step() trading_prices = None for aid, agent in world.agents.items(): if is_system_agent(aid): continue trading_prices = agent.awi.trading_prices.copy() break diffs = np.maximum(diffs, np.abs(trading_prices - catalog_prices)) assert diffs.max() > eps force_single_thread(False)
def check_trading_explosion(world, checked_types=(PricePumpingAgent, )): stats = world.stats_df contracts = pd.DataFrame(world.saved_contracts) negotiations = pd.DataFrame(world.saved_negotiations) for aid, agent in world.agents.items(): if is_system_agent(aid): continue if not agent.awi.is_last_level or not any( issubclass(agent.__class__, _) for _ in checked_types): continue # all sellers should go bankrupt assert world.is_bankrupt[aid] assert agent.awi.is_bankrupt() # bankrupt agents remain bankrupt bankrupt = stats[f"bankrupt_{aid}"].values.astype(bool) bankrupt_first_index = np.where(bankrupt == True)[0][0] assert not np.any(bankrupt[:bankrupt_first_index]) assert np.all(bankrupt[bankrupt_first_index:]) # cannot become bankrupt without contracts assert (len(contracts.loc[(contracts.signed_at <= bankrupt_first_index) & (contracts.buyer_name == aid), :, ]) > 0) # no contracts after becoming bankrupt assert (len(contracts.loc[(contracts.signed_at > bankrupt_first_index) & (contracts.buyer_name == aid), :, ]) == 0) # no negotiations after becoming bankrupt assert (len(negotiations.loc[ (negotiations.requested_at > bankrupt_first_index) & (negotiations.partners.apply(lambda x: aid in x)), :, ]) == 0) # some negotiations before becoming bankrupt assert (len(negotiations.loc[ (negotiations.requested_at <= bankrupt_first_index) & (negotiations.partners.apply(lambda x: aid in x)), :, ]) > 0)
n_lines=n_lines, n_agents_per_process=n_agents_per_process, initial_balance=initial_balance, buy_missing_products=buy_missing_products, **kwargs, ) ) for s1, s2 in zip(world.suppliers[:-1], world.suppliers[1:]): assert len(set(s1).intersection(set(s2))) == 0 for s1, s2 in zip(world.consumers[:-1], world.consumers[1:]): assert len(set(s1).intersection(set(s2))) == 0 for p in range(n_processes): assert len(world.suppliers[p + 1]) == n_agents_per_process assert len(world.consumers[p]) == n_agents_per_process for a in world.agents.keys(): if is_system_agent(a): continue assert len(world.agent_inputs[a]) == 1 assert len(world.agent_outputs[a]) == 1 assert len(world.agent_processes[a]) == 1 assert len(world.agent_suppliers[a]) == ( n_agents_per_process if world.agent_inputs[a][0] != 0 else 1 ) assert len(world.agent_consumers[a]) == ( n_agents_per_process if world.agent_outputs[a][0] != n_processes else 1 ) return world @mark.parametrize("agent_type", types) @given(buy_missing=st.booleans(), n_processes=st.integers(2, 4))