def pg_error(data: namedtuple) -> namedtuple:
    """
    **Overview**:
        Implementation of PG (Policy Gradient)
    """
    # Unpack data: $$<\pi(a|s), a, G_t>$$
    logit, action, return_ = data
    # Prepare policy distribution from logit and get log propability.
    dist = torch.distributions.categorical.Categorical(logits=logit)
    log_prob = dist.log_prob(action)
    # Policy loss: $$- \frac 1 N \sum_{n=1}^{N} log(\pi(a^n|s^n)) G_t^n$$
    policy_loss = -(log_prob * return_).mean()
    # Entropy bonus: $$\frac 1 N \sum_{n=1}^{N} \sum_{a^n}\pi(a^n|s^n) log(\pi(a^n|s^n))$$
    # P.S. the final loss is ``policy_loss - entropy_weight * entropy_loss`` .
    entropy_loss = dist.entropy().mean()
    # Return the concrete loss items.
    return pg_loss(policy_loss, entropy_loss)

    **Overview**:
        Implementation of PG (Policy Gradient)
    
chapter1_overview/pg.py