If y_true looks like [[1, 1, 0], [0, 1, 1]] and y_hat looks like [[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]], try the following “no for” implementation. (If your implementation is correct.)
def cdl(y_hat, y_true):
    pre_sub = (y_hat[:, :, None] - y_hat[:, None, :])
    pre_exp = torch.exp(pre_sub)
    y_z, y_o = y_true == 0, y_true != 0
    num_comparisons = y_z.sum(dim=1) * y_true.sum(dim=1)
    pre_exp = pre_exp / num_comparisons[:, None, None]
    mask = torch.logical_and(y_z[:, :, None], y_o[:, None, :])
    loss = pre_exp[mask].sum()
    return loss

PS: Since pre_sub and pre_exp calculate all pairs, if batch << classes, maybe “for implementation” is faster(I am not sure)?