class_docstr: OSCaR bias mitigator. Mitigates bias in embeddings by dissociating concept subspaces
  through subspace orthogonalization. Formally, OSCaR applies a graded rotation
  on the embedding space to rectify two ideally-independent concept subspaces
  so that they become orthogonal.
  
  !!! Note
      For a detailed walkthrough and visual descriptions of the steps, please
      refer to Figure 6 in [VERB: Visualizing and Interpreting Bias Mitigation Techniques
      for Word Representations](https://api.semanticscholar.org/CorpusID:233168618).
  
  Based on: Dev, S., Li, T., Phillips, J.M., & Srikumar, V. (2020). [OSCaR: Orthogonal Subspace
  Correction and Rectification of Biases in Word Embeddings](https://api.semanticscholar.org/CorpusID:220281039).
  ArXiv, abs/2007.00049.
  
  Implementation and terminology based on Rathore, A., Dev, S., Phillips, J.M., Srikumar,
  V., Zheng, Y., Yeh, C.M., Wang, J., Zhang, W., & Wang, B. (2021).
  [VERB: Visualizing and Interpreting Bias Mitigation Techniques for
  Word Representations](https://api.semanticscholar.org/CorpusID:233168618).
  ArXiv, abs/2104.02797.
docstr: # 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.
docstr_tok: ["#","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","."]
code: 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
          )
  
imports: import torch
  import numpy
  import scipy
  import sklearn
  from allennlp.common.checks import ConfigurationError
label_desc: Use image node_id 1 for calling the OSCaRBiasMitigator obj's underlying member method code with example usage: obj.__call__(evaluation_embeddings, bias_direction1, bias_direction2) and returns: torch