    def __call__(
        self,
        evaluation_embeddings: torch.Tensor,
        bias_direction1: torch.Tensor,
        bias_direction2: torch.Tensor,
    ):
        """

        # Parameters

        evaluation_embeddings : `torch.Tensor`
            A tensor of size (batch_size, ..., dim) of embeddings for which to mitigate bias.
        bias_direction1 : `torch.Tensor`
            A unit tensor of size (dim, ) representing a concept subspace (e.g. gender).
        bias_direction2 : `torch.Tensor`
            A unit tensor of size (dim, ) representing another concept subspace from
            which bias_direction1 should be dissociated (e.g. occupation).

        !!! Note
            All tensors are expected to be on the same device.

        # Returns

        bias_mitigated_embeddings : `torch.Tensor`
            A tensor of the same size as evaluation_embeddings.
        """
        # Some sanity checks
        if evaluation_embeddings.ndim < 2:
            raise ConfigurationError("evaluation_embeddings must have at least two dimensions.")
        if bias_direction1.ndim != 1 or bias_direction2.ndim != 1:
            raise ConfigurationError("bias_direction1 and bias_direction2 must be one-dimensional.")
        if evaluation_embeddings.size(-1) != bias_direction1.size(-1) or evaluation_embeddings.size(
            -1
        ) != bias_direction2.size(-1):
            raise ConfigurationError(
                "All embeddings, bias_direction1, and bias_direction2 must have the same dimensionality."
            )
        if bias_direction1.size(-1) < 2:
            raise ConfigurationError(
                "Dimensionality of all embeddings, bias_direction1, and bias_direction2 must \
                be >= 2."
            )

        with torch.set_grad_enabled(self.requires_grad):
            bias_direction1 = bias_direction1 / torch.linalg.norm(bias_direction1)
            bias_direction2 = bias_direction2 / torch.linalg.norm(bias_direction2)

            bias_direction2_orth = self._remove_component(
                bias_direction2.reshape(1, -1), bias_direction1
            )[0]
            bias_direction2_orth = bias_direction2_orth / torch.linalg.norm(bias_direction2_orth)

            # Create rotation matrix as orthonormal basis
            # with v1 and v2'
            init_orth_matrix = torch.eye(
                bias_direction1.size(0),
                device=evaluation_embeddings.device,
                requires_grad=self.requires_grad,
            )
            rotation_matrix = torch.zeros(
                (bias_direction1.size(0), bias_direction1.size(0)),
                device=evaluation_embeddings.device,
                requires_grad=self.requires_grad,
            )
            rotation_matrix = torch.cat(
                [
                    bias_direction1.reshape(1, -1),
                    bias_direction2_orth.reshape(1, -1),
                    rotation_matrix[2:],
                ]
            )
            # Apply Gram-Schmidt
            for i in range(len(rotation_matrix) - 2):
                subspace_proj = torch.sum(
                    self._proj(
                        rotation_matrix[: i + 2].clone(), init_orth_matrix[i], normalize=True
                    ),
                    dim=0,
                )
                rotation_matrix[i + 2] = (init_orth_matrix[i] - subspace_proj) / torch.linalg.norm(
                    init_orth_matrix[i] - subspace_proj
                )

            mask = ~(evaluation_embeddings == 0).all(dim=-1)
            # Transform all evaluation embeddings
            # using orthonormal basis computed above
            rotated_evaluation_embeddings = torch.matmul(
                evaluation_embeddings[mask], rotation_matrix.t()
            )
            # Want to adjust first 2 coordinates and leave d - 2
            # other orthogonal components fixed
            fixed_rotated_evaluation_embeddings = rotated_evaluation_embeddings[..., 2:]
            # Restrict attention to subspace S spanned by bias directions
            # which we hope to make orthogonal
            restricted_rotated_evaluation_embeddings = torch.cat(
                [
                    torch.matmul(rotated_evaluation_embeddings, bias_direction1.reshape(-1, 1)),
                    torch.matmul(
                        rotated_evaluation_embeddings, bias_direction2_orth.reshape(-1, 1)
                    ),
                ],
                dim=-1,
            )

            # Transform and restrict bias directions
            restricted_bias_direction1 = torch.tensor(
                [1.0, 0.0], device=evaluation_embeddings.device, requires_grad=self.requires_grad
            )
            bias_direction_inner_prod = torch.dot(bias_direction1, bias_direction2)
            restricted_bias_direction2 = torch.tensor(
                [
                    bias_direction_inner_prod,
                    torch.sqrt(1 - torch.square(bias_direction_inner_prod)),
                ],
                device=evaluation_embeddings.device,
                requires_grad=self.requires_grad,
            )
            restricted_bias_direction2_orth = torch.tensor(
                [0.0, 1.0], device=evaluation_embeddings.device, requires_grad=self.requires_grad
            )

            restricted_bias_direction_inner_prod = torch.dot(
                restricted_bias_direction1, restricted_bias_direction2
            )
            theta = torch.abs(torch.arccos(restricted_bias_direction_inner_prod))
            theta_proj = np.pi / 2 - theta
            phi = torch.arccos(
                torch.matmul(
                    restricted_rotated_evaluation_embeddings
                    / torch.linalg.norm(
                        restricted_rotated_evaluation_embeddings, dim=-1, keepdim=True
                    ),
                    restricted_bias_direction1,
                )
            )
            d = torch.matmul(
                restricted_rotated_evaluation_embeddings
                / torch.linalg.norm(restricted_rotated_evaluation_embeddings, dim=-1, keepdim=True),
                restricted_bias_direction2_orth,
            )

            # Add noise to avoid DivideByZero
            theta_x = torch.zeros_like(phi, requires_grad=self.requires_grad)
            theta_x = torch.where(
                (d > 0) & (phi < theta_proj),
                theta * (phi / (theta_proj + 1e-10)),
                theta_x,
            )
            theta_x = torch.where(
                (d > 0) & (phi > theta_proj),
                theta * ((np.pi - phi) / (np.pi - theta_proj + 1e-10)),
                theta_x,
            )
            theta_x = torch.where(
                (d < 0) & (phi >= np.pi - theta_proj),
                theta * ((np.pi - phi) / (theta_proj + 1e-10)),
                theta_x,
            )
            theta_x = torch.where(
                (d < 0) & (phi < np.pi - theta_proj),
                theta * (phi / (np.pi - theta_proj + 1e-10)),
                theta_x,
            )

            f_matrix = torch.cat(
                [
                    torch.cos(theta_x).unsqueeze(-1),
                    -torch.sin(theta_x).unsqueeze(-1),
                    torch.sin(theta_x).unsqueeze(-1),
                    torch.cos(theta_x).unsqueeze(-1),
                ],
                dim=-1,
            )
            f_matrix = f_matrix.reshape(f_matrix.size()[:-1] + (2, 2))

            evaluation_embeddings_clone = evaluation_embeddings.clone()
            evaluation_embeddings_clone[mask] = torch.cat(
                [
                    torch.bmm(
                        f_matrix,
                        restricted_rotated_evaluation_embeddings.unsqueeze(-1),
                    ).squeeze(-1),
                    fixed_rotated_evaluation_embeddings,
                ],
                dim=-1,
            )
            return torch.matmul(evaluation_embeddings_clone, rotation_matrix)
# Parameters

evaluation_embeddings : `torch.Tensor`
A tensor of size (batch_size, ..., dim) of embeddings for which to mitigate bias.
bias_direction1 : `torch.Tensor`
A unit tensor of size (dim, ) representing a concept subspace (e.g. gender).
bias_direction2 : `torch.Tensor`
A unit tensor of size (dim, ) representing another concept subspace from
which bias_direction1 should be dissociated (e.g. occupation).

!!! Note
All tensors are expected to be on the same device.

# Returns

bias_mitigated_embeddings : `torch.Tensor`
A tensor of the same size as evaluation_embeddings.
allennlp/fairness/bias_mitigators.py