def forward(
        self, embeddings_left: torch.Tensor, embeddings_right: torch.Tensor
    ) -> torch.Tensor:
        """Forward propagation method for the contrastive loss.

        Args:
            embeddings_left: left objects embeddings [batch_size, features_dim]
            embeddings_right: right objects embeddings [batch_size, features_dim]

        Raises:
            ValueError: if the batch size is 1
            ValueError: if embeddings_left and embeddings_right shapes are different
            ValueError: if embeddings shapes are not in a form (batch_size, features_dim)

        Returns:
            torch.Tensor: loss
        """
        shape_left, shape_right = embeddings_left.shape, embeddings_right.shape
        if len(shape_left) != 2:
            raise ValueError(
                "Left shape should be (batch_size, feature_dim),"
                f"but got - {shape_left}!"
            )
        elif len(shape_right) != 2:
            raise ValueError(
                "Right shape should be (batch_size, feature_dim),"
                f"but got - {shape_right}!"
            )
        if shape_left[0] == 1:
            raise ValueError(f"Batch size should be >= 2, but got - {shape_left[0]}!")
        if shape_left != shape_right:
            raise ValueError(
                f"Shapes should be equall, but got - {shape_left} and {shape_right}!"
            )
        # normalization
        z_left = (embeddings_left - embeddings_left.mean(dim=0)) / (
            embeddings_left.var(dim=0) + self.eps
        ).pow(1 / 2)
        z_right = (embeddings_right - embeddings_right.mean(dim=0)) / (
            embeddings_right.var(dim=0) + self.eps
        ).pow(1 / 2)

        # cross-correlation matrix
        batch_size = z_left.shape[0]
        cross_correlation = torch.matmul(z_left.T, z_right) / batch_size

        # selection of diagonal elements and off diagonal elements
        on_diag = torch.diagonal(cross_correlation)
        off_diag = cross_correlation.clone().fill_diagonal_(0)

        # the loss described in the original Barlow Twin's paper
        # encouraging off_diag to be zero and on_diag to be one
        on_diag_loss = on_diag.add_(-1).pow_(2).sum()
        off_diag_loss = off_diag.pow_(2).sum()
        loss = on_diag_loss + self.offdiag_lambda * off_diag_loss
        return loss
Forward propagation method for the contrastive loss.

        Args:
            embeddings_left: left objects embeddings [batch_size, features_dim]
            embeddings_right: right objects embeddings [batch_size, features_dim]

        Raises:
            ValueError: if the batch size is 1
            ValueError: if embeddings_left and embeddings_right shapes are different
            ValueError: if embeddings shapes are not in a form (batch_size, features_dim)

        Returns:
            torch.Tensor: loss
        
catalyst/contrib/losses/contrastive.py