<commit_before>/*
 * The Exomiser - A tool to annotate and prioritize variants
 *
 * Copyright (C) 2012 - 2015  Charite Universitätsmedizin Berlin and Genome Research Ltd.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.monarchinitiative.exomiser.core.analysis.util;

import com.google.common.collect.*;
import de.charite.compbio.jannovar.mendel.ModeOfInheritance;
import de.charite.compbio.jannovar.mendel.SubModeOfInheritance;
import de.charite.compbio.jannovar.mendel.bridge.CannotAnnotateMendelianInheritance;
import de.charite.compbio.jannovar.mendel.bridge.VariantContextMendelianAnnotator;
import de.charite.compbio.jannovar.pedigree.Genotype;
import de.charite.compbio.jannovar.pedigree.Pedigree;
import htsjdk.variant.variantcontext.Allele;
import htsjdk.variant.variantcontext.VariantContext;
import org.monarchinitiative.exomiser.core.model.Gene;
import org.monarchinitiative.exomiser.core.model.VariantEvaluation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

import static java.util.stream.Collectors.toList;

/**
 * This class allows us to do segregation analysis for the variants supplied to
 * it i.e., to determine if they are compatible with autosomal recessive,
 * autosomal dominant, or X-linked recessive inheritance.
 *
 * @author <NAME> <<EMAIL>>
 */
public class InheritanceModeAnalyser {

    private static final Logger logger = LoggerFactory.getLogger(InheritanceModeAnalyser.class);

    private final ModeOfInheritance modeOfInheritance;
    private final VariantContextMendelianAnnotator inheritanceAnnotator;
    private final Set<ModeOfInheritance> compatibleModes;

    public InheritanceModeAnalyser(ModeOfInheritance modeOfInheritance, Pedigree pedigree) {
        this.modeOfInheritance = modeOfInheritance;
        this.inheritanceAnnotator = new VariantContextMendelianAnnotator(pedigree, false, false);
        this.compatibleModes = Sets.immutableEnumSet(modeOfInheritance);
    }

    /**
     * Analyses the compatibility of a list of {@link Gene} with the {@link ModeOfInheritance} used in the constructor
     * of this class according to the observed pattern of inheritance in the {@link Pedigree}. This will only be applied
     * to genes and the variants in the gene which have *PASSED* filtering.
     */
    public void analyseInheritanceModes(Collection<Gene> genes) {
        genes.stream().filter(Gene::passedFilters).forEach(this::analyseInheritanceModes);
    }

    /**
     * Finds pairs of alleles compatible with autosomal recessive compound heterozygous inheritance according to the
     * pedigree supplied in the class constructor. This will work independently of the mode of inheritance specified in
     * the class constructor.
     *
     * @param passedVariantEvaluations
     * @return a list of allele pairs compatible with an autosomal recessive compound heterozygous inheritance pattern.
     */
    public List<List<VariantEvaluation>> findCompatibleCompHetAlleles(List<VariantEvaluation> passedVariantEvaluations) {
        //Cant't be comp het if there's only one allele.
        if (passedVariantEvaluations.size() <= 1) {
            return Collections.emptyList();
        }

        List<List<VariantEvaluation>> compatibleAllelePairs = new ArrayList<>();
        //don't do all vs all otherwise we'll get the reciprocal pairs being tested so only check one side of the diagonal
        for (int i = 0; i < passedVariantEvaluations.size(); i++) {
            for (int j = i + 1; j < passedVariantEvaluations.size(); j++) {
                VariantEvaluation ve1 = passedVariantEvaluations.get(i);
                VariantEvaluation ve2 = passedVariantEvaluations.get(j);
                if (!ve1.equals(ve2) && isCompHetCompatible(ve1, ve2)) {
                    compatibleAllelePairs.add(ImmutableList.of(ve1, ve2));
                }
            }
        }
        return ImmutableList.copyOf(compatibleAllelePairs);
    }

    private boolean isCompHetCompatible(VariantEvaluation ve1, VariantEvaluation ve2) {
        List<VariantContext> pair = Arrays.asList(ve1.getVariantContext(), ve2.getVariantContext());
        try {
            ImmutableMap<SubModeOfInheritance, ImmutableList<VariantContext>> compatibleSubModesMap = inheritanceAnnotator
                    .computeCompatibleInheritanceSubModes(pair);
            if (compatibleSubModesMap.containsKey(SubModeOfInheritance.AUTOSOMAL_RECESSIVE_COMP_HET)) {
                ImmutableList<VariantContext> compHetPair = compatibleSubModesMap.get(SubModeOfInheritance.AUTOSOMAL_RECESSIVE_COMP_HET);
                if (compHetPair.size() == 2) {
                    return true;
                }
            }
        } catch (CannotAnnotateMendelianInheritance ex) {
            logger.error(null, ex);
        }
        return false;
    }

