    def _input_likelihood_lannoy(
        self, logits: torch.Tensor, tags: torch.Tensor, mask: torch.BoolTensor
    ) -> torch.Tensor:
        """
        Computes the (batch_size,) denominator term for the log-likelihood, which is the
        sum of the likelihoods across all possible state sequences.

        Compute this value using the scaling trick instead of the log domain trick, since
        this is necessary to implement the label-weighting method by Lannoy et al. (2012).
        """
        batch_size, sequence_length, num_tags = logits.size()

        # Transpose batch size and sequence dimensions
        mask = mask.transpose(0, 1).contiguous()
        logits = logits.transpose(0, 1).contiguous()
        tags = tags.transpose(0, 1).contiguous()

        # insert an 1-sized second dimension to match z.shape
        label_weights = self.label_weights.view(num_tags, 1)

        # emit_scores.shape = (batch_size, num_tags)
        emit_scores = logits[0]

        # Initial alpha is the (batch_size, num_tags) tensor of likelihoods combining the
        # transitions to the initial states and the logits for the first timestep.
        # alpha.shape = (batch_size, num_tags)
        if self.include_start_end_transitions:
            alpha = torch.exp(self.start_transitions.view(1, num_tags) + emit_scores)
        else:
            alpha = torch.exp(emit_scores)

        # z.shape = (batch_size, 1)
        z = alpha.sum(dim=1, keepdim=True)
        alpha = alpha / z
        sum_log_z = torch.log(z) * label_weights[tags[0]]

        # For each i we compute logits for the transitions from timestep i-1 to timestep i.
        # We do so in a (batch_size, num_tags, num_tags) tensor where the axes are
        # (instance, current_tag, next_tag)
        for i in range(1, sequence_length):
            # multiply the logits by the label weights
            # logits[i].shape: (batch_size, num_tags)
            # emit_scores = torch.mul(logits[i], label_weights)
            emit_scores = logits[i]

            # The emit scores are for time i ("next_tag") so we broadcast along the current_tag axis.
            emit_scores = emit_scores.view(batch_size, 1, num_tags)
            # Transition scores are (current_tag, next_tag) so we broadcast along the instance axis.
            transition_scores = self.transitions.view(1, num_tags, num_tags)
            # Alpha is for the current_tag (i-1), so we broadcast along the next_tag axis.
            broadcast_alpha = alpha.view(batch_size, num_tags, 1)

            # Add all the scores together and logexp over the current_tag axis.
            inner = broadcast_alpha * torch.exp(emit_scores + transition_scores)

            # In valid positions (mask == True) we want to take the logsumexp over the current_tag dimension
            # of `inner`. Otherwise (mask == False) we want to retain the previous alpha.
            alpha = inner.sum(dim=1) * mask[i].view(batch_size, 1) + alpha * (~mask[i]).view(
                batch_size, 1
            )

            # scale alphas to avoid underflow (sum of alphas equal to 1)
            z = alpha.sum(dim=1, keepdim=True)
            alpha = alpha / z
            # weight z (normalization factor) according to the current tag
            sum_log_z += torch.log(z) * label_weights[tags[i]]

        # Every sequence needs to end with a transition to the stop_tag.
        if self.include_start_end_transitions:
            alpha = alpha * torch.exp(self.end_transitions.view(1, num_tags))
            z = alpha.sum(dim=1, keepdim=True)
            # alpha = alpha / z  # this step is unnecessary since alpha is not used anymore
            sum_log_z += torch.log(z)

        return sum_log_z.squeeze(1)
Computes the (batch_size,) denominator term for the log-likelihood, which is the
sum of the likelihoods across all possible state sequences.

Compute this value using the scaling trick instead of the log domain trick, since
this is necessary to implement the label-weighting method by Lannoy et al. (2012).
allennlp/modules/conditional_random_field/conditional_random_field_wlannoy.py