Files
2026-07-13 13:17:40 +08:00

143 lines
4.7 KiB
Python

import tempfile
import unittest
import gymnasium as gym
import numpy as np
import ray
import ray.rllib.algorithms.ppo as ppo
from ray.rllib.algorithms.ppo.ppo import LEARNER_RESULTS_CURR_KL_COEFF_KEY
from ray.rllib.core.columns import Columns
from ray.rllib.examples.envs.classes.multi_agent import MultiAgentCartPole
from ray.rllib.utils.metrics import LEARNER_RESULTS
from ray.rllib.utils.test_utils import check
from ray.tune.registry import register_env
# Fake CartPole episode of n time steps.
FAKE_BATCH = {
Columns.OBS: np.array(
[[0.1, 0.2, 0.3, 0.4], [0.5, 0.6, 0.7, 0.8], [0.9, 1.0, 1.1, 1.2]],
dtype=np.float32,
),
Columns.NEXT_OBS: np.array(
[[0.1, 0.2, 0.3, 0.4], [0.5, 0.6, 0.7, 0.8], [0.9, 1.0, 1.1, 1.2]],
dtype=np.float32,
),
Columns.ACTIONS: np.array([0, 1, 1]),
Columns.REWARDS: np.array([1.0, -1.0, 0.5], dtype=np.float32),
Columns.TERMINATEDS: np.array([False, False, True]),
Columns.TRUNCATEDS: np.array([False, False, False]),
Columns.VF_PREDS: np.array([0.5, 0.6, 0.7], dtype=np.float32),
Columns.ACTION_DIST_INPUTS: np.array(
[[-2.0, 0.5], [-3.0, -0.3], [-0.1, 2.5]], dtype=np.float32
),
Columns.ACTION_LOGP: np.array([-0.5, -0.1, -0.2], dtype=np.float32),
Columns.EPS_ID: np.array([0, 0, 0]),
}
class TestPPO(unittest.TestCase):
ENV = gym.make("CartPole-v1")
@classmethod
def setUpClass(cls):
ray.init()
@classmethod
def tearDownClass(cls):
ray.shutdown()
def test_save_to_path_and_restore_from_path(self):
"""Tests saving and loading the state of the PPO Learner Group."""
config = (
ppo.PPOConfig()
.environment("CartPole-v1")
.env_runners(
num_env_runners=0,
)
.training(
gamma=0.99,
model=dict(
fcnet_hiddens=[10, 10],
fcnet_activation="linear",
vf_share_layers=False,
),
)
)
algo_config = config.copy(copy_frozen=False)
algo_config.validate()
algo_config.freeze()
learner_group1 = algo_config.build_learner_group(env=self.ENV)
learner_group2 = algo_config.build_learner_group(env=self.ENV)
with tempfile.TemporaryDirectory() as tmpdir:
learner_group1.save_to_path(tmpdir)
learner_group2.restore_from_path(tmpdir)
# Remove functions from state b/c they are not comparable via `check`.
s1 = learner_group1.get_state()
s2 = learner_group2.get_state()
check(s1, s2)
def test_kl_coeff_changes(self):
# Simple environment with 4 independent cartpole entities
register_env(
"multi_agent_cartpole", lambda _: MultiAgentCartPole({"num_agents": 2})
)
initial_kl_coeff = 0.01
config = (
ppo.PPOConfig()
.environment("CartPole-v1")
.env_runners(
num_env_runners=0,
rollout_fragment_length=50,
exploration_config={},
)
.training(
gamma=0.99,
model=dict(
fcnet_hiddens=[10, 10],
fcnet_activation="linear",
vf_share_layers=False,
),
kl_coeff=initial_kl_coeff,
)
.environment("multi_agent_cartpole")
.multi_agent(
policies={"p0", "p1"},
policy_mapping_fn=lambda agent_id, episode, **kwargs: (
"p{}".format(agent_id % 2)
),
)
)
algo = config.build()
# Call train while results aren't returned because this is
# a asynchronous Algorithm and results are returned asynchronously.
curr_kl_coeff_1 = None
curr_kl_coeff_2 = None
while not curr_kl_coeff_1 or not curr_kl_coeff_2:
results = algo.train()
# Attempt to get the current KL coefficient from the learner.
# Iterate until we have found both coefficients at least once.
if "p0" in results[LEARNER_RESULTS]:
curr_kl_coeff_1 = results[LEARNER_RESULTS]["p0"][
LEARNER_RESULTS_CURR_KL_COEFF_KEY
]
if "p1" in results[LEARNER_RESULTS]:
curr_kl_coeff_2 = results[LEARNER_RESULTS]["p1"][
LEARNER_RESULTS_CURR_KL_COEFF_KEY
]
self.assertNotEqual(curr_kl_coeff_1, initial_kl_coeff)
self.assertNotEqual(curr_kl_coeff_2, initial_kl_coeff)
if __name__ == "__main__":
import sys
import pytest
sys.exit(pytest.main(["-v", __file__]))