    /**
     * Analyses the compatibility of a {@link Gene} with the {@link ModeOfInheritance} used in the constructor
     * of this class according to the observed pattern of inheritance in the {@link Pedigree}. This will only be applied
     * to the variants in the gene which have *PASSED* filtering.
     */
    public boolean analyseInheritanceModes(Gene gene) {
        if (gene.passedFilters()) {
            checkInheritanceCompatibilityOfPassedVariants(gene);
        }
        return gene.isCompatibleWith(modeOfInheritance);
    }

    private void checkInheritanceCompatibilityOfPassedVariants(Gene gene) {
        if (modeOfInheritance == ModeOfInheritance.ANY) {
            return;
        }
        //it is *CRITICAL* that only the PASSED variantEvaluations are taken into account here.
        List<VariantEvaluation> passedVariantEvaluations = gene.getPassedVariantEvaluations();

        Multimap<String, VariantEvaluation> geneVariants = mapVariantEvaluationsToVariantContextString(passedVariantEvaluations);
        List<VariantContext> compatibleVariants = getCompatibleVariantContexts(passedVariantEvaluations);

        if (!compatibleVariants.isEmpty()) {
            logger.debug("Gene {} has {} variants compatible with {}:", gene.getGeneSymbol(), compatibleVariants.size(), modeOfInheritance);
            gene.setInheritanceModes(compatibleModes);
            setVariantEvaluationInheritanceModes(geneVariants, compatibleVariants);
        }
    }

    private Multimap<String, VariantEvaluation> mapVariantEvaluationsToVariantContextString(List<VariantEvaluation> passedVariantEvaluations) {
        Multimap<String, VariantEvaluation> geneVariants = ArrayListMultimap.create();
        for (VariantEvaluation variantEvaluation : passedVariantEvaluations) {
            geneVariants.put(toKeyValue(variantEvaluation.getVariantContext()), variantEvaluation);
        }
        return geneVariants;
    }
<commit_msg>added some logging for genes with more than 1000 passed variants as it takes a long time for the inheritance checking to happen<commit_after>/*
 * The Exomiser - A tool to annotate and prioritize variants
 *
 * Copyright (C) 2012 - 2015  Charite Universitätsmedizin Berlin and Genome Research Ltd.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.monarchinitiative.exomiser.core.analysis.util;

import com.google.common.collect.*;
import de.charite.compbio.jannovar.mendel.ModeOfInheritance;
import de.charite.compbio.jannovar.mendel.SubModeOfInheritance;
import de.charite.compbio.jannovar.mendel.bridge.CannotAnnotateMendelianInheritance;
import de.charite.compbio.jannovar.mendel.bridge.VariantContextMendelianAnnotator;
import de.charite.compbio.jannovar.pedigree.Genotype;
import de.charite.compbio.jannovar.pedigree.Pedigree;
import htsjdk.variant.variantcontext.Allele;
import htsjdk.variant.variantcontext.VariantContext;
import org.monarchinitiative.exomiser.core.model.Gene;
import org.monarchinitiative.exomiser.core.model.VariantEvaluation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

import static java.util.stream.Collectors.toList;

/**
 * This class allows us to do segregation analysis for the variants supplied to
 * it i.e., to determine if they are compatible with autosomal recessive,
 * autosomal dominant, or X-linked recessive inheritance.
 *
 * @author <NAME> <<EMAIL>>
 */
public class InheritanceModeAnalyser {

    private static final Logger logger = LoggerFactory.getLogger(InheritanceModeAnalyser.class);

    private final ModeOfInheritance modeOfInheritance;
    private final VariantContextMendelianAnnotator inheritanceAnnotator;
    private final Set<ModeOfInheritance> compatibleModes;

    public InheritanceModeAnalyser(ModeOfInheritance modeOfInheritance, Pedigree pedigree) {
        this.modeOfInheritance = modeOfInheritance;
        this.inheritanceAnnotator = new VariantContextMendelianAnnotator(pedigree, false, false);
        this.compatibleModes = Sets.immutableEnumSet(modeOfInheritance);
    }

    /**
     * Analyses the compatibility of a list of {@link Gene} with the {@link ModeOfInheritance} used in the constructor
     * of this class according to the observed pattern of inheritance in the {@link Pedigree}. This will only be applied
     * to genes and the variants in the gene which have *PASSED* filtering.
     */
    public void analyseInheritanceModes(Collection<Gene> genes) {
        genes.stream().filter(Gene::passedFilters).forEach(this::analyseInheritanceModes);
    }

