class ActorCritic(nnx.Module):
"""A policy and a value function, each with its own optimizer."""
def __init__(self, policy, value, lr=1e-2):
self.policy, self.value = policy, value
self.opt_pi = nnx.Optimizer(policy, optax.adam(lr), wrt=nnx.Param)
self.opt_v = nnx.Optimizer(value, optax.adam(lr), wrt=nnx.Param)
def log_prob(self, obs, act, policy=None):
"""log pi(a|s). Gradients flow w.r.t. the module you differentiate;
the update functions pass that module back in as `policy`."""
policy = self.policy if policy is None else policy
logp = jax.nn.log_softmax(policy(obs), axis=-1)
return jnp.take_along_axis(logp, act[:, None], axis=-1).squeeze(-1)
def V(self, obs, value=None):
value = self.value if value is None else value
return value(obs).squeeze(-1)
@classmethod
def tabular(cls, num_states, num_actions, lr=0.1, rngs=None):
"""One preference theta_{s,a} per state-action pair: an embedding."""
rngs = nnx.Rngs(d2l.get_key()) if rngs is None else rngs
zeros = nnx.initializers.zeros_init()
return cls(nnx.Embed(num_states, num_actions,
embedding_init=zeros, rngs=rngs),
nnx.Embed(num_states, 1, embedding_init=zeros, rngs=rngs),
lr)