def METHOD_NAME(self, emissions, targets=None, masks=None, beam=None):
    # HACK: we include "target" which is a hueristic for training
    # HACK: we use a beam of tokens to approximate the normalizing factor (which is bad?)
    beam = beam if beam is not None else self.beam
    batch_size, seq_len = emissions.size()[:2]
    if targets is not None:
        _emissions = emissions.scatter(2, targets[:, :, None], np.float("inf"))
        beam_targets = _emissions.topk(beam, 2)[1]
        beam_emission_scores = emissions.gather(2, beam_targets)
    else:
        beam_emission_scores, beam_targets = emissions.topk(beam, 2)
    beam_transition_score1 = self.E1(beam_targets[:, :-1])  # B x (T-1) x K x D
    beam_transition_score2 = self.E2(beam_targets[:, 1:])  # B x (T-1) x K x D
    beam_transition_matrix = torch.bmm(
        beam_transition_score1.view(-1, beam, self.rank),
        beam_transition_score2.view(-1, beam, self.rank).transpose(1, 2),
    )
    beam_transition_matrix = beam_transition_matrix.view(batch_size, -1, beam, beam)
    # compute the normalizer in the log-space
    score = beam_emission_scores[:, 0]  # B x K
    for i in range(1, seq_len):
        next_score = score[:, :, None] + beam_transition_matrix[:, i - 1]
        next_score = logsumexp(next_score, dim=1) + beam_emission_scores[:, i]
        if masks is not None:
            score = torch.where(masks[:, i : i + 1], next_score, score)
        else:
            score = next_score
    # Sum (log-sum-exp) over all possible tags
    return logsumexp(score, dim=1)