| import os |
| os.environ["TF_USE_LEGACY_KERAS"] = "1" |
| import psutil |
| import numpy as np |
| import tensorflow as tf |
| from tf_agents.environments import py_environment |
| from tf_agents.specs import array_spec |
| from tf_agents.trajectories import time_step as ts |
| from config import Config |
|
|
| try: |
| import pynvml |
| pynvml.nvmlInit() |
| HAS_NVML = True |
| except ImportError: |
| HAS_NVML = False |
|
|
| def configure_gpu(): |
| """Configures TensorFlow memory growth to prevent VRAM allocation errors.""" |
| gpus = tf.config.list_physical_devices('GPU') |
| if gpus: |
| try: |
| for gpu in gpus: |
| tf.config.experimental.set_memory_growth(gpu, True) |
| print(f"✅ Found and configured {len(gpus)} GPU(s).") |
| except RuntimeError as e: |
| print(e) |
| else: |
| print("⚠️ No GPU found. Falling back to CPU.") |
|
|
| def get_system_telemetry(): |
| """Fetches real-time RAM, CPU, and GPU usage metrics for TensorBoard profiling.""" |
| metrics = { |
| 'system/ram_percent': psutil.virtual_memory().percent, |
| 'system/cpu_percent': psutil.cpu_percent(), |
| } |
| |
| if HAS_NVML: |
| try: |
| handle = pynvml.nvmlDeviceGetHandleByIndex(0) |
| util = pynvml.nvmlDeviceGetUtilizationRates(handle) |
| temp = pynvml.nvmlDeviceGetTemperature(handle, pynvml.NVML_TEMPERATURE_GPU) |
| mem = pynvml.nvmlDeviceGetMemoryInfo(handle) |
| |
| metrics['hardware/gpu_utilization'] = float(util.gpu) |
| metrics['hardware/gpu_temperature_c'] = float(temp) |
| metrics['hardware/gpu_memory_percent'] = (mem.used / mem.total) * 100.0 |
| except Exception: |
| pass |
| |
| return metrics |
|
|
| class PettingZooToTFAgentsWrapper(py_environment.PyEnvironment): |
| """Wraps a PettingZoo Parallel environment into a TF-Agents PyEnvironment.""" |
| |
| def __init__(self, pz_env): |
| super().__init__() |
| self._env = pz_env |
| self._current_info = {} |
| |
| |
| total_nodes = Config.TOTAL_HOSTS() |
| |
| |
| self._action_spec = { |
| 'red': array_spec.BoundedArraySpec(shape=(), dtype=np.int32, minimum=0, maximum=(total_nodes * 4) - 1, name='red_action'), |
| 'blue': array_spec.BoundedArraySpec(shape=(), dtype=np.int32, minimum=0, maximum=(total_nodes * 2) - 1, name='blue_action') |
| } |
| |
| |
| self._observation_spec = { |
| agent: { |
| 'node_features': array_spec.BoundedArraySpec(shape=(total_nodes, 2), dtype=np.float32, minimum=0, maximum=1, name='features'), |
| 'adjacency_matrix': array_spec.BoundedArraySpec(shape=(total_nodes, total_nodes), dtype=np.float32, minimum=0, maximum=1, name='topology') |
| } for agent in ['red', 'blue'] |
| } |
|
|
| def action_spec(self): |
| return self._action_spec |
|
|
| def observation_spec(self): |
| return self._observation_spec |
|
|
| def reward_spec(self): |
| """Declares multi-agent reward specs to prevent unpacking errors.""" |
| return { |
| 'red': array_spec.ArraySpec(shape=(), dtype=np.float32, name='red_reward'), |
| 'blue': array_spec.ArraySpec(shape=(), dtype=np.float32, name='blue_reward') |
| } |
|
|
| def get_current_info(self): |
| """Safely forwards custom internal metadata down to the trainer.""" |
| return self._current_info |
|
|
| def _reset(self): |
| obs, infos = self._env.reset() |
| self._current_info = infos |
| |
| |
| formatted_obs = { |
| 'red': { |
| 'node_features': np.array(obs['red']['node_features'], dtype=np.float32), |
| 'adjacency_matrix': np.array(obs['red']['adjacency_matrix'], dtype=np.float32) |
| }, |
| 'blue': { |
| 'node_features': np.array(obs['blue']['node_features'], dtype=np.float32), |
| 'adjacency_matrix': np.array(obs['blue']['adjacency_matrix'], dtype=np.float32) |
| } |
| } |
| |
| initial_rewards = { |
| 'red': np.array(0.0, dtype=np.float32), |
| 'blue': np.array(0.0, dtype=np.float32) |
| } |
| |
| return ts.TimeStep( |
| step_type=np.array(ts.StepType.FIRST, dtype=np.int32), |
| reward=initial_rewards, |
| discount=np.array(1.0, dtype=np.float32), |
| observation=formatted_obs |
| ) |
|
|
| def _step(self, action): |
| |
| pz_actions = {'red': int(action['red']), 'blue': int(action['blue'])} |
| obs, rewards, terminations, truncations, infos = self._env.step(pz_actions) |
| |
| self._current_info = infos |
| |
| |
| formatted_obs = { |
| 'red': { |
| 'node_features': np.array(obs['red']['node_features'], dtype=np.float32), |
| 'adjacency_matrix': np.array(obs['red']['adjacency_matrix'], dtype=np.float32) |
| }, |
| 'blue': { |
| 'node_features': np.array(obs['blue']['node_features'], dtype=np.float32), |
| 'adjacency_matrix': np.array(obs['blue']['adjacency_matrix'], dtype=np.float32) |
| } |
| } |
| |
| formatted_rewards = { |
| 'red': np.array(rewards['red'], dtype=np.float32), |
| 'blue': np.array(rewards['blue'], dtype=np.float32) |
| } |
| |
| if terminations['red'] or truncations['red']: |
| return ts.termination(formatted_obs, reward=formatted_rewards) |
| |
| return ts.transition(formatted_obs, reward=formatted_rewards, discount=np.array(0.99, dtype=np.float32)) |