Calculate multi-perspective cosine matching between each time step of
    one vector and each time step of another vector.

    Parameters
    ----------
    vector1 : ``torch.Tensor``
        A tensor of shape ``(batch, seq_len1, hidden_size)``
    vector2 : ``torch.Tensor``
        A tensor of shape ``(batch, seq_len2, hidden_size)``
    weight : ``torch.Tensor``
        A tensor of shape ``(num_perspectives, hidden_size)``
    eps : ``float`` optional, (default = 1e-8)
        A small value to avoid zero division problem

    Returns
    -------
    A tensor of shape (batch, seq_len1, seq_len2, num_perspectives) consisting
    multi-perspective matching results

def multi_perspective_match_pairwise(vector1: torch.Tensor,
                                     vector2: torch.Tensor,
                                     weight: torch.Tensor,
                                     eps: float = 1e-8) -> torch.Tensor:
    """
    Calculate multi-perspective cosine matching between each time step of
    one vector and each time step of another vector.

    Parameters
    ----------
    vector1 : ``torch.Tensor``
        A tensor of shape ``(batch, seq_len1, hidden_size)``
    vector2 : ``torch.Tensor``
        A tensor of shape ``(batch, seq_len2, hidden_size)``
    weight : ``torch.Tensor``
        A tensor of shape ``(num_perspectives, hidden_size)``
    eps : ``float`` optional, (default = 1e-8)
        A small value to avoid zero division problem

    Returns
    -------
    A tensor of shape (batch, seq_len1, seq_len2, num_perspectives) consisting
    multi-perspective matching results
    """
    num_perspectives = weight.size(0)

    # (1, num_perspectives, 1, hidden_size)
    weight = weight.unsqueeze(0).unsqueeze(2)

    # (batch, num_perspectives, seq_len*, hidden_size)
    vector1 = weight * vector1.unsqueeze(1).expand(-1, num_perspectives, -1, -1)
    vector2 = weight * vector2.unsqueeze(1).expand(-1, num_perspectives, -1, -1)

    # (batch, num_perspectives, seq_len*, 1)
    vector1_norm = vector1.norm(p=2, dim=3, keepdim=True)
    vector2_norm = vector2.norm(p=2, dim=3, keepdim=True)

    # (batch, num_perspectives, seq_len1, seq_len2)
    mul_result = torch.matmul(vector1, vector2.transpose(2, 3))
    norm_value = vector1_norm * vector2_norm.transpose(2, 3)

    # (batch, seq_len1, seq_len2, num_perspectives)
    return (mul_result / norm_value.clamp(min=eps)).permute(0, 2, 3, 1)