def METHOD_NAME(
    self,
    estimate: torch.Tensor,
    reference: torch.Tensor,
    mask: Optional[torch.Tensor] = None,
    epsilon: float = 1e-8,
) -> torch.Tensor:
    """Compute utterance-level PIT Loss
    Args:
        estimate (torch.Tensor): Estimated source signals.
            Shape: [bacth, speakers, time frame]
        reference (torch.Tensor): Reference (original) source signals.
            Shape: [batch, speakers, time frame]
        mask (torch.Tensor or None, optional): Binary mask to indicate padded value (0) or valid value (1).
            Shape: [batch, 1, time frame]
        epsilon (float, optional): constant value used to stabilize division.
    Returns:
        torch.Tensor: Maximum criterion over the speaker permutation.
            Shape: [batch, ]
    """
    assert estimate.shape == reference.shape
    batch_size, num_speakers = reference.shape[:2]
    num_permute = math.factorial(num_speakers)
    util_mat = torch.zeros(batch_size, num_permute, dtype=estimate.dtype, device=estimate.device)
    for i, idx in enumerate(permutations(range(num_speakers))):
        util = self.utility_func(estimate, reference[:, idx, :], mask=mask, epsilon=epsilon)
        util_mat[:, i] = util.mean(dim=1)  # take the average over speaker dimension
    return util_mat.max(dim=1).values