import torch import torch.nn as nn import torch.nn.functional as F class RSSMState: def __init__(self, stochastic: torch.Tensor, deterministic: torch.Tensor): self.stoch = stochastic self.deter = deterministic def get_features(self): """Returns the concatenated stochastic and deterministic state.""" return torch.cat([self.stoch, self.deter], dim=-1) class RSSM(nn.Module): def __init__(self, action_dim: int, embed_dim: int = 256, stoch_dim: int = 32, deter_dim: int = 256, hidden_dim: int = 256): super().__init__() self.stoch_dim = stoch_dim self.deter_dim = deter_dim self.action_dim = action_dim # RNN now takes action_dim + stoch_dim instead of embed_dim self.rnn = nn.GRUCell(action_dim + stoch_dim, deter_dim) # Prior predicts next stochastic state from deterministic state self.prior_net = nn.Sequential( nn.Linear(deter_dim, hidden_dim), nn.ELU(), nn.Linear(hidden_dim, 2 * stoch_dim) ) # Posterior predicts stochastic state from deterministic state + current observation embedding self.post_net = nn.Sequential( nn.Linear(deter_dim + embed_dim, hidden_dim), nn.ELU(), nn.Linear(hidden_dim, 2 * stoch_dim) ) def initial_state(self, batch_size: int, device: torch.device) -> RSSMState: return RSSMState( stochastic=torch.zeros(batch_size, self.stoch_dim, device=device), deterministic=torch.zeros(batch_size, self.deter_dim, device=device) ) def observe_step(self, prev_state: RSSMState, action: torch.Tensor, embed: torch.Tensor) -> tuple: """ Single step of posterior given a real observation embedding. Uses the prior action taken to advance the deterministic dynamics. """ rnn_input = torch.cat([prev_state.stoch, action], dim=-1) deter = self.rnn(rnn_input, prev_state.deter) post_stats = self.post_net(torch.cat([deter, embed], dim=-1)) post_mean, post_std = torch.chunk(post_stats, 2, dim=-1) post_std = F.softplus(post_std) + 0.1 prior_stats = self.prior_net(deter) prior_mean, prior_std = torch.chunk(prior_stats, 2, dim=-1) prior_std = F.softplus(prior_std) + 0.1 stoch = post_mean + post_std * torch.randn_like(post_std) new_state = RSSMState(stochastic=stoch, deterministic=deter) return new_state, (post_mean, post_std), (prior_mean, prior_std) def imagine_step(self, prev_state: RSSMState, action: torch.Tensor) -> RSSMState: """ Single step of prior imagination without real observations. Requires the action intended to be taken. """ rnn_input = torch.cat([prev_state.stoch, action], dim=-1) deter = self.rnn(rnn_input, prev_state.deter) prior_stats = self.prior_net(deter) prior_mean, prior_std = torch.chunk(prior_stats, 2, dim=-1) prior_std = F.softplus(prior_std) + 0.1 stoch = prior_mean + prior_std * torch.randn_like(prior_std) new_state = RSSMState(stochastic=stoch, deterministic=deter) return new_state