from __future__ import annotations
import copy
from collections import defaultdict
from functools import partial
from itertools import chain
from os import PathLike
from pathlib import Path
from random import randint, random, shuffle
from typing import Any, Callable, Iterable, Sequence
import numpy as np
from negmas import Agent
from negmas.helpers import get_class, get_full_type_name, unique_name
from negmas.helpers.numeric import truncated_mean
from negmas.serialization import deserialize, serialize
from negmas.tournaments import TournamentResults, WorldRunResults, tournament
from scml.oneshot.agent import OneShotAgent
from scml.oneshot.agents import (
GreedyOneShotAgent,
SingleAgreementAspirationAgent,
SyncRandomOneShotAgent,
)
from scml.oneshot.sysagents import _StdSystemAgent as OneShotSysAgent
from scml.oneshot.world import OneShotWorld, SCML2024OneShotWorld
from scml.scml2020.agent import _SystemAgent as StdSysAgent
from scml.scml2020.agents import (
BuyCheapSellExpensiveAgent,
DecentralizingAgent,
MarketAwareIndDecentralizingAgent,
SatisficerAgent,
)
from scml.scml2020.world import SCML2020Agent, SCML2020World, is_system_agent
from scml.std.agent import StdAgent
from scml.std.world import STD_DEFAULT_PARAMS
from scml.oneshot.context import DefaultAgentsOneShot
from scml.std.context import DefaultAgentsStd
__all__ = [
"anac_config_generator_oneshot",
"anac_config_generator_std_new",
"anac_config_generator_std_old",
"anac_config_generator_collusion",
"anac_assigner_oneshot",
"anac_assigner_std",
"anac_assigner_collusion",
"anac_oneshot_world_generator",
"anac_world_generator",
"balance_calculator",
"balance_calculator_oneshot",
"DefaultAgents",
"DefaultAgents2021",
"DefaultAgents2022",
"DefaultAgents2023",
"DefaultAgentsOneShot",
"DefaultAgentsOneShot2023",
"DefaultAgentsOneShot",
"DefaultAgentsStd",
"anac2020_tournament",
"anac2020_std",
"anac2020_collusion",
"anac2021_oneshot",
"anac2021_std",
"anac2021_collusion",
"anac2022_oneshot",
"anac2022_std",
"anac2022_collusion",
"anac2023_oneshot",
"anac2023_std",
"anac2023_collusion",
"anac2024_oneshot",
"anac2024_std",
]
ONESHOT_TIMEOUT = 60 * 15
ONESHOT_NEG_TIMEOUT = 60
ONESHOT_NEG_HIDDEN_TIMEOUT = 60
NEG_STEP_TIME_LIMIT = 20
FORCED_LOGS_FRACTION = 1.0
ROUND_ROBIN = True
[docs]
DefaultAgents: tuple[type[SCML2020Agent], ...] = (
DecentralizingAgent,
BuyCheapSellExpensiveAgent,
)
[docs]
DefaultAgents2021 = (
DecentralizingAgent,
# MarketAwareDecentralizingAgent,
MarketAwareIndDecentralizingAgent,
SatisficerAgent,
# RandomOneShotAgent,
)
[docs]
DefaultAgents2022 = (
DecentralizingAgent,
# MarketAwareDecentralizingAgent,
MarketAwareIndDecentralizingAgent,
SatisficerAgent,
# RandomOneShotAgent,
)
[docs]
DefaultAgents2023 = (
DecentralizingAgent,
# MarketAwareDecentralizingAgent,
MarketAwareIndDecentralizingAgent,
SatisficerAgent,
# RandomOneShotAgent,
)
DefaultAgentsOneShot2022 = DefaultAgentsOneShot2023 = (
GreedyOneShotAgent,
SingleAgreementAspirationAgent,
SyncRandomOneShotAgent,
)
DefaultAgentsOneShot2024 = DefaultAgentsOneShot
DefaultAgentsStd2024 = DefaultAgentsStd
def integer_cut(
total: int,
n: int,
mn: int | list[int],
mx: int | list[int] = float("inf"), # type: ignore
) -> list[int]:
"""
Generates l random integers that sum to n where each of them is at least l_m
Args:
total: total
n: number of levels
mn: minimum per level
mx: maximum per level. Can be set to infinity
Returns:
"""
if not isinstance(mn, Iterable):
mn = [mn] * n
if not isinstance(mx, Iterable):
mx = [mx] * n
sizes = np.asarray(mn)
if total < sizes.sum():
raise ValueError(
f"Cannot generate {n} numbers summing to {total} with a minimum summing to {sizes.sum()}"
)
maxs = np.asarray(mx)
if total > maxs.sum():
raise ValueError(
f"Cannot generate {n} numbers summing to {total} with a maximum summing to {maxs.sum()}"
)
# TODO That is most likely the most stupid way to do it. We just try blindly. There MUST be a better way
while sizes.sum() < total:
indx = randint(0, n - 1)
if sizes[indx] >= mx[indx]:
continue
sizes[indx] += 1
return list(sizes.tolist())
def integer_cut_dynamic(
n: int, l_min: int, l_max: int, min_levels: int = 0
) -> list[int]:
"""
Generates a list random integers that sum to n where each of them is between l_m and l_max
Args:
n: total
l_min: minimum per level
l_max: maximum per level. Can be set to infinity
min_levels: THe minimum number of levels to use
Returns:
"""
if n < min_levels * l_min:
raise ValueError(
f"Cannot cut {n} into at least {min_levels} numbers each is at least {l_min}"
)
sizes = [l_min] * min_levels
for i in range(len(sizes)):
sizes[i] += randint(0, l_max - l_min)
while sum(sizes) < n:
i = randint(l_min, l_max)
sizes.append(i)
to_remove = sum(sizes) - n
if to_remove == 0:
return sizes
sizes = sorted(sizes, reverse=True)
for i, x in enumerate(sizes):
can_remove = x - l_min
removed = min(can_remove, to_remove)
sizes[i] -= removed
to_remove -= removed
if sum(sizes) == n:
break
sizes = [_ for _ in sizes if _ > 0]
shuffle(sizes)
assert sum(sizes) == n, f"n={n}\nsizes={sizes}"
return sizes
def _realin(rng: tuple[float, float] | float) -> float:
"""
Selects a random number within a range if given or the input if it was a float
Args:
rng: Range or single value
Returns:
the real within the given range
"""
if isinstance(rng, float):
return rng
if abs(rng[1] - rng[0]) < 1e-8:
return rng[0]
return rng[0] + random() * (rng[1] - rng[0])
def _intin(rng: tuple[int, int] | int) -> int:
"""
Selects a random number within a range if given or the input if it was an int
Args:
rng: Range or single value
Returns:
the int within the given range
"""
if not isinstance(rng, Iterable):
return int(rng)
if rng[0] == rng[1]:
return rng[0]
return randint(rng[0], rng[1])
def anac_config_generator(
year: int,
n_competitors: int,
n_agents_per_competitor: int,
agent_names_reveal_type: bool = False,
non_competitors: Sequence[str | type[SCML2020Agent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
compact: bool = False,
*,
n_steps: int | tuple[int, int] = (50, 200),
n_processes: tuple[int, int] = (
2,
4,
), # minimum is strictly guarantee but maximum is only guaranteed if select_n_levels_first
min_factories_per_level: int = 2, # strictly guaranteed
max_factories_per_level: int = 6, # not strictly guaranteed except if select_n_levels_first is False
n_lines: int = 10,
select_n_levels_first=True,
oneshot_world: bool = False,
std_world: bool = False,
context=None,
**kwargs,
) -> list[dict[str, Any]]:
if non_competitors is None:
non_competitors = DefaultAgents
non_competitor_params = tuple(dict() for _ in non_competitors)
if non_competitor_params and all(not _ for _ in non_competitor_params):
non_competitor_params = None
if not non_competitor_params:
non_competitor_params = tuple(dict() for _ in non_competitors)
if isinstance(n_processes, Iterable):
n_processes = tuple(n_processes)
else:
n_processes = (n_processes, n_processes)
n_steps = _intin(n_steps)
if select_n_levels_first:
np = randint(*n_processes)
n_agents = n_agents_per_competitor * n_competitors
n_default_managers = max(0, np * min_factories_per_level)
n_defaults = integer_cut(n_default_managers, np, 0)
n_a_list = integer_cut(n_agents, np, 0)
for i, n_a in enumerate(n_a_list):
if n_a + n_defaults[i] < min_factories_per_level:
n_defaults[i] = min_factories_per_level - n_a
if n_a + n_defaults[i] > max_factories_per_level and n_defaults[i] > 1:
n_defaults[i] = max(1, min_factories_per_level - n_a)
n_f_list = [a + b for a, b in zip(n_defaults, n_a_list)]
else:
min_n_processes = randint(*n_processes)
n_agents = n_agents_per_competitor * n_competitors
n_default_managers = max(
0, min_n_processes * min_factories_per_level - n_agents
)
n_f_list = integer_cut_dynamic(
n_agents + n_default_managers,
min_factories_per_level,
max_factories_per_level,
min_n_processes,
)
np = len(n_f_list)
n_defaults = [0] * np
while n_default_managers > 0:
indx = randint(0, np - 1)
if n_f_list[indx] <= n_defaults[indx]:
continue
n_defaults[indx] += 1
n_default_managers -= 1
n_factories = sum(n_f_list)
if non_competitor_params is None:
non_competitor_params = [{}] * len(non_competitors) # type: ignore
non_competitors = [get_full_type_name(_) for _ in non_competitors]
max_def_agents = len(non_competitors) - 1
agent_types = [None] * n_factories
manager_params = [None] * n_factories
first_in_level = 0
for level in range(np):
n_d = n_defaults[level]
n_f = n_f_list[level]
assert (
n_d <= n_f
), f"Got {n_f} total factories at level {level} out of which {n_d} are default!!"
for j in range(n_f):
if j >= n_f - n_d: # default managers are last managers in the list
def_indx = randint(0, max_def_agents)
agent_types[first_in_level + j] = non_competitors[def_indx] # type: ignore
params_ = copy.deepcopy(non_competitor_params[def_indx])
params_["name"] = f"_df_{level}_{j}"
manager_params[first_in_level + j] = params_ # type: ignore
first_in_level += n_f
world_name = unique_name("", add_time=True, rand_digits=4)
agent_types = [
get_full_type_name(_) if isinstance(_, SCML2020Agent) else _
for _ in agent_types
]
no_logs = compact
agent_processes = []
for i, n in enumerate(n_f_list):
for j in range(n):
agent_processes.append(i)
if oneshot_world or std_world:
world_params = dict(
name=world_name,
agent_types=agent_types,
agent_params=manager_params,
time_limit=ONESHOT_TIMEOUT,
neg_time_limit=ONESHOT_NEG_TIMEOUT,
neg_hidden_time_limit=ONESHOT_NEG_HIDDEN_TIMEOUT,
neg_n_steps=20,
neg_step_time_limit=NEG_STEP_TIME_LIMIT,
negotiation_speed=21,
start_negotiations_immediately=False,
agent_processes=agent_processes,
n_processes=np,
n_steps=n_steps,
n_lines=n_lines,
compact=compact,
no_logs=no_logs,
random_agent_types=False,
)
else:
world_params = dict(
name=world_name,
agent_types=agent_types,
agent_params=manager_params,
time_limit=ONESHOT_TIMEOUT,
neg_time_limit=ONESHOT_NEG_TIMEOUT,
neg_hidden_time_limit=ONESHOT_NEG_HIDDEN_TIMEOUT,
neg_n_steps=20,
neg_step_time_limit=NEG_STEP_TIME_LIMIT,
negotiation_speed=21,
spot_market_global_loss=0.2,
interest_rate=0.08,
bankruptcy_limit=1.0,
initial_balance=None,
start_negotiations_immediately=False,
agent_processes=agent_processes,
n_processes=np,
n_steps=n_steps,
n_lines=n_lines,
compact=compact,
no_logs=no_logs,
random_agent_types=False,
)
world_params.update(kwargs)
# _agent_types = copy.deepcopy(world_params.pop("agent_types"))
# _agent_params = copy.deepcopy(world_params.pop("agent_params"))
if oneshot_world:
cls = get_class(f"scml.oneshot.world.SCML{int(year)}OneShotWorld")
elif std_world:
cls = get_class(f"scml.std.world.SCML{int(year)}StdWorld")
else:
cls = get_class(f"scml.scml2020.world.SCML{int(year)}World")
if context is None:
generated_world_params = cls.generate(**world_params) # type: ignore
else:
generated_world_params = context.make_config(cls, params=world_params) # type: ignore
for k in ("agent_types", "agent_params"):
if k in generated_world_params.keys():
del generated_world_params[k]
if oneshot_world or std_world:
for _p in generated_world_params["profiles"]:
_p.cost = int(_p.cost)
else:
for _p in generated_world_params["profiles"]:
_p.costs = _p.costs.tolist()
world_params["__exact_params"] = serialize(
generated_world_params, deep=True, ignore_lambda=True
)
if context is not None:
world_params["__context"] = serialize(context, deep=True, ignore_lambda=True)
config = {
"world_params": world_params,
"compact": compact,
"scoring_context": {},
"non_competitors": non_competitors,
"non_competitor_params": non_competitor_params,
"agent_types": agent_types,
"agent_params": manager_params,
}
config.update(kwargs)
return [config]
[docs]
anac_config_generator_std_old = anac_config_generator
[docs]
def anac_config_generator_oneshot(*args, **kwargs):
if len(args) >= 5:
args = tuple(list(args[:4]) + [DefaultAgentsOneShot] + list(args[5:]))
else:
kwargs["non_competitors"] = DefaultAgentsOneShot
kwargs["oneshot_world"] = True
kwargs["std_world"] = False
return anac_config_generator(*args, **kwargs)
[docs]
def anac_config_generator_std_new(*args, **kwargs):
if len(args) >= 5:
args = tuple(list(args[:4]) + [DefaultAgentsStd] + list(args[5:]))
else:
kwargs["non_competitors"] = DefaultAgentsStd
kwargs["std_world"] = True
kwargs["oneshot_world"] = False
return anac_config_generator(*args, **kwargs)
[docs]
def anac_config_generator_collusion(
year: int,
n_competitors: int,
n_agents_per_competitor: int,
agent_names_reveal_type: bool = False,
non_competitors: tuple[str | type[SCML2020Agent], ...] | None = None,
non_competitor_params: tuple[dict[str, Any], ...] | None = None,
*args,
**kwargs,
) -> list[dict[str, Any]]:
assert n_agents_per_competitor > 1
return anac_config_generator(
year=year,
n_competitors=n_competitors,
n_agents_per_competitor=n_agents_per_competitor,
agent_names_reveal_type=agent_names_reveal_type,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
**kwargs,
)
anac2020_config_generator_std = partial(anac_config_generator, year=2020)
anac2021_config_generator_std = partial(anac_config_generator, year=2021)
anac2022_config_generator_std = partial(anac_config_generator, year=2022)
anac2023_config_generator_std = partial(anac_config_generator, year=2023)
anac2020_config_generator_oneshot = partial(anac_config_generator_oneshot, year=2020)
anac2021_config_generator_oneshot = partial(anac_config_generator_oneshot, year=2021)
anac2022_config_generator_oneshot = partial(anac_config_generator_oneshot, year=2022)
anac2023_config_generator_oneshot = partial(anac_config_generator_oneshot, year=2023)
anac2024_config_generator_oneshot = partial(anac_config_generator_oneshot, year=2024)
anac2024_config_generator_std = partial(anac_config_generator_std_new, year=2024)
anac2020_config_generator_collusion = partial(
anac_config_generator_collusion, year=2020
)
anac2021_config_generator_collusion = partial(
anac_config_generator_collusion, year=2021
)
anac2022_config_generator_collusion = partial(
anac_config_generator_collusion, year=2022
)
anac2023_config_generator_collusion = partial(
anac_config_generator_collusion, year=2023
)
[docs]
def anac_assigner_std(
config: list[dict[str, Any]],
max_n_worlds: int,
n_agents_per_competitor: int = 1,
fair: bool = True,
competitors: Sequence[str | type[OneShotAgent] | type[Agent]] = (),
params: Sequence[dict[str, Any]] | None = (),
dynamic_non_competitors: Sequence[str | type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = True,
) -> list[list[dict[str, Any]]]:
tentative = []
for i, current_config in enumerate(config):
if i > 0:
n_agents_per_competitor = 1
competitors = list(
get_full_type_name(_) if not isinstance(_, str) and _ is not None else _
for _ in competitors
)
n_competitors = len(competitors)
params = (
list(params)
if params is not None
else [dict() for _ in range(n_competitors)]
)
n_permutations = n_competitors
agent_types = current_config["agent_types"]
is_default = [_ is not None for _ in agent_types]
# assign non-competitor factories to extra-non-competitors
if dynamic_non_competitors is not None:
n_extra = len(dynamic_non_competitors)
dynamic_non_competitors = list(
get_full_type_name(_) if not isinstance(_, str) and _ is not None else _
for _ in dynamic_non_competitors
)
if dynamic_non_competitor_params is None:
dynamic_non_competitor_params = [dict() for _ in range(n_extra)]
# removing the competitors from the dynamic competitors
if exclude_competitors_from_reassignment:
# TODO May be use a better way to hash the a parameters than just conversion to str
# Note that None and and empty dict() will both become ""
compset = set(zip(competitors, (str(_) if _ else "" for _ in params)))
dynset = list(
zip(
dynamic_non_competitors,
(str(_) if _ else "" for _ in dynamic_non_competitor_params),
)
)
dynamic_non_competitor_indices = [
i for i, _ in enumerate(dynset) if _ not in compset
]
dynamic_non_competitors = [
dynamic_non_competitors[i] for i in dynamic_non_competitor_indices
]
dynamic_non_competitor_params = [
dynamic_non_competitor_params[i]
for i in dynamic_non_competitor_indices
]
n_extra = len(dynamic_non_competitors)
if n_extra:
for i, isd in enumerate(is_default):
if not isd:
continue
extra_indx = randint(0, n_extra - 1)
current_config["agent_types"][i] = dynamic_non_competitors[
extra_indx
]
current_config["agent_params"][i] = dynamic_non_competitor_params[
extra_indx
]
_assignable_f = [i for i, mtype in enumerate(agent_types) if mtype is None]
shuffle(_assignable_f)
assignable_factories = (
np.asarray(_assignable_f)
.reshape((n_competitors, n_agents_per_competitor))
.tolist()
)
current_configs = []
def _copy_config(perm_, c, indx):
_ = indx
new_config = copy.deepcopy(c)
new_config["is_default"] = is_default
for (a, p_), assignable in zip(perm_, assignable_factories):
for factory in assignable:
new_config["agent_types"][factory] = a
new_config["agent_params"][factory] = copy.deepcopy(p_)
return [new_config]
if n_permutations is not None and max_n_worlds is None:
permutation = list(zip(competitors, params))
assert len(permutation) == len(assignable_factories)
shuffle(permutation)
perm = permutation
for k in range(n_permutations):
perm = copy.deepcopy(perm)
perm = perm[-1:] + perm[:-1]
current_configs.append(_copy_config(perm, current_config, k))
elif max_n_worlds is None:
raise ValueError(
"Did not give max_n_worlds and cannot find n_permutations."
)
else:
permutation = list(zip(competitors, params))
assert len(permutation) == len(assignable_factories)
if fair:
n_min = len(assignable_factories)
n_rounds = int(max_n_worlds // n_min)
if n_rounds < 1:
raise ValueError(
f"Cannot guarantee fair assignment: n. competitors {len(assignable_factories)}, at least"
f" {n_min} runs are needed for fair assignment"
)
max_n_worlds = n_rounds * n_min if n_rounds > 0 else max_n_worlds
n_rounds = max(n_rounds, 1)
k = 0
for _ in range(n_rounds):
shuffle(permutation)
for __ in range(n_min):
k += 1
perm = copy.deepcopy(permutation)
perm = perm[-1:] + perm[:-1]
current_configs.append(_copy_config(perm, current_config, k))
else:
for k in range(max_n_worlds):
perm = copy.deepcopy(permutation)
shuffle(perm)
current_configs.append(_copy_config(perm, current_config, k))
tentative.append(current_configs)
if len(config) == 1:
return tentative[0]
assert len({len(_) for _ in tentative}) == 1
n = len(tentative[0])
final_configs = []
for i in range(n):
lst = [_[i] for _ in tentative]
final_configs += lst
return final_configs
[docs]
anac_assigner_oneshot = anac_assigner_std
[docs]
def anac_assigner_collusion(
config: list[dict[str, Any]],
max_n_worlds: int,
n_agents_per_competitor: int = 1,
fair: bool = True,
competitors: Sequence[type[Agent] | str] = (),
params: Sequence[dict[str, Any]] | None = (),
dynamic_non_competitors: list[type[Agent] | str] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = True,
) -> list[list[dict[str, Any]]]:
current_config = config[0]
competitors = list(
get_full_type_name(_) if not isinstance(_, str) and _ is not None else _
for _ in competitors
)
n_competitors = len(competitors)
params = (
list(params) if params is not None else [dict() for _ in range(n_competitors)]
)
n_permutations = n_competitors
agent_types = current_config["agent_types"]
is_default = [_ is not None for _ in agent_types]
# assign non-competitor factories to extra-non-competitors
if dynamic_non_competitors is not None:
n_extra = len(dynamic_non_competitors)
dynamic_non_competitors = list(
get_full_type_name(_) if not isinstance(_, str) and _ is not None else _
for _ in dynamic_non_competitors
)
if dynamic_non_competitor_params is None:
dynamic_non_competitor_params = [dict() for _ in range(n_extra)]
# removing the competitors from the dynamic competitors
if exclude_competitors_from_reassignment:
# TODO May be use a better way to hash the a parameters than just conversion to str
# Note that None and and empty dict() will both become ""
compset = set(zip(competitors, (str(_) if _ else "" for _ in params)))
dynset = list(
zip(
dynamic_non_competitors,
(str(_) if _ else "" for _ in dynamic_non_competitor_params),
)
)
dynamic_non_competitor_indices = [
i for i, _ in enumerate(dynset) if _ not in compset
]
dynamic_non_competitors = [
dynamic_non_competitors[i] for i in dynamic_non_competitor_indices
]
dynamic_non_competitor_params = [
dynamic_non_competitor_params[i] for i in dynamic_non_competitor_indices
]
n_extra = len(dynamic_non_competitors)
if n_extra:
for i, isd in enumerate(is_default):
if not isd:
continue
extra_indx = randint(0, n_extra - 1)
current_config["agent_types"][i] = dynamic_non_competitors[extra_indx]
current_config["agent_params"][i] = dynamic_non_competitor_params[
extra_indx
]
afs = [i for i, mtype in enumerate(agent_types) if mtype is None]
shuffle(afs)
assignable_factories = (
np.asarray(afs).reshape((n_competitors, n_agents_per_competitor)).tolist()
)
current_configs = []
def _copy_config(perm_, c, indx):
_ = indx
new_config = copy.deepcopy(c)
new_config["is_default"] = is_default
for (a, p_), assignable in zip(perm_, assignable_factories):
for factory in assignable:
new_config["agent_types"][factory] = a
new_config["agent_params"][factory] = copy.deepcopy(p_)
configs: list[dict] = [
copy.deepcopy(new_config) for _ in range(n_agents_per_competitor + 1)
]
non_competitor_info = list(
(a, p if p else dict())
for a, p, d in zip(
new_config["agent_types"], new_config["agent_params"], is_default
)
if d and a is not None and a not in competitors
)
non_competitors = [get_full_type_name(a) for a, _ in non_competitor_info]
non_competitor_params = [b for _, b in non_competitor_info]
max_def_agents = len(non_competitors) - 1
assert len({tuple(_) for _ in assignable_factories}) == 1
free_factories = assignable_factories[0]
assert len(free_factories) == n_agents_per_competitor
# for each config other than the first, remove all unassigned factories keeping one
for j, config in zip(free_factories, configs[1:]):
for i in free_factories:
if i == j:
continue
# assert config["agent_types"][i] is None
assert not config["is_default"][i]
def_indx = randint(0, max_def_agents)
params_ = copy.deepcopy(non_competitor_params[def_indx])
params_["name"] = f"_df_{100}_{i}"
config["agent_types"][i] = non_competitors[def_indx]
config["agent_params"][i] = params_
config["is_default"][i] = True
# config["is_default"][i] = False
assert len([_ for _ in config["is_default"] if not _]) == 1
return configs
if n_permutations is not None and max_n_worlds is None:
permutation = list(zip(competitors, params))
assert len(permutation) == len(assignable_factories)
shuffle(permutation)
perm = permutation
for k in range(n_permutations):
perm = copy.deepcopy(perm)
perm = perm[-1:] + perm[:-1]
current_configs.append(_copy_config(perm, current_config, k))
elif max_n_worlds is None:
raise ValueError("Did not give max_n_worlds and cannot find n_permutations.")
else:
permutation = list(zip(competitors, params))
assert len(permutation) == len(assignable_factories)
if fair:
n_min = len(assignable_factories)
n_rounds = int(max_n_worlds // n_min)
if n_rounds < 1:
raise ValueError(
f"Cannot guarantee fair assignment: n. competitors {len(assignable_factories)}, at least"
f" {n_min} runs are needed for fair assignment"
)
max_n_worlds = n_rounds * n_min
k = 0
for _ in range(n_rounds):
shuffle(permutation)
for __ in range(n_min):
k += 1
perm = copy.deepcopy(permutation)
perm = perm[-1:] + perm[:-1]
current_configs.append(_copy_config(perm, current_config, k))
else:
for k in range(max_n_worlds):
perm = copy.deepcopy(permutation)
shuffle(perm)
current_configs.append(_copy_config(perm, current_config, k))
return current_configs
[docs]
def anac_world_generator(*, year: int, **kwargs):
if "n_agents_per_process" in kwargs["world_params"]:
assert sum(kwargs["world_params"]["n_agents_per_process"]) == len(
kwargs["world_params"]["agent_types"]
)
else:
assert len(kwargs["world_params"]["agent_processes"]) == len(
kwargs["world_params"]["agent_types"]
)
cnfg = kwargs["world_params"].pop("__exact_params")
kwargs["world_params"].pop("__context", None)
cnfg = deserialize(cnfg)
kwargs["world_params"]["random_agent_types"] = False
cls = get_class(f"scml.scml2020.world.SCML{int(year)}World")
cnfg2 = cls.generate(**kwargs["world_params"]) # type: ignore
assert isinstance(cnfg, dict)
assert isinstance(cnfg2, dict)
for k in (
"agent_types",
"agent_params",
"name",
"log_folder",
"log_ufuns",
"log_negotiations",
"ignore_agent_exceptions",
"ignore_contract_execution_exceptions",
"ignore_simulation_exceptions",
"ignore_negotiation_exceptions",
):
if k in cnfg2:
cnfg[k] = cnfg2[k]
for _p in cnfg["profiles"]: # type: ignore
_p.costs = np.asarray(_p.costs) # type: ignore
if "info" not in cnfg.keys(): # type: ignore
cnfg["info"] = dict() # type: ignore
cnfg["info"]["is_default"] = kwargs["is_default"] # type: ignore
world = cls(**cnfg) # type: ignore
return world
anac2020_world_generator = partial(anac_world_generator, year=2020)
anac2021_world_generator = partial(anac_world_generator, year=2021)
anac2022_world_generator = partial(anac_world_generator, year=2022)
anac2023_world_generator = partial(anac_world_generator, year=2023)
[docs]
def anac_oneshot_world_generator(*, year, **kwargs):
if "n_agents_per_process" in kwargs["world_params"]:
assert sum(kwargs["world_params"]["n_agents_per_process"]) == len(
kwargs["world_params"]["agent_types"]
)
else:
assert len(kwargs["world_params"]["agent_processes"]) == len(
kwargs["world_params"]["agent_types"]
)
# cnfg = SCML2020OneShotWorld.generate(**kwargs["world_params"])
# for k in ("n_agents_per_process","n_processes"):
# del kwargs["world_params"][k]
kwargs["world_params"]["random_agent_types"] = False
cnfg = kwargs["world_params"].pop("__exact_params")
if "random_agent_types" in cnfg:
cnfg["random_agent_types"] = False
cnfg = deserialize(cnfg)
context = kwargs["world_params"].pop("__context", None)
cls = get_class(f"scml.oneshot.world.SCML{int(year)}OneShotWorld")
if context is None:
cnfg2 = cls.generate(**kwargs["world_params"])
else:
cnfg2 = deserialize(context).generate(cls, params=kwargs["world_params"])
assert isinstance(cnfg, dict)
assert isinstance(cnfg2, dict)
for k in (
"agent_types",
"agent_params",
"name",
"log_folder",
"log_ufuns",
"log_negotiations",
"ignore_agent_exceptions",
"ignore_contract_execution_exceptions",
"ignore_simulation_exceptions",
"ignore_negotiation_exceptions",
):
if k in cnfg2:
cnfg[k] = cnfg2[k]
if "info" not in cnfg.keys():
cnfg["info"] = dict()
cnfg["info"]["is_default"] = kwargs["is_default"]
world = cls(**cnfg)
return world
def anac_std_world_generator(*, year, **kwargs):
if "n_agents_per_process" in kwargs["world_params"]:
assert sum(kwargs["world_params"]["n_agents_per_process"]) == len(
kwargs["world_params"]["agent_types"]
)
else:
assert len(kwargs["world_params"]["agent_processes"]) == len(
kwargs["world_params"]["agent_types"]
)
# cnfg = SCML2020OneShotWorld.generate(**kwargs["world_params"])
# for k in ("n_agents_per_process","n_processes"):
# del kwargs["world_params"][k]
cnfg = kwargs["world_params"].pop("__exact_params")
cnfg = deserialize(cnfg)
context = kwargs["world_params"].pop("__context", None)
kwargs["world_params"]["random_agent_types"] = False
cls = get_class(f"scml.std.world.SCML{int(year)}StdWorld")
d = STD_DEFAULT_PARAMS
d.update(kwargs["world_params"])
kwargs["world_params"] = d
if context is None:
cnfg2 = cls.generate(**kwargs["world_params"])
else:
cnfg2 = deserialize(context).generate(cls, params=kwargs["world_params"])
assert isinstance(cnfg, dict)
assert isinstance(cnfg2, dict)
for k in (
"agent_types",
"agent_params",
"name",
"log_folder",
"log_ufuns",
"log_negotiations",
"ignore_agent_exceptions",
"ignore_contract_execution_exceptions",
"ignore_simulation_exceptions",
"ignore_negotiation_exceptions",
):
if k in cnfg2:
cnfg[k] = cnfg2[k]
if "info" not in cnfg.keys():
cnfg["info"] = dict()
cnfg["info"]["is_default"] = kwargs["is_default"]
world = cls(**cnfg)
return world
anac2021_oneshot_world_generator = partial(anac_oneshot_world_generator, year=2021)
anac2022_oneshot_world_generator = partial(anac_oneshot_world_generator, year=2022)
anac2023_oneshot_world_generator = partial(anac_oneshot_world_generator, year=2023)
anac2024_oneshot_world_generator = partial(anac_oneshot_world_generator, year=2024)
anac2024_std_world_generator = partial(anac_std_world_generator, year=2024)
[docs]
def balance_calculator(
worlds: list[SCML2020World],
scoring_context: dict[str, Any],
dry_run: bool,
ignore_default=True,
inventory_catalog_price_weight=0.0,
inventory_trading_average_weight=0.5,
consolidated=False,
) -> WorldRunResults:
"""A scoring function that scores factory managers' performance by the final balance only ignoring whatever still
in their inventory.
Args:
worlds: The world which is assumed to be run up to the point at which the scores are to be calculated.
scoring_context: A dict of context parameters passed by the world generator or assigner.
dry_run: A boolean specifying whether this is a dry_run. For dry runs, only names and types are expected in
the returned `WorldRunResults`
ignore_default: Whether to ignore non-competitors (default agents)
inventory_catalog_price_weight: The weight assigned to catalog price
inventory_trading_average_weight: The weight assigned to trading price average
consolidated: If true, the score of an agent type will be based on a consolidated statement of
all the factories it controlled
Returns:
WorldRunResults giving the names, scores, and types of factory managers.
"""
if scoring_context is not None:
inventory_catalog_price_weight = scoring_context.get(
"inventory_catalog_price_weight", inventory_catalog_price_weight
)
inventory_trading_average_weight = scoring_context.get(
"inventory_trading_average_weight", inventory_trading_average_weight
)
consolidated = scoring_context.get("consolidated", consolidated)
assert len(worlds) == 1
world = worlds[0]
if world.inventory_valuation_trading is not None:
inventory_trading_average_weight = world.inventory_valuation_trading
if world.inventory_valuation_catalog is not None:
inventory_catalog_price_weight = world.inventory_valuation_catalog
result = WorldRunResults(
world_names=[world.name], log_file_names=[world.log_file_name]
)
initial_balances = []
is_default = world.info["is_default"]
factories = [_ for _ in world.factories if not is_system_agent(_.agent_id)]
agents = [
world.agents[f.agent_id] for f in factories if not is_system_agent(f.agent_id)
]
agent_types = [
_
for _ in world.agent_unique_types
if not _.startswith("system_agent")
and not _.split(".")[-1].startswith("_System")
]
if len(set(agent_types)) == len(set(world.agent_types)):
agent_types = [
_
for _ in world.agent_types
if not _.startswith("system_agent")
and not _.split(".")[-1].startswith("_System")
and not get_class(_) == StdSysAgent
]
for i, factory in enumerate(factories):
if is_default[i] and ignore_default:
continue
initial_balances.append(factory.initial_balance)
normalize = all(_ != 0 for _ in initial_balances)
consolidated_scores = defaultdict(float)
individual_scores = list()
initial_sums = defaultdict(float)
assert len(is_default) == len(agents)
assert len(agents) == len(factories)
assert len(factories) == len(agent_types)
for default, factory, manager, agent_type in zip(
is_default, factories, agents, agent_types
):
if default and ignore_default:
continue
result.names.append(manager.name)
result.ids.append(manager.id)
result.types.append(agent_type)
if dry_run:
result.scores.append(None)
continue
final_balance = factory.current_balance
if inventory_catalog_price_weight != 0.0:
final_balance += np.sum(
inventory_catalog_price_weight
* factory.current_inventory
* world.catalog_prices
)
if inventory_trading_average_weight != 0.0:
final_balance += np.sum(
inventory_trading_average_weight
* factory.current_inventory
* world.trading_prices
)
profit = final_balance - factory.initial_balance
individual_scores.append(
profit / factory.initial_balance if normalize else profit
)
consolidated_scores[agent_type] += profit
initial_sums[agent_type] += factory.initial_balance
if normalize:
for k in consolidated_scores.keys():
consolidated_scores[k] /= initial_sums[k]
extra = []
for k, v in consolidated_scores.items():
extra.append(dict(type=k, score=v))
result.extra_scores["combined_scores"] = extra
result.extra_scores["consolidated_scores"] = extra
if consolidated:
for indx, type_ in enumerate(result.types):
result.scores.append(consolidated_scores[type_])
else:
result.scores = individual_scores
return result
balance_calculator2020 = partial(balance_calculator, consolidated=False)
balance_calculator2021 = partial(balance_calculator, consolidated=False)
balance_calculator2022 = partial(balance_calculator, consolidated=True)
balance_calculator2023 = partial(balance_calculator, consolidated=True)
def balance_calculator_collusion(
worlds: list[SCML2020World],
scoring_context: dict[str, Any],
dry_run: bool,
year: int,
ignore_default=True,
inventory_catalog_price_weight=0.0,
inventory_trading_average_weight=0.5,
raw_collusion_score_multiplier=0.2,
**kwargs,
) -> WorldRunResults:
"""A scoring function that scores factory managers' performance by the
final balance only ignoring whatever still in their inventory after
consolidating all factories in the simulation that belong to the same
agent type.
Args:
worlds: The world which is assumed to be run up to the point at which the scores are to be calculated.
scoring_context: A dict of context parameters passed by the world generator or assigner.
dry_run: A boolean specifying whether this is a dry_run. For dry runs, only names and types are expected in
the returned `WorldRunResults`
ignore_default: Whether to ignore non-competitors (default agents)
inventory_catalog_price_weight: The weight assigned to catalog price
inventory_trading_average_weight: The weight assigned to trading price average
raw_collusion_score_multiplier: multiplies the raw collusion score with one minus this value multiplying the difference between collusion and standard scores.
Returns:
WorldRunResults giving the names, scores, and types of factory managers.
Remarks:
- If multiple agents belonged to the same agent_type, the score of
all of these agents will be set to the same value which is the
consolidated profit of the group. This means that agent types that
have more instantiations will tend to have higher scores at the end.
When using this balance calculator, it is recommended to have the
same number of instantiations of all agent types in each simulation
to make sure that scores of different agent types are comparable in
each and every simulation.
"""
if year < 2021:
return balance_calculator2020(
[worlds[0]],
scoring_context,
dry_run,
ignore_default,
inventory_catalog_price_weight,
inventory_trading_average_weight,
)
assert len(worlds) > 1
results_with_collusion = balance_calculator2021(
[worlds[0]],
scoring_context,
dry_run,
ignore_default,
inventory_catalog_price_weight,
inventory_trading_average_weight,
)
results_without_collusion = [
balance_calculator2021(
[w],
scoring_context,
dry_run,
ignore_default,
inventory_catalog_price_weight,
inventory_trading_average_weight,
)
for w in worlds[1:]
]
if dry_run:
return results_with_collusion
allscores = list(chain(*(_.scores for _ in results_without_collusion)))
mean_score = sum(allscores) / len(allscores)
results_with_collusion.scores = [
_ - (1.0 - raw_collusion_score_multiplier) * mean_score
for _ in results_with_collusion.scores
]
# todo: check consolidated scores
return results_with_collusion
balance_calculator2020collusion = partial(balance_calculator_collusion, year=2020)
balance_calculator2021collusion = partial(balance_calculator_collusion, year=2021)
balance_calculator2022collusion = partial(balance_calculator_collusion, year=2022)
balance_calculator2023collusion = partial(balance_calculator_collusion, year=2023)
[docs]
def balance_calculator_oneshot(
worlds: list[OneShotWorld],
scoring_context: dict[str, Any],
dry_run: bool,
ignore_default=True,
consolidated=True,
**kwargs,
) -> WorldRunResults:
"""A scoring function that scores factory managers' performance by the final balance only ignoring whatever still
in their inventory.
Args:
worlds: The world which is assumed to be run up to the point at which the scores are to be calculated.
scoring_context: A dict of context parameters passed by the world generator or assigner.
dry_run: A boolean specifying whether this is a dry_run. For dry runs, only names and types are expected in
the returned `WorldRunResults`
ignore_default: Whether to ignore non-competitors (default agents)
consolidated: If true, the score of an agent type will be based on a consolidated statement of
all the factories it controlled
Returns:
WorldRunResults giving the names, scores, and types of factory managers.
"""
if scoring_context is not None:
consolidated = scoring_context.get("consolidated", consolidated)
assert len(worlds) == 1
world = worlds[0]
result = WorldRunResults(
world_names=[world.name], log_file_names=[world.log_file_name]
)
is_default = world.info["is_default"]
agents = list(
_ for _ in world.agents.values() if not issubclass(type(_), OneShotSysAgent)
)
agent_types = [
_
for _ in world.agent_unique_types
if not _.startswith("system_agent") and not _.startswith("System")
]
if len(set(agent_types)) == len(set(world.agent_types)):
agent_types = [
_
for _ in world.agent_types
if not _.startswith("system_agent") and not _.startswith("System")
]
consolidated_scores = defaultdict(float)
individual_scores = list()
scores = world.scores()
assert len(agents) == len(is_default) and len(agents) == len(agent_types)
for default, manager, agent_type in zip(is_default, agents, agent_types):
if default and ignore_default:
continue
result.names.append(manager.name)
result.ids.append(manager.id)
result.types.append(agent_type)
if dry_run:
result.scores.append(None)
continue
profit = scores[manager.id]
individual_scores.append(profit)
consolidated_scores[agent_type] += profit
extra = []
for k, v in consolidated_scores.items():
extra.append(dict(type=k, score=v))
result.extra_scores["combined_scores"] = extra
result.extra_scores["consolidated_scores"] = extra
if consolidated:
for indx, type_ in enumerate(result.types):
result.scores.append(consolidated_scores[type_])
else:
result.scores = individual_scores
return result
balance_calculator_std = balance_calculator_oneshot
[docs]
def anac2020_tournament(
competitors: Sequence[str | type[SCML2020Agent]],
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 2,
n_agents_per_competitor: int = 3,
min_factories_per_level: int = 2,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2020 SCML tournament (collusion track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (by rotating agents over factories).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
n_agents_per_competitor: Number of agents per competitor
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, effort will be made to reduce memory footprint including disableing most logs
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
return anac2020_collusion(
competitors=competitors,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
max_worlds_per_config=max_worlds_per_config,
n_runs_per_world=n_runs_per_world,
n_agents_per_competitor=n_agents_per_competitor,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
min_factories_per_level=min_factories_per_level,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
compact=compact,
configs_only=configs_only,
non_competitors=None,
non_competitor_params=None,
**kwargs,
)
[docs]
def anac2020_std(
competitors: Sequence[str | type[SCML2020Agent]],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
min_factories_per_level: int = 2,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
non_competitors: Sequence[str | type[SCML2020Agent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = True,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=None,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2020 SCML tournament (standard track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
competitor_params: A list of competitor parameters (used to initialize the competitors).
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (all permutations).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for
distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
non_competitors: A list of agent types that will not be competing in the sabotage competition but will exist
in the world
non_competitor_params: parameters of non competitor agents
dynamic_non_competitors: A list of non-competing agents that are assigned to the simulation dynamically during
the creation of the final assignment instead when the configuration is created
dynamic_non_competitor_params: paramters of dynamic non competitor agents
exclude_competitors_from_reassignment: If true, competitors are excluded from the dynamic non-competitors
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, compact logs will be created and effort will be made to reduce the memory footprint
n_competitors_per_world: Number of competitors in every simulation. If not given it will be a random number
between 2 and min(2, n), where n is the number of competitors
forced_logs_fraction: Fraction of simulations for which logs are always saved (including negotiations)
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
if n_competitors_per_world is None:
n_competitors_per_world = kwargs.get(
"n_competitors_per_world", randint(2, min(4, len(competitors)))
)
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgents
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
n_agents_per_competitor=1,
world_generator=anac2020_world_generator,
config_generator=anac2020_config_generator_std,
config_assigner=anac_assigner_std,
score_calculator=balance_calculator2020,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric="median",
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
**kwargs,
)
[docs]
def anac2020_collusion(
competitors: Sequence[str | type],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
n_agents_per_competitor: int = 3,
min_factories_per_level: int = 2,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
non_competitors: Sequence[str | type[SCML2020Agent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = True,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=None,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2020 SCML tournament (collusion track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
competitor_params: A list of competitor parameters (used to initialize the competitors).
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (all permutations).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
n_agents_per_competitor: Number of agents per competitor
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for
distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
non_competitors: A list of agent types that will not be competing in the sabotage competition but will exist
in the world
non_competitor_params: parameters of non competitor agents
dynamic_non_competitors: A list of non-competing agents that are assigned to the simulation dynamically during
the creation of the final assignment instead when the configuration is created
dynamic_non_competitor_params: paramters of dynamic non competitor agents
exclude_competitors_from_reassignment: If true, competitors are excluded from the dynamic non-competitors
n_competitors_per_world: Number of competitors in every simulation. If not given it will be a random number
between 2 and min(2, n), where n is the number of competitors
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, compact logs will be created and effort will be made to reduce the memory footprint
forced_logs_fraction: Fraction of simulations for which logs are always saved (including negotiations)
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
if n_competitors_per_world is None:
n_competitors_per_world = kwargs.get(
"n_competitors_per_world", randint(2, min(4, len(competitors)))
)
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgents
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
n_agents_per_competitor=n_agents_per_competitor,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
world_generator=anac2020_world_generator,
config_generator=anac2020_config_generator_collusion,
config_assigner=anac_assigner_collusion,
score_calculator=balance_calculator2020,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric="median",
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
**kwargs,
)
def anac2021_tournament(
competitors: Sequence[str | type[SCML2020Agent]],
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 2,
n_agents_per_competitor: int = 3,
min_factories_per_level: int = 2,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2021 SCML tournament (collusion track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (by rotating agents over factories).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
n_agents_per_competitor: Number of agents per competitor
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, effort will be made to reduce memory footprint including disableing most logs
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
return anac2021_collusion(
competitors=competitors,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
max_worlds_per_config=max_worlds_per_config,
n_runs_per_world=n_runs_per_world,
n_agents_per_competitor=n_agents_per_competitor,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
min_factories_per_level=min_factories_per_level,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
compact=compact,
configs_only=configs_only,
non_competitors=None,
non_competitor_params=None,
**kwargs,
)
[docs]
def anac2021_std(
competitors: Sequence[str | type[SCML2020Agent]],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
min_factories_per_level: int = 2,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
non_competitors: Sequence[str | type[SCML2020Agent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = True,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=None,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2021 SCML tournament (standard track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
competitor_params: A list of competitor parameters (used to initialize the competitors).
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (all permutations).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for
distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
non_competitors: A list of agent types that will not be competing in the sabotage competition but will exist
in the world
non_competitor_params: parameters of non competitor agents
dynamic_non_competitors: A list of non-competing agents that are assigned to the simulation dynamically during
the creation of the final assignment instead when the configuration is created
dynamic_non_competitor_params: paramters of dynamic non competitor agents
exclude_competitors_from_reassignment: If true, competitors are excluded from the dynamic non-competitors
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, compact logs will be created and effort will be made to reduce the memory footprint
n_competitors_per_world: Number of competitors in every simulation. If not given it will be a random number
between 2 and min(2, n), where n is the number of competitors
forced_logs_fraction: Fraction of simulations for which logs are always saved (including negotiations)
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
if n_competitors_per_world is None:
n_competitors_per_world = kwargs.get(
"n_competitors_per_world", randint(2, min(4, len(competitors)))
)
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgents2021
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
n_agents_per_competitor=1,
world_generator=anac2021_world_generator,
config_generator=anac2021_config_generator_std,
config_assigner=anac_assigner_std,
score_calculator=balance_calculator2021,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric=truncated_mean,
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
publish_exogenous_summary=True,
publish_trading_prices=True,
**kwargs,
)
[docs]
def anac2021_collusion(
competitors: Sequence[str | type],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
n_agents_per_competitor: int = 3,
min_factories_per_level: int = 2,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
non_competitors: Sequence[str | type[SCML2020Agent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = False,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=1,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2021 SCML tournament (collusion track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
competitor_params: A list of competitor parameters (used to initialize the competitors).
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (all permutations).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
n_agents_per_competitor: Number of agents per competitor
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for
distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
non_competitors: A list of agent types that will not be competing in the sabotage competition but will exist
in the world
non_competitor_params: parameters of non competitor agents
dynamic_non_competitors: A list of non-competing agents that are assigned to the simulation dynamically during
the creation of the final assignment instead when the configuration is created
dynamic_non_competitor_params: paramters of dynamic non competitor agents
exclude_competitors_from_reassignment: If true, competitors are excluded from the dynamic non-competitors
n_competitors_per_world: Number of competitors in every simulation. If not given it will be a random number
between 2 and min(2, n), where n is the number of competitors. This value will
always be set to 1 in SCML2021
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, compact logs will be created and effort will be made to reduce the memory footprint
forced_logs_fraction: Fraction of simulations for which logs are always saved (including negotiations)
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
n_competitors_per_world = 1
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgents2021
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
n_agents_per_competitor=n_agents_per_competitor,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
world_generator=anac2021_world_generator,
config_generator=anac2021_config_generator_collusion,
config_assigner=anac_assigner_collusion,
score_calculator=balance_calculator2021,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric=truncated_mean,
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
publish_exogenous_summary=True,
publish_trading_prices=True,
**kwargs,
)
[docs]
def anac2021_oneshot(
competitors: Sequence[str | type[SCML2020Agent]],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
min_factories_per_level: int = 4,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
non_competitors: Sequence[str | type[SCML2020Agent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = False,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=None,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2021 SCML tournament (oneshot track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
competitor_params: A list of competitor parameters (used to initialize the competitors).
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (all permutations).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for
distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
non_competitors: A list of agent types that will not be competing in the sabotage competition but will exist
in the world
non_competitor_params: parameters of non competitor agents
dynamic_non_competitors: A list of non-competing agents that are assigned to the simulation dynamically during
the creation of the final assignment instead when the configuration is created
dynamic_non_competitor_params: paramters of dynamic non competitor agents
exclude_competitors_from_reassignment: If true, competitors are excluded from the dynamic non-competitors
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, compact logs will be created and effort will be made to reduce the memory footprint
n_competitors_per_world: Number of competitors in every simulation. If not given it will be a random number
between 2 and min(2, n), where n is the number of competitors
forced_logs_fraction: Fraction of simulations for which logs are always saved (including negotiations)
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
# if competitor_params is None:
# competitor_params = [dict() for _ in range(len(competitors))]
# for t, p in zip(competitors, competitor_params):
# p["controller_type"] = get_full_type_name(t)
# competitors = ["scml.oneshot.world.DefaultOneShotAdapter"] * len(competitors)
if n_competitors_per_world is None:
n_competitors_per_world = kwargs.get(
"n_competitors_per_world", randint(2, min(4, len(competitors)))
)
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgentsOneShot
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
kwargs["oneshot_world"] = True
kwargs["n_processes"] = 2
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
n_agents_per_competitor=1,
world_generator=anac2021_oneshot_world_generator,
config_generator=anac2021_config_generator_oneshot,
config_assigner=anac_assigner_std,
score_calculator=balance_calculator_oneshot,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric=truncated_mean,
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
publish_exogenous_summary=True,
publish_trading_prices=True,
**kwargs,
)
def anac2022_tournament(
competitors: Sequence[str | type[SCML2020Agent]],
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 2,
n_agents_per_competitor: int = 3,
min_factories_per_level: int = 2,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2022 SCML tournament (collusion track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (by rotating agents over factories).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
n_agents_per_competitor: Number of agents per competitor
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, effort will be made to reduce memory footprint including disableing most logs
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
return anac2022_std(
competitors=competitors,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
max_worlds_per_config=max_worlds_per_config,
n_runs_per_world=n_runs_per_world,
n_agents_per_competitor=n_agents_per_competitor,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
min_factories_per_level=min_factories_per_level,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
compact=compact,
configs_only=configs_only,
non_competitors=None,
non_competitor_params=None,
**kwargs,
)
[docs]
def anac2022_std(
competitors: Sequence[str | type[SCML2020Agent]],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
min_factories_per_level: int = 2,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
non_competitors: Sequence[str | type[SCML2020Agent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = True,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=None,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2022 SCML tournament (standard track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
competitor_params: A list of competitor parameters (used to initialize the competitors).
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (all permutations).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for
distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
non_competitors: A list of agent types that will not be competing in the sabotage competition but will exist
in the world
non_competitor_params: parameters of non competitor agents
dynamic_non_competitors: A list of non-competing agents that are assigned to the simulation dynamically during
the creation of the final assignment instead when the configuration is created
dynamic_non_competitor_params: paramters of dynamic non competitor agents
exclude_competitors_from_reassignment: If true, competitors are excluded from the dynamic non-competitors
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, compact logs will be created and effort will be made to reduce the memory footprint
n_competitors_per_world: Number of competitors in every simulation. If not given it will be a random number
between 2 and min(2, n), where n is the number of competitors
forced_logs_fraction: Fraction of simulations for which logs are always saved (including negotiations)
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
if n_competitors_per_world is None:
n_competitors_per_world = kwargs.get(
"n_competitors_per_world", randint(2, min(4, len(competitors)))
)
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgents2022
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
n_agents_per_competitor=1,
world_generator=anac2022_world_generator,
config_generator=anac2022_config_generator_std,
config_assigner=anac_assigner_std,
score_calculator=balance_calculator2022,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric=truncated_mean,
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
publish_exogenous_summary=True,
publish_trading_prices=True,
**kwargs,
)
[docs]
def anac2022_collusion(
competitors: Sequence[str | type],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
n_agents_per_competitor: int = 3,
min_factories_per_level: int = 2,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
non_competitors: Sequence[str | type[SCML2020Agent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = False,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=1,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2022 SCML tournament (collusion track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
competitor_params: A list of competitor parameters (used to initialize the competitors).
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (all permutations).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
n_agents_per_competitor: Number of agents per competitor
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for
distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
non_competitors: A list of agent types that will not be competing in the sabotage competition but will exist
in the world
non_competitor_params: parameters of non competitor agents
dynamic_non_competitors: A list of non-competing agents that are assigned to the simulation dynamically during
the creation of the final assignment instead when the configuration is created
dynamic_non_competitor_params: paramters of dynamic non competitor agents
exclude_competitors_from_reassignment: If true, competitors are excluded from the dynamic non-competitors
n_competitors_per_world: Number of competitors in every simulation. If not given it will be a random number
between 2 and min(2, n), where n is the number of competitors. This value will
always be set to 1 in SCML2022
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, compact logs will be created and effort will be made to reduce the memory footprint
forced_logs_fraction: Fraction of simulations for which logs are always saved (including negotiations)
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
n_competitors_per_world = 1
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgents2022
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
n_agents_per_competitor=n_agents_per_competitor,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
world_generator=anac2022_world_generator,
config_generator=anac2022_config_generator_collusion,
config_assigner=anac_assigner_collusion,
score_calculator=balance_calculator2022collusion,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric=truncated_mean,
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
publish_exogenous_summary=True,
publish_trading_prices=True,
**kwargs,
)
[docs]
def anac2022_oneshot(
competitors: Sequence[str | type[SCML2020Agent]],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
min_factories_per_level: int = 4,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
non_competitors: Sequence[str | type[SCML2020Agent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = False,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=None,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2022 SCML tournament (oneshot track).
Args:
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
# if competitor_params is None:
# competitor_params = [dict() for _ in range(len(competitors))]
# for t, p in zip(competitors, competitor_params):
# p["controller_type"] = get_full_type_name(t)
# competitors = ["scml.oneshot.world.DefaultOneShotAdapter"] * len(competitors)
if n_competitors_per_world is None:
n_competitors_per_world = kwargs.get(
"n_competitors_per_world", randint(2, min(4, len(competitors)))
)
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgentsOneShot
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
kwargs["oneshot_world"] = True
kwargs["n_processes"] = 2
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
n_agents_per_competitor=1,
world_generator=anac2022_oneshot_world_generator,
config_generator=anac2022_config_generator_oneshot,
config_assigner=anac_assigner_oneshot,
score_calculator=balance_calculator_oneshot,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric=truncated_mean,
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
publish_exogenous_summary=True,
publish_trading_prices=True,
**kwargs,
)
def anac2023_tournament(
competitors: Sequence[str | type[SCML2020Agent]],
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 2,
n_agents_per_competitor: int = 3,
min_factories_per_level: int = 2,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2023 SCML tournament (standard track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (by rotating agents over factories).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
n_agents_per_competitor: Number of agents per competitor
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, effort will be made to reduce memory footprint including disableing most logs
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
return anac2023_std(
competitors=competitors,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
max_worlds_per_config=max_worlds_per_config,
n_runs_per_world=n_runs_per_world,
n_agents_per_competitor=n_agents_per_competitor,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
min_factories_per_level=min_factories_per_level,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
compact=compact,
configs_only=configs_only,
non_competitors=None,
non_competitor_params=None,
**kwargs,
)
[docs]
def anac2023_collusion(
competitors: Sequence[str | type],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
n_agents_per_competitor: int = 3,
min_factories_per_level: int = 2,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
non_competitors: Sequence[str | type[SCML2020Agent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = False,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=1,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2023 SCML tournament (collusion track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
competitor_params: A list of competitor parameters (used to initialize the competitors).
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (all permutations).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
n_agents_per_competitor: Number of agents per competitor
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for
distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
non_competitors: A list of agent types that will not be competing in the sabotage competition but will exist
in the world
non_competitor_params: parameters of non competitor agents
dynamic_non_competitors: A list of non-competing agents that are assigned to the simulation dynamically during
the creation of the final assignment instead when the configuration is created
dynamic_non_competitor_params: paramters of dynamic non competitor agents
exclude_competitors_from_reassignment: If true, competitors are excluded from the dynamic non-competitors
n_competitors_per_world: Number of competitors in every simulation. If not given it will be a random number
between 2 and min(2, n), where n is the number of competitors. This value will
always be set to 1 in SCML2022
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, compact logs will be created and effort will be made to reduce the memory footprint
forced_logs_fraction: Fraction of simulations for which logs are always saved (including negotiations)
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
n_competitors_per_world = 1
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgents2023
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
n_agents_per_competitor=n_agents_per_competitor,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
world_generator=anac2023_world_generator,
config_generator=anac2023_config_generator_collusion,
config_assigner=anac_assigner_collusion,
score_calculator=balance_calculator2023collusion,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric=truncated_mean,
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
publish_exogenous_summary=True,
publish_trading_prices=True,
**kwargs,
)
[docs]
def anac2023_std(
competitors: Sequence[str | type[SCML2020Agent]],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
min_factories_per_level: int = 2,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
non_competitors: Sequence[str | type[SCML2020Agent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = True,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=None,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2023 SCML tournament (standard track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
competitor_params: A list of competitor parameters (used to initialize the competitors).
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (all permutations).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for
distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
non_competitors: A list of agent types that will not be competing in the sabotage competition but will exist
in the world
non_competitor_params: parameters of non competitor agents
dynamic_non_competitors: A list of non-competing agents that are assigned to the simulation dynamically during
the creation of the final assignment instead when the configuration is created
dynamic_non_competitor_params: paramters of dynamic non competitor agents
exclude_competitors_from_reassignment: If true, competitors are excluded from the dynamic non-competitors
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, compact logs will be created and effort will be made to reduce the memory footprint
n_competitors_per_world: Number of competitors in every simulation. If not given it will be a random number
between 2 and min(2, n), where n is the number of competitors
forced_logs_fraction: Fraction of simulations for which logs are always saved (including negotiations)
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
if n_competitors_per_world is None:
n_competitors_per_world = kwargs.get(
"n_competitors_per_world", randint(2, min(4, len(competitors)))
)
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgents2023
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
n_agents_per_competitor=1,
world_generator=anac2023_world_generator,
config_generator=anac2023_config_generator_std,
config_assigner=anac_assigner_std,
score_calculator=balance_calculator2023,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric=truncated_mean,
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
publish_exogenous_summary=True,
publish_trading_prices=True,
**kwargs,
)
[docs]
def anac2023_oneshot(
competitors: Sequence[str | type[SCML2020Agent]],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
min_factories_per_level: int = 4,
tournament_path: str | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: Callable[[SCML2020World | None], None] | None = None,
non_competitors: Sequence[str | type[SCML2020Agent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = False,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=None,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2023 SCML tournament (oneshot track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
competitor_params: A list of competitor parameters (used to initialize the competitors).
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (all permutations).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for
distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
non_competitors: A list of agent types that will not be competing in the sabotage competition but will exist
in the world
non_competitor_params: parameters of non competitor agents
dynamic_non_competitors: A list of non-competing agents that are assigned to the simulation dynamically during
the creation of the final assignment instead when the configuration is created
dynamic_non_competitor_params: paramters of dynamic non competitor agents
exclude_competitors_from_reassignment: If true, competitors are excluded from the dynamic non-competitors
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, compact logs will be created and effort will be made to reduce the memory footprint
n_competitors_per_world: Number of competitors in every simulation. If not given it will be a random number
between 2 and min(2, n), where n is the number of competitors
forced_logs_fraction: Fraction of simulations for which logs are always saved (including negotiations)
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
# if competitor_params is None:
# competitor_params = [dict() for _ in range(len(competitors))]
# for t, p in zip(competitors, competitor_params):
# p["controller_type"] = get_full_type_name(t)
# competitors = ["scml.oneshot.world.DefaultOneShotAdapter"] * len(competitors)
if n_competitors_per_world is None:
n_competitors_per_world = kwargs.get(
"n_competitors_per_world", randint(2, min(4, len(competitors)))
)
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgentsOneShot
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
kwargs["oneshot_world"] = True
kwargs["n_processes"] = 2
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
n_agents_per_competitor=1,
world_generator=anac2023_oneshot_world_generator,
config_generator=anac2023_config_generator_oneshot,
config_assigner=anac_assigner_oneshot,
score_calculator=balance_calculator_oneshot,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric=truncated_mean,
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
publish_exogenous_summary=True,
publish_trading_prices=True,
**kwargs,
)
[docs]
def anac2024_oneshot(
competitors: Sequence[str | type[OneShotAgent]],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
min_factories_per_level: int = 4,
tournament_path: str | Path | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: (
Callable[[SCML2024OneShotWorld | None], None] | None
) = None,
non_competitors: Sequence[str | type[OneShotAgent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[OneShotAgent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = False,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=None,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
context=None,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2024 SCML tournament (oneshot track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
competitor_params: A list of competitor parameters (used to initialize the competitors).
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (all permutations).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for
distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
non_competitors: A list of agent types that will not be competing in the sabotage competition but will exist
in the world
non_competitor_params: parameters of non competitor agents
dynamic_non_competitors: A list of non-competing agents that are assigned to the simulation dynamically during
the creation of the final assignment instead when the configuration is created
dynamic_non_competitor_params: paramters of dynamic non competitor agents
exclude_competitors_from_reassignment: If true, competitors are excluded from the dynamic non-competitors
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, compact logs will be created and effort will be made to reduce the memory footprint
n_competitors_per_world: Number of competitors in every simulation. If not given it will be a random number
between 2 and min(2, n), where n is the number of competitors
forced_logs_fraction: Fraction of simulations for which logs are always saved (including negotiations)
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
# if competitor_params is None:
# competitor_params = [dict() for _ in range(len(competitors))]
# for t, p in zip(competitors, competitor_params):
# p["controller_type"] = get_full_type_name(t)
# competitors = ["scml.oneshot.world.DefaultOneShotAdapter"] * len(competitors)
if n_competitors_per_world is None:
n_competitors_per_world = kwargs.get(
"n_competitors_per_world", randint(2, min(4, len(competitors)))
)
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgentsOneShot
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
kwargs["oneshot_world"] = True
kwargs["n_processes"] = 2
if context:
kwargs["context"] = context
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
n_agents_per_competitor=1,
world_generator=anac2024_oneshot_world_generator,
config_generator=anac2024_config_generator_oneshot,
config_assigner=anac_assigner_oneshot,
score_calculator=balance_calculator_oneshot,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric=truncated_mean,
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
publish_exogenous_summary=True,
publish_trading_prices=True,
**kwargs,
)
[docs]
def anac2024_std(
competitors: Sequence[str | type[StdAgent]],
competitor_params: Sequence[dict[str, Any]] | None = None,
agent_names_reveal_type=False,
n_configs: int = 5,
max_worlds_per_config: int | None = None,
n_runs_per_world: int = 1,
min_factories_per_level: int = 4,
tournament_path: str | Path | Path | None = None,
total_timeout: int | None = None,
parallelism="parallel",
scheduler_ip: str | None = None,
scheduler_port: str | None = None,
tournament_progress_callback: (
Callable[[WorldRunResults | None], None] | None
) = None,
world_progress_callback: (
Callable[[SCML2024OneShotWorld | None], None] | None
) = None,
non_competitors: Sequence[str | type[OneShotAgent]] | None = None,
non_competitor_params: Sequence[dict[str, Any]] | None = None,
dynamic_non_competitors: list[type[Agent]] | None = None,
dynamic_non_competitor_params: list[dict[str, Any]] | None = None,
exclude_competitors_from_reassignment: bool = False,
name: str | None = None,
verbose: bool = False,
configs_only=False,
compact=False,
n_competitors_per_world=None,
forced_logs_fraction: float = FORCED_LOGS_FRACTION,
context=None,
**kwargs,
) -> TournamentResults | PathLike:
"""
The function used to run ANAC 2024 SCML tournament (std track).
Args:
name: Tournament name
competitors: A list of class names for the competitors
competitor_params: A list of competitor parameters (used to initialize the competitors).
agent_names_reveal_type: If true then the type of an agent should be readable in its name (most likely at its
beginning).
n_configs: The number of different world configs (up to competitor assignment) to be generated.
max_worlds_per_config: The maximum number of worlds to run per config. If None, then all possible assignments
of competitors within each config will be tried (all permutations).
n_runs_per_world: Number of runs per world. All of these world runs will have identical competitor assignment
and identical world configuration.
min_factories_per_level: Minimum number of factories for each production level
total_timeout: Total timeout for the complete process
tournament_path: Path at which to store all results. A scores.csv file will keep the scores and logs folder will
keep detailed logs
parallelism: Type of parallelism. Can be 'serial' for serial, 'parallel' for parallel and 'distributed' for
distributed
scheduler_port: Port of the dask scheduler if parallelism is dask, dist, or distributed
scheduler_ip: IP Address of the dask scheduler if parallelism is dask, dist, or distributed
world_progress_callback: A function to be called after everystep of every world run (only allowed for serial
evaluation and should be used with cautious).
tournament_progress_callback: A function to be called with `WorldRunResults` after each world finished
processing
non_competitors: A list of agent types that will not be competing in the sabotage competition but will exist
in the world
non_competitor_params: parameters of non competitor agents
dynamic_non_competitors: A list of non-competing agents that are assigned to the simulation dynamically during
the creation of the final assignment instead when the configuration is created
dynamic_non_competitor_params: paramters of dynamic non competitor agents
exclude_competitors_from_reassignment: If true, competitors are excluded from the dynamic non-competitors
verbose: Verbosity
configs_only: If true, a config file for each
compact: If true, compact logs will be created and effort will be made to reduce the memory footprint
n_competitors_per_world: Number of competitors in every simulation. If not given it will be a random number
between 2 and min(2, n), where n is the number of competitors
forced_logs_fraction: Fraction of simulations for which logs are always saved (including negotiations)
kwargs: Arguments to pass to the `world_generator` function
Returns:
`TournamentResults` The results of the tournament or a `PathLike` giving the location where configs were saved
Remarks:
Default parameters will be used in the league with the exception of `parallelism` which may use distributed
processing
"""
# if competitor_params is None:
# competitor_params = [dict() for _ in range(len(competitors))]
# for t, p in zip(competitors, competitor_params):
# p["controller_type"] = get_full_type_name(t)
# competitors = ["scml.std.world.DefaultOneShotAdapter"] * len(competitors)
if n_competitors_per_world is None:
n_competitors_per_world = kwargs.get(
"n_competitors_per_world", randint(2, min(4, len(competitors)))
)
kwargs.pop("n_competitors_per_world", None)
if non_competitors is None:
non_competitors = DefaultAgentsOneShot
non_competitor_params = [dict() for _ in non_competitors]
kwargs["round_robin"] = kwargs.get("round_robin", ROUND_ROBIN)
kwargs["std_world"] = True
if context:
kwargs["context"] = context
# kwargs["n_processes"] = 2
return tournament(
competitors=competitors,
competitor_params=competitor_params,
non_competitors=non_competitors,
non_competitor_params=non_competitor_params,
agent_names_reveal_type=agent_names_reveal_type,
n_configs=n_configs,
n_runs_per_world=n_runs_per_world,
max_worlds_per_config=max_worlds_per_config,
tournament_path=tournament_path,
total_timeout=total_timeout,
parallelism=parallelism,
scheduler_ip=scheduler_ip,
scheduler_port=scheduler_port,
tournament_progress_callback=tournament_progress_callback,
world_progress_callback=world_progress_callback,
name=name,
verbose=verbose,
configs_only=configs_only,
n_agents_per_competitor=1,
world_generator=anac2024_std_world_generator,
config_generator=anac2024_config_generator_std,
config_assigner=anac_assigner_std,
score_calculator=balance_calculator_std,
min_factories_per_level=min_factories_per_level,
compact=compact,
metric=truncated_mean,
n_competitors_per_world=n_competitors_per_world,
dynamic_non_competitors=dynamic_non_competitors,
dynamic_non_competitor_params=dynamic_non_competitor_params,
exclude_competitors_from_reassignment=exclude_competitors_from_reassignment,
save_video_fraction=0.0,
forced_logs_fraction=forced_logs_fraction,
publish_exogenous_summary=True,
publish_trading_prices=True,
**kwargs,
)