byte-vortex's picture
download
raw
4.4 kB
"""
Generalized N-agent MatGame environment for reproducing NonZero's Claim 4.
Reward rule per NonZero paper, Appendix D ("Settings of Benchmarks"):
- Linear setting: r_t = sum(agent action indices)
- Non-linear setting: r_t = sum(agent action indices) + u + v
where u ~ N(0, 2^2), v ~ U(-3, 3)
This intentionally mirrors config/matrix/matgame.py's structure (same
gym.Env shape, same step/reset signature) so it drops into the MAZero
repo's config/matrix/ registration with minimal changes.
"""
import gymnasium as gym
import gymnasium.spaces
import numpy as np
class MatgameNonZeroEnv(gym.Env):
"""
Generalized N-agent, N-action matrix game with linear or
linear+noise ("non-linear") joint reward, per NonZero Appendix D.
"""
def __init__(
self,
n_agents=8,
n_actions=10,
reward_type="nonlinear", # "linear" or "nonlinear"
episode_limit=10, # NOTE: matches existing matgame.py's
# convention (episode_limit=10 for all
# variants). Table 1's "Steps" column
# (500/1000/2000) is the *training*
# step budget passed to main.py's
# --training_steps flag -- it is NOT
# this environment's episode length.
noise_std=2.0, # Gaussian noise std (paper: 2)
noise_uniform_range=3.0, # Uniform noise half-range (paper: [-3, 3])
seed=None,
):
assert reward_type in ("linear", "nonlinear")
self.n_agents = n_agents
self.n_actions = n_actions
self.reward_type = reward_type
self.episode_limit = episode_limit
self.noise_std = noise_std
self.noise_uniform_range = noise_uniform_range
self._rng = np.random.default_rng(seed)
self._episode_count = 0
self._episode_steps = 0
self._total_steps = 0
self.observation_space = gym.spaces.Box(
-np.inf, np.inf, shape=(self.n_agents, 1, 1), dtype=np.float64
)
self.action_space = gym.spaces.Discrete(self.n_actions)
def _joint_reward(self, actions):
base = float(np.sum(actions))
if self.reward_type == "linear":
return base
u = self._rng.normal(0.0, self.noise_std)
v = self._rng.uniform(-self.noise_uniform_range, self.noise_uniform_range)
return base + u + v
def step(self, actions):
assert len(actions) == self.n_agents, \
f"expected {self.n_agents} actions, got {len(actions)}"
self._total_steps += 1
self._episode_steps += 1
info = {}
state = np.array([self._episode_steps for _ in range(self.n_agents)])
reward = self._joint_reward(actions)
terminated = self._episode_steps >= self.episode_limit
if terminated:
self._episode_count += 1
return state, reward, terminated, info
def reset(self):
self._episode_steps = 0
state = np.array([self._episode_steps for _ in range(self.n_agents)])
return state
TABLE1_CONFIGS = {}
for n_agents, n_actions in [(2, 3), (4, 5), (6, 8), (8, 10)]:
for reward_type in ["linear", "nonlinear"]:
key = f"{n_agents}x{n_actions}_{reward_type}"
TABLE1_CONFIGS[key] = dict(
env_kwargs=dict(
n_agents=n_agents,
n_actions=n_actions,
reward_type=reward_type,
episode_limit=10,
),
training_steps_reported=[
500 if n_agents == 2 else 1000,
1000 if n_agents == 2 else 2000,
],
)
if __name__ == "__main__":
cfg = TABLE1_CONFIGS["8x10_nonlinear"]["env_kwargs"]
env = MatgameNonZeroEnv(**cfg)
env.reset()
total = 0.0
rng = np.random.default_rng(0)
done = False
while not done:
actions = rng.integers(0, env.n_actions, size=env.n_agents)
_, r, done, _ = env.step(actions)
total += r
print(f"Random-policy episode return (8x10, nonlinear, episode_limit=10): {total:.1f}")
print(f"Max possible per-episode return: {env.n_agents * (env.n_actions - 1) * env.episode_limit}")

Xet Storage Details

Size:
4.4 kB
·
Xet hash:
c3ee6008723a97468fe500d0b9c11e4a7171566c1da7e415cf96a005b14b752b

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.