docs: def gradient_pred(model, ref, ref_rc, alt, alt_rc, mutation_positions, out_annotation_all_outputs,
                    output_filter_mask=None, out_annotation=None):
      """Gradient-based (saliency) variant effect prediction
  
      Based on the idea of [saliency maps](https://arxiv.org/pdf/1312.6034.pdf) the gradient-based prediction of
      variant effects uses the `gradient` function of the Keras backend to estimate the importance of a variant
      for a given output. This value is then multiplied by the input, as recommended by
      [Shrikumar et al., 2017](https://arxiv.org/pdf/1605.01713.pdf).
  
      # Arguments
          model: Keras model
          ref: Input sequence with the reference genotype in the mutation position
          ref_rc: Reverse complement of the 'ref' argument
          alt: Input sequence with the alternative genotype in the mutation position
          alt_rc: Reverse complement of the 'alt' argument
          mutation_positions: Position on which the mutation was placed in the forward sequences
          out_annotation_all_outputs: Output labels of the model.
          output_filter_mask: Mask of boolean values indicating which model outputs should be used.
              Use this or 'out_annotation'
          out_annotation: List of outputs labels for which of the outputs (in case of a multi-task model) the
              predictions should be calculated.
  
      # Returns
  
      Dictionary with three different entries:
  
      - ref: Gradient * input at the mutation position using the reference sequence.
          Forward or reverse-complement sequence is chose based on sequence direction caused
          the bigger absolute difference ('diff')
      - alt: Gradient * input at the mutation position using the alternative sequence. Forward or
          reverse-complement sequence is chose based on sequence direction caused the bigger
          absolute difference ('diff')
      - diff: 'alt' - 'ref'. Forward or reverse-complement sequence is chose based on sequence
          direction caused the bigger absolute difference.
      """
      seqs = {"ref": ref, "ref_rc": ref_rc, "alt": alt, "alt_rc": alt_rc}
  
      for k in seqs:
          if not isinstance(seqs[k], (list, tuple, np.ndarray)):
              raise Exception("At the moment only models with list, tuple or np.ndarray inputs are supported.")
  
      assert np.all([np.array(get_seq_len(ref)) == np.array(get_seq_len(seqs[k])) for k in seqs.keys() if k != "ref"])
      assert get_seq_len(ref)[0] == mutation_positions.shape[0]
      assert len(mutation_positions.shape) == 1
  
      # determine which outputs should be selected
      if output_filter_mask is None:
          if out_annotation is None:
              output_filter_mask = np.arange(out_annotation_all_outputs.shape[0])
          else:
              output_filter_mask = np.where(np.in1d(out_annotation_all_outputs, out_annotation))[0]
  
      # make sure the labels are assigned correctly
      out_annotation = out_annotation_all_outputs[output_filter_mask]
  
      # Generate the necessary gradient functions
      sal_funcs = __generate_direct_saliency_functions__(model, out_annotation_all_outputs, out_annotation)
  
      # ANALOGOUS TO ISM:
      # predict
      preds = {}
  
      for k in seqs:
          preds[k] = {}
          if "_rc" in k:
              mutated_positions_here = get_seq_len(ref)[1] - 1 - mutation_positions
          else:
              mutated_positions_here = mutation_positions
          for l in out_annotation:
              preds[k][l] = predict_vals(input_data=seqs[k], apply_function=__get_direct_saliencies__,
                                         score_func=sal_funcs[l], mutated_positions=mutated_positions_here, model = model)
  
      diff_ret_dGrad = {}
      pred_out = {"ref": {}, "alt": {}}
      for k in preds["ref"]:
          # TODO make list (and dict)-ready
          diff_fwd = general_diff(preds["alt"][k]["dGrad"],  preds["ref"][k]["dGrad"])
          diff_rc = general_diff(preds["alt_rc"][k]["dGrad"],  preds["ref_rc"][k]["dGrad"])
          sel = general_sel(diff_fwd, diff_rc)
          replace_by_sel(diff_fwd, diff_rc, sel)
          diff_ret_dGrad[k] = diff_fwd
          # Overwrite the fwd values with rc values if rc was selected
          replace_by_sel(preds["ref"][k]["dGrad"], preds["ref_rc"][k]["dGrad"], sel)
          replace_by_sel(preds["alt"][k]["dGrad"], preds["alt_rc"][k]["dGrad"], sel)
          pred_out["ref"][k] = preds["ref"][k]["dGrad"]
          pred_out["alt"][k] = preds["alt"][k]["dGrad"]
  
      return {"diff": pd.DataFrame(diff_ret_dGrad),
              "ref": pd.DataFrame(pred_out["ref"]),
              "alt": pd.DataFrame(pred_out["alt"])}