class_docstr: A component that locates and extract answers to a given query from Documents. It's used for performing extractive
  QA. The Reader assigns a score to every possible answer span independently of other answer spans.
  This fixes a common issue of other implementations which make comparisons across documents harder by normalizing
  each document's answers independently.
  
  Example usage:
  ```python
  p = Pipeline()
  p.add_component(instance=InMemoryBM25Retriever(document_store=InMemoryDocumentStore()), name="retriever")
  p.add_component(instance=ExtractiveReader(), name="reader")
  p.connect("retriever", "reader")
  question = "Who lives in Berlin?"
  p.run({"retriever": {"query": question}, "reader": {"query": question}})
  ```
docstr: Turn start and end logits into probabilities for each answer span. Unlike most other
  implementations, it doesn't normalize the scores in each split to make them easier to compare across different
  splits. Returns the top k answer spans.
docstr_tok: ["Turn","start","and","end","logits","into","probabilities","for","each","answer","span",".","Unlike","most","other","implementations",",","it","does","n't","normalize","the","scores","in","each","split","to","make","them","easier","to","compare","across","different","splits",".","Returns","the","top","k","answer","spans","."]
code: def _postprocess(
      self,
      start: "torch.Tensor",
      end: "torch.Tensor",
      sequence_ids: "torch.Tensor",
      attention_mask: "torch.Tensor",
      answers_per_seq: int,
      encodings: List["Encoding"],
  ) -> Tuple[List[List[int]], List[List[int]], "torch.Tensor"]:
      """
      Turn start and end logits into probabilities for each answer span. Unlike most other
      implementations, it doesn't normalize the scores in each split to make them easier to compare across different
      splits. Returns the top k answer spans.
      """
      mask = (
          sequence_ids == 1
      )  # Only keep tokens from the context (should ignore special tokens)
      mask = torch.logical_and(
          mask, attention_mask == 1
      )  # Definitely remove special tokens
      start = torch.where(
          mask, start, -torch.inf
      )  # Apply the mask on the start logits
      end = torch.where(
          mask, end, -torch.inf
      )  # Apply the mask on the end logits
      start = start.unsqueeze(-1)
      end = end.unsqueeze(-2)
  
      logits = (
          start + end
      )  # shape: (batch_size, seq_length (start), seq_length (end))
  
      # The mask here onwards is the same for all instances in the batch
      # As such we do away with the batch dimension
      mask = torch.ones(
          logits.shape[-2:], dtype=torch.bool, device=self.device
      )
      mask = torch.triu(mask)  # End shouldn't be before start
      masked_logits = torch.where(mask, logits, -torch.inf)
      probabilities = torch.sigmoid(
          masked_logits * self.calibration_factor
      )
  
      flat_probabilities = probabilities.flatten(
          -2, -1
      )  # necessary for topk
  
      # topk can return invalid candidates as well if answers_per_seq > num_valid_candidates
      # We only keep probability > 0 candidates later on
      candidates = torch.topk(flat_probabilities, answers_per_seq)
      seq_length = logits.shape[-1]
      start_candidates = (
          candidates.indices // seq_length
      )  # Recover indices from flattening
      end_candidates = candidates.indices % seq_length
      candidates_values = candidates.values.cpu()
      start_candidates = start_candidates.cpu()
      end_candidates = end_candidates.cpu()
  
      start_candidates_tokens_to_chars = []
      end_candidates_tokens_to_chars = []
      for i, (s_candidates, e_candidates, encoding) in enumerate(
          zip(start_candidates, end_candidates, encodings)
      ):
          # Those with probabilities > 0 are valid
          valid = candidates_values[i] > 0
          s_char_spans = []
          e_char_spans = []
          for start_token, end_token in zip(
              s_candidates[valid], e_candidates[valid]
          ):
              # token_to_chars returns `None` for special tokens
              # But we shouldn't have special tokens in the answers at this point
              # The whole span is given by the start of the start_token (index 0)
              # and the end of the end token (index 1)
              s_char_spans.append(
                  encoding.token_to_chars(start_token)[0]
              )
              e_char_spans.append(encoding.token_to_chars(end_token)[1])
          start_candidates_tokens_to_chars.append(s_char_spans)
          end_candidates_tokens_to_chars.append(e_char_spans)
  
      return (
          start_candidates_tokens_to_chars,
          end_candidates_tokens_to_chars,
          candidates_values,
      )
  
imports: from pathlib import Path
  from typing import Any, Dict, List, Optional, Tuple, Union
  import math
  import warnings
  import logging
  from haystack import component, default_to_dict, default_from_dict, ComponentError, Document, ExtractedAnswer
  from haystack.lazy_imports import LazyImport
  from haystack.utils import get_device
label_desc: Use image node_id 8 for calling the ExtractiveReader obj's underlying member method code with example usage: obj._postprocess(start, end, sequence_ids, attention_mask, answers_per_seq, encodings) and returns: start_candidates_tokens_to_chars,  end_candidates_tokens_to_chars,  candidates_values