From cd8fc6d42d153b28b5cd731a4fe19239bfcc327d Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Fri, 13 Sep 2024 12:10:49 +0100 Subject: [PATCH 1/8] #2879: Handle generate_seed_value option --- src/primaite/session/environment.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/primaite/session/environment.py b/src/primaite/session/environment.py index c66663e3..ac9415ac 100644 --- a/src/primaite/session/environment.py +++ b/src/primaite/session/environment.py @@ -26,14 +26,25 @@ except ModuleNotFoundError: _LOGGER.debug("Torch not available for importing") -def set_random_seed(seed: int) -> Union[None, int]: +def set_random_seed(seed: int, generate_seed_value: bool) -> Union[None, int]: """ Set random number generators. + If seed is None or -1 and generate_seed_value is True randomly generate a + seed value. + If seed is > -1 and generate_seed_value is True ignore the latter and use + the provide seed value. + :param seed: int + :param generate_seed_value: bool + :return: None or the int representing the seed used. """ if seed is None or seed == -1: - return None + if generate_seed_value: + rng = np.random.default_rng() + seed = int(rng.integers(low=0, high=2**63)) + else: + return None elif seed < -1: raise ValueError("Invalid random number seed") # Seed python RNG @@ -65,7 +76,8 @@ class PrimaiteGymEnv(gymnasium.Env): """Object that returns a config corresponding to the current episode.""" self.seed = self.episode_scheduler(0).get("game", {}).get("seed") """Get RNG seed from config file. NB: Must be before game instantiation.""" - self.seed = set_random_seed(self.seed) + self.generate_seed_value = self.episode_scheduler(0).get("game", {}).get("generate_seed_value") + self.seed = set_random_seed(self.seed, self.generate_seed_value) self.io = PrimaiteIO.from_config(self.episode_scheduler(0).get("io_settings", {})) """Handles IO for the environment. This produces sys logs, agent logs, etc.""" self.game: PrimaiteGame = PrimaiteGame.from_config(self.episode_scheduler(0)) From 6ebe50c331725c5059f269a59d87bd1dcd4077b3 Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Fri, 13 Sep 2024 12:58:37 +0100 Subject: [PATCH 2/8] #2879: Reduce max seed value to comply with python random seed limit --- src/primaite/session/environment.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/primaite/session/environment.py b/src/primaite/session/environment.py index ac9415ac..0fd21b9f 100644 --- a/src/primaite/session/environment.py +++ b/src/primaite/session/environment.py @@ -42,7 +42,8 @@ def set_random_seed(seed: int, generate_seed_value: bool) -> Union[None, int]: if seed is None or seed == -1: if generate_seed_value: rng = np.random.default_rng() - seed = int(rng.integers(low=0, high=2**63)) + # 2**32-1 is highest value for python RNG seed. + seed = int(rng.integers(low=0, high=2**32-1)) else: return None elif seed < -1: From 08fcf1df19fc811bf9a24aee04d8c5c3239f9678 Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Fri, 13 Sep 2024 12:59:41 +0100 Subject: [PATCH 3/8] #2879: Add generate_seed_value to global options. --- src/primaite/game/game.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/primaite/game/game.py b/src/primaite/game/game.py index 123b6ddd..0e7b8c23 100644 --- a/src/primaite/game/game.py +++ b/src/primaite/game/game.py @@ -80,6 +80,8 @@ class PrimaiteGameOptions(BaseModel): seed: int = None """Random number seed for RNGs.""" + generate_seed_value: bool = False + """Internally generated seed value.""" max_episode_length: int = 256 """Maximum number of episodes for the PrimAITE game.""" ports: List[str] From 01a2c834ce3c8ff23c90ff098ef2cce04bdd5bab Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Fri, 13 Sep 2024 14:53:15 +0100 Subject: [PATCH 4/8] #2879: Write seed value to log file. --- src/primaite/session/environment.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/primaite/session/environment.py b/src/primaite/session/environment.py index 0fd21b9f..9054106e 100644 --- a/src/primaite/session/environment.py +++ b/src/primaite/session/environment.py @@ -62,6 +62,13 @@ def set_random_seed(seed: int, generate_seed_value: bool) -> Union[None, int]: return seed +def log_seed_value(seed: int): + """Log the selected seed value to file.""" + path = SIM_OUTPUT.path / "seed.log" + with open(path, "w") as file: + file.write(f"Seed value = {seed}") + + class PrimaiteGymEnv(gymnasium.Env): """ Thin wrapper env to provide agents with a gymnasium API. @@ -92,6 +99,8 @@ class PrimaiteGymEnv(gymnasium.Env): _LOGGER.info(f"PrimaiteGymEnv RNG seed = {self.seed}") + log_seed_value(self.seed) + def action_masks(self) -> np.ndarray: """ Return the action mask for the agent. From e0a10928343c650b986da8aa8cd6207786448e0f Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Mon, 16 Sep 2024 09:04:17 +0100 Subject: [PATCH 5/8] #2879: Pre-commit fix. --- src/primaite/session/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primaite/session/environment.py b/src/primaite/session/environment.py index 9054106e..07635b70 100644 --- a/src/primaite/session/environment.py +++ b/src/primaite/session/environment.py @@ -43,7 +43,7 @@ def set_random_seed(seed: int, generate_seed_value: bool) -> Union[None, int]: if generate_seed_value: rng = np.random.default_rng() # 2**32-1 is highest value for python RNG seed. - seed = int(rng.integers(low=0, high=2**32-1)) + seed = int(rng.integers(low=0, high=2**32 - 1)) else: return None elif seed < -1: From 215ceaa6e8b5977b231d226715b70d8e88df7f14 Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Mon, 16 Sep 2024 10:08:45 +0100 Subject: [PATCH 6/8] #2879: Fix call to set_random_seed() in reset(). --- src/primaite/session/environment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/primaite/session/environment.py b/src/primaite/session/environment.py index 07635b70..db5425e3 100644 --- a/src/primaite/session/environment.py +++ b/src/primaite/session/environment.py @@ -168,7 +168,7 @@ class PrimaiteGymEnv(gymnasium.Env): f"avg. reward: {self.agent.reward_function.total_reward}" ) if seed is not None: - set_random_seed(seed) + set_random_seed(seed, self.generate_seed_value) self.total_reward_per_episode[self.episode_counter] = self.agent.reward_function.total_reward if self.io.settings.save_agent_actions: From f3ca9c55c90fe05b2e43c2c167767037578a0fb7 Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Mon, 16 Sep 2024 16:38:19 +0100 Subject: [PATCH 7/8] #2879: Update tests --- .../game_layer/test_RNG_seed.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/integration_tests/game_layer/test_RNG_seed.py b/tests/integration_tests/game_layer/test_RNG_seed.py index 0c6d567d..508f35e6 100644 --- a/tests/integration_tests/game_layer/test_RNG_seed.py +++ b/tests/integration_tests/game_layer/test_RNG_seed.py @@ -7,6 +7,7 @@ import yaml from primaite.config.load import data_manipulation_config_path from primaite.game.agent.interface import AgentHistoryItem from primaite.session.environment import PrimaiteGymEnv +from primaite.simulator import SIM_OUTPUT @pytest.fixture() @@ -33,6 +34,11 @@ def test_rng_seed_set(create_env): assert a == b + # Check that seed log file was created. + path = SIM_OUTPUT.path / "seed.log" + with open(path, "r") as file: + assert file + def test_rng_seed_unset(create_env): """Test with no RNG seed.""" @@ -48,3 +54,19 @@ def test_rng_seed_unset(create_env): b = [item.timestep for item in env.game.agents["client_2_green_user"].history if item.action != "DONOTHING"] assert a != b + + +def test_for_generated_seed(): + """ + Show that setting generate_seed_value to true producess a valid seed. + """ + with open(data_manipulation_config_path(), "r") as f: + cfg = yaml.safe_load(f) + + cfg["game"]["generate_seed_value"] = True + PrimaiteGymEnv(env_config=cfg) + path = SIM_OUTPUT.path / "seed.log" + with open(path, "r") as file: + data = file.read() + + assert data.split(" ")[3] != None From 078b89856535b0071c76921612b6758f6d48782c Mon Sep 17 00:00:00 2001 From: Nick Todd Date: Tue, 17 Sep 2024 09:30:14 +0100 Subject: [PATCH 8/8] #2879: Update changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 77b7bb7d..a9f6c891 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Log observation space data by episode and step. - ACL's are no longer applied to layer-2 traffic. +- Random number seed values are recorded in simulation/seed.log if the seed is set in the config file + or `generate_seed_value` is set to `true`. ## [3.3.0] - 2024-09-04 ### Added