"""Load and manage Disco103 pre-trained weights. This module handles loading the Disco103 meta-parameters from the npz file provided in the DiscoRL repository and integrating them with DiscoRL agents. """ import os from typing import Any, Dict, Tuple import numpy as np import jax import jax.numpy as jnp def load_disco103_weights(disco_rl_path: str = None) -> Dict[str, Any]: """Load Disco103 pre-trained weights. Args: disco_rl_path: path to disco_rl repo root. If None, search from cwd. Returns: dict with keys like 'meta_params' or structure matching the npz file """ if disco_rl_path is None: # Try to find disco_rl in standard locations possible_paths = [ 'disco_rl/disco_rl/update_rules/weights/disco_103.npz', '../disco_rl/disco_rl/update_rules/weights/disco_103.npz', '../../disco_rl/disco_rl/update_rules/weights/disco_103.npz', ] npz_path = None for p in possible_paths: if os.path.exists(p): npz_path = p break if npz_path is None: raise FileNotFoundError( 'Could not find disco_103.npz. Please provide disco_rl_path ' 'or ensure disco_rl/ is accessible.' ) else: npz_path = os.path.join( disco_rl_path, 'disco_rl/update_rules/weights/disco_103.npz' ) if not os.path.exists(npz_path): raise FileNotFoundError(f'disco_103.npz not found at {npz_path}') # Load the npz file data = np.load(npz_path, allow_pickle=True) # Convert to dictionary; npz files can be accessed as dict-like weights = {} for key in data.files: item = data[key] # Some items might be numpy object arrays (e.g., nested structures) # Try to convert to jax arrays where possible if isinstance(item, np.ndarray): weights[key] = jnp.asarray(item) else: weights[key] = item print(f'Loaded Disco103 weights from {npz_path}') print(f' Keys: {list(weights.keys())}') for key, val in weights.items(): if hasattr(val, 'shape'): print(f' {key}: shape={val.shape}, dtype={val.dtype}') else: print(f' {key}: {type(val)}') return weights def unflatten_disco_weights(flat_dict: Dict[str, Any]) -> Dict[str, Any]: """Convert flat npz weight dict to nested structure expected by DiscoRL. The exact structure depends on how the weights were saved. This is a placeholder; you may need to adjust based on the actual npz structure. For now, we assume the npz contains the meta_params directly or under a 'meta_params' key. """ # If there's a 'meta_params' key, use it; otherwise assume flat_dict IS the params if 'meta_params' in flat_dict: meta_params = flat_dict['meta_params'] else: # Try to reconstruct nested structure from flat keys # This is environment-specific; adjust as needed meta_params = flat_dict return meta_params def merge_weights_with_agent( agent_meta_state: Dict[str, Any], disco_weights: Dict[str, Any], ) -> Dict[str, Any]: """Merge loaded Disco103 weights into agent's meta_state. This updates the meta_state's rnn_state and other components with pre-trained weights if available. Args: agent_meta_state: the agent's initial meta_state dict disco_weights: loaded weights dict Returns: updated agent_meta_state """ # For now, we mainly care about the meta_params (update_rule weights) # The rnn_state and ema_state are often initialized fresh during # agent creation, but we can override them if they're in disco_weights. updated_state = dict(agent_meta_state) # If the npz has useful rnn_state or other components, merge them # This is a placeholder; adjust based on actual npz structure for key in ['rnn_state', 'adv_ema_state', 'td_ema_state']: if key in disco_weights: updated_state[key] = disco_weights[key] return updated_state