13
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
"""训练/部署观测和动作的一致性,以及RL改进的目标口径。"""
|
||||
import importlib.util
|
||||
import math
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
np = pytest.importorskip('numpy')
|
||||
pytest.importorskip('gymnasium')
|
||||
|
||||
from mcm26b.rl.env import LocalizationEnv
|
||||
from mcm26b.rl.features import action_mask, build_observation, candidate_actions
|
||||
|
||||
|
||||
def test_training_observation_matches_runtime_channel_belief():
|
||||
env = LocalizationEnv(problem=3, handoff_probability=1)
|
||||
obs, _ = env.reset(seed=1)
|
||||
belief = env._belief.channels[env._channel]
|
||||
expected = build_observation(belief.region, env._client.state.position, env._channel,
|
||||
env.config, env.problem, env._blocked, belief,
|
||||
virtual_time_s=env._client.state.virtual_time_s,
|
||||
receiver_channel=env._client.state.channel)
|
||||
np.testing.assert_array_equal(obs, expected)
|
||||
assert obs[7] == np.float32(1/12)
|
||||
assert tuple(obs[14:17]) == (0,1,0)
|
||||
assert env._client.state.channel != env._channel
|
||||
env.close()
|
||||
|
||||
|
||||
def test_mask_rejects_measured_and_duplicate_candidates():
|
||||
env = LocalizationEnv(problem=3)
|
||||
env.reset(seed=17)
|
||||
belief = env._belief.channels[env._channel]
|
||||
region = belief.region
|
||||
position = env._client.state.position
|
||||
candidates = candidate_actions(region, position, env.config)
|
||||
belief.measurements.append(dict(position=candidates[0], result='no_signal', svd_deg=None))
|
||||
mask = env.action_masks()
|
||||
assert not mask[0]
|
||||
keys = [(round(p[0]*2),round(p[1]*2)) for p,v in zip(candidates,mask) if v]
|
||||
assert len(keys) == len(set(keys))
|
||||
assert any(mask)
|
||||
assert all(math.dist(p,region.mec()[0]) <= 1000-region.mec()[1]+1e-6
|
||||
for p,v in zip(candidates,mask) if v)
|
||||
env.close()
|
||||
|
||||
|
||||
def test_q4_blocked_sequence_resets_on_direction():
|
||||
env = LocalizationEnv(problem=4,directional_probability=1,handoff_probability=0)
|
||||
env.reset(seed=7)
|
||||
for expected in ('no_signal','direction'):
|
||||
region = env._belief.candidate_set(env._channel)
|
||||
candidates = candidate_actions(region,env._client.state.position,env.config,env._blocked)
|
||||
# 只有测试端可用真值选择一个指定分支,策略从不接触该接口。
|
||||
index = next(i for i,p in enumerate(candidates)
|
||||
if env._simulator._observe(p,env._channel)[0] == expected)
|
||||
_, _, terminated, truncated, _ = env.step(index)
|
||||
assert len(env._blocked) == (1 if expected=='no_signal' else 0)
|
||||
if expected=='no_signal':
|
||||
assert not terminated and not truncated
|
||||
env.close()
|
||||
|
||||
|
||||
def test_success_reward_is_constant_minus_actual_virtual_cost():
|
||||
env = LocalizationEnv(problem=3,handoff_probability=0)
|
||||
env.reset(seed=3)
|
||||
total = 0
|
||||
for _ in range(16):
|
||||
legal = np.flatnonzero(env.action_masks())
|
||||
action = int(0 if 0 in legal else legal[0])
|
||||
_, reward, terminated, truncated, info = env.step(action)
|
||||
total += reward
|
||||
if terminated or truncated:
|
||||
break
|
||||
assert info['cleared']
|
||||
assert total == pytest.approx(20-info['virtual_cost_s']/100)
|
||||
assert env._client.state.exited
|
||||
|
||||
|
||||
def test_bc_and_ppo_forward_mapping_agree():
|
||||
torch = pytest.importorskip('torch')
|
||||
from stable_baselines3 import PPO
|
||||
spec = importlib.util.spec_from_file_location('pretrain',Path('scripts/pretrain_rl_expert.py'))
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
torch.set_num_threads(1)
|
||||
env = LocalizationEnv()
|
||||
ppo = PPO('MlpPolicy',env,policy_kwargs={'net_arch':[128,128]},device='cpu',n_steps=16,batch_size=8)
|
||||
bc = module.BCMlp()
|
||||
state = bc.net.state_dict()
|
||||
ppo.policy.mlp_extractor.policy_net.load_state_dict({k:v for k,v in state.items() if not k.startswith('4.')})
|
||||
ppo.policy.action_net.load_state_dict({'weight':state['4.weight'],'bias':state['4.bias']})
|
||||
x = torch.randn(10,401)
|
||||
torch.testing.assert_close(bc(x),ppo.policy.action_net(ppo.policy.mlp_extractor.policy_net(x)))
|
||||
env.close()
|
||||
Reference in New Issue
Block a user