    /**
     * Finds pairs of alleles compatible with autosomal recessive compound heterozygous inheritance according to the
     * pedigree supplied in the class constructor. This will work independently of the mode of inheritance specified in
     * the class constructor.
     *
     * @param passedVariantEvaluations
     * @return a list of allele pairs compatible with an autosomal recessive compound heterozygous inheritance pattern.
     */
    public List<List<VariantEvaluation>> findCompatibleCompHetAlleles(List<VariantEvaluation> passedVariantEvaluations) {
        //Cant't be comp het if there's only one allele.
        if (passedVariantEvaluations.size() <= 1) {
            return Collections.emptyList();
        }

        List<List<VariantEvaluation>> compatibleAllelePairs = new ArrayList<>();
        //don't do all vs all otherwise we'll get the reciprocal pairs being tested so only check one side of the diagonal
        for (int i = 0; i < passedVariantEvaluations.size(); i++) {
            for (int j = i + 1; j < passedVariantEvaluations.size(); j++) {
                VariantEvaluation ve1 = passedVariantEvaluations.get(i);
                VariantEvaluation ve2 = passedVariantEvaluations.get(j);
                if (!ve1.equals(ve2) && isCompHetCompatible(ve1, ve2)) {
                    compatibleAllelePairs.add(ImmutableList.of(ve1, ve2));
                }
            }
        }
        return ImmutableList.copyOf(compatibleAllelePairs);
    }

    private boolean isCompHetCompatible(VariantEvaluation ve1, VariantEvaluation ve2) {
        List<VariantContext> pair = Arrays.asList(ve1.getVariantContext(), ve2.getVariantContext());
        try {
            ImmutableMap<SubModeOfInheritance, ImmutableList<VariantContext>> compatibleSubModesMap = inheritanceAnnotator
                    .computeCompatibleInheritanceSubModes(pair);
            if (compatibleSubModesMap.containsKey(SubModeOfInheritance.AUTOSOMAL_RECESSIVE_COMP_HET)) {
                ImmutableList<VariantContext> compHetPair = compatibleSubModesMap.get(SubModeOfInheritance.AUTOSOMAL_RECESSIVE_COMP_HET);
                if (compHetPair.size() == 2) {
                    return true;
                }
            }
        } catch (CannotAnnotateMendelianInheritance ex) {
            logger.error(null, ex);
        }
        return false;
    }

    /**
     * Analyses the compatibility of a {@link Gene} with the {@link ModeOfInheritance} used in the constructor
     * of this class according to the observed pattern of inheritance in the {@link Pedigree}. This will only be applied
     * to the variants in the gene which have *PASSED* filtering.
     */
    public boolean analyseInheritanceModes(Gene gene) {
        if (gene.passedFilters()) {
            checkInheritanceCompatibilityOfPassedVariants(gene);
        }
        return gene.isCompatibleWith(modeOfInheritance);
    }

    private void checkInheritanceCompatibilityOfPassedVariants(Gene gene) {
        if (modeOfInheritance == ModeOfInheritance.ANY) {
            return;
        }
        //it is *CRITICAL* that only the PASSED variantEvaluations are taken into account here.
        List<VariantEvaluation> passedVariantEvaluations = gene.getPassedVariantEvaluations();
        if (passedVariantEvaluations.size() > 1000)
            logger.info("Warning - this will take a while. Testing gene " + gene.getGeneSymbol() + " with " + passedVariantEvaluations.size() + " passed variants for inheritance mode compatibility!");

        Multimap<String, VariantEvaluation> geneVariants = mapVariantEvaluationsToVariantContextString(passedVariantEvaluations);
        List<VariantContext> compatibleVariants = getCompatibleVariantContexts(passedVariantEvaluations);

        if (!compatibleVariants.isEmpty()) {
            logger.debug("Gene {} has {} variants compatible with {}:", gene.getGeneSymbol(), compatibleVariants.size(), modeOfInheritance);
            gene.setInheritanceModes(compatibleModes);
            setVariantEvaluationInheritanceModes(geneVariants, compatibleVariants);
        }
    }

    private Multimap<String, VariantEvaluation> mapVariantEvaluationsToVariantContextString(List<VariantEvaluation> passedVariantEvaluations) {
        Multimap<String, VariantEvaluation> geneVariants = ArrayListMultimap.create();
        for (VariantEvaluation variantEvaluation : passedVariantEvaluations) {
            geneVariants.put(toKeyValue(variantEvaluation.getVariantContext()), variantEvaluation);
        }
        return geneVariants;
    }

{'fraction_non_alphanumeric': 0.07641879298155409, 'fraction_numerical': 0.003984831930072627, 'mean_word_length': 3.618581181359454, 'pattern_counts': {'":': 0, '<': 53, '<?xml version=': 0, '>': 48, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2659726', 'n_tokens_mistral': 4465, 'n_tokens_neox': 4123, 'n_words': 1188}