PaperTool/workspace/resource_allocation/src/models/environment.py
hc ced50ea2b0 feat(agent): add result-verifier for blind visual comparison
Root cause: test-runner was giving overly optimistic results due to:
1. Context bias - knew the implementation, tended to defend it
2. No actual visual comparison - just wrote 'ACCEPTABLE' without looking
3. No structural validation - accepted 35x scale differences as 'acceptable'

Solution:
- New result-verifier agent that performs blind visual comparison
- Strict pass/fail criteria for structural validation
- Updated test-runner to use result-verifier for each figure
- Clear guidelines: structural mismatches = FAIL, not ACCEPTABLE

Test result: verifier correctly identified Fig3 as FAIL with 7 specific issues:
- Wrong X-axis variable (channels vs power)
- Wrong Y-axis scale (5x difference)
- Wrong curve count (5 vs 4)
- etc.
2026-03-31 23:56:36 +08:00

90 lines
2.6 KiB
Python

"""
src/models/environment.py
Implements Module 1: Environment & Channel Simulator
"""
import numpy as np
from typing import NamedTuple, Tuple
class EnvironmentConfig(NamedTuple):
"""Configuration for the channel simulator."""
num_users: int = 10
num_channels: int = 10
bandwidth: float = 1e6 # Hz, 1 MHz per channel
radius: float = 0.5 # km, cell radius
shadow_fading_std: float = 6.0 # dB
noise_psd_dbm: float = -174.0 # dBm/Hz
class ChannelSimulator:
"""
Simulates the wireless channel environment.
Paper Reference:
- Pathloss: 128.1 + 37.6 lg[d(km)] dB
- Shadow fading: 6 dB
"""
def __init__(self, config: EnvironmentConfig):
self.config = config
def _calculate_pathloss(self, distances: np.ndarray) -> np.ndarray:
"""Calculate pathloss for given distances in km."""
return 128.1 + 37.6 * np.log10(distances)
def generate_channels(
self, transmit_power_dbm: float
) -> Tuple[np.ndarray, np.ndarray]:
"""
Generate channel conditions (SNR) for all users and channels.
Args:
transmit_power_dbm: Transmit power in dBm
Returns:
Tuple of (snr_db, snr_linear) with shape (num_users, num_channels)
"""
N, M = self.config.num_users, self.config.num_channels
# 1. Distances (randomly distributed between 0.05km and cell radius)
min_dist = 0.05
distances = np.random.uniform(min_dist, self.config.radius, size=N)
# 2. Pathloss
path_loss_db = self._calculate_pathloss(distances)
# 3. Shadow fading
shadowing_db = np.random.normal(0, self.config.shadow_fading_std, size=N)
# 4. Total large scale fading (dB)
large_scale_db = path_loss_db + shadowing_db
# 5. Rayleigh fading (small scale)
# Power of Rayleigh follows exponential distribution (mean=1)
small_scale_power = np.random.exponential(1.0, size=(N, M))
small_scale_db = 10 * np.log10(small_scale_power)
# 6. Noise power
# Noise = PSD (dBm/Hz) + 10*log10(BW)
noise_power_dbm = self.config.noise_psd_dbm + 10 * np.log10(
self.config.bandwidth
)
# 7. Calculate SNR
# SNR(dB) = Pt(dBm) - LargeScale(dB) + SmallScale(dB) - Noise(dBm)
snr_db = np.zeros((N, M))
for n in range(N):
snr_db[n, :] = (
transmit_power_dbm
- large_scale_db[n]
+ small_scale_db[n, :]
- noise_power_dbm
)
snr_linear = 10 ** (snr_db / 10)
return snr_db, snr_linear