Skip to content

Commit 33bda5e

Browse files
author
ldgauthier
authored
Remove bad "safety check" in GGVCFs (#7772)
All hom-ref sites with unnormalizes PLs threw exceptions because they violated assumptions -- remove the IllegalStateException check on monomorphic sites
1 parent 3b0bc03 commit 33bda5e

8 files changed

Lines changed: 20 additions & 3386 deletions

File tree

src/main/java/org/broadinstitute/hellbender/tools/walkers/genotyper/AlleleSubsettingUtils.java

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -287,18 +287,15 @@ else if ( clazz.equals(int[].class) ) {
287287
*
288288
* @param vc target variant context.
289289
* @param numAltAllelesToKeep number of alt alleles to keep.
290+
* @param ensureReturnContainsAlt make sure the alleles returned include an alternate, even if it's not in the most likely genotype
290291
* @return the list of alleles to keep, including the reference and {@link Allele#NON_REF_ALLELE} if present
291292
*
292293
*/
293294
public static List<Allele> calculateMostLikelyAlleles(final VariantContext vc, final int defaultPloidy,
294-
final int numAltAllelesToKeep) {
295+
final int numAltAllelesToKeep, boolean ensureReturnContainsAlt) {
295296
Utils.nonNull(vc, "vc is null");
296297
Utils.validateArg(defaultPloidy > 0, () -> "default ploidy must be > 0 but defaultPloidy=" + defaultPloidy);
297298
Utils.validateArg(numAltAllelesToKeep > 0, () -> "numAltAllelesToKeep must be > 0, but numAltAllelesToKeep=" + numAltAllelesToKeep);
298-
//allow PLs or GPs (as for GATK-DRAGEN), but we need some kind of genotype data
299-
Utils.validateArg(vc.getGenotypes().stream().anyMatch(g -> g.hasPL() || g.hasExtendedAttribute(VCFConstants.GENOTYPE_POSTERIORS_KEY)), () -> "Most likely alleles cannot be calculated without likelihoods");
300-
//NOTE: this is used in the reblocking case when we have a hom-ref GT and real ALTs
301-
final boolean allHomRefData = vc.getGenotypes().stream().allMatch(g -> g.hasPL() && g.getPL()[0] == 0); //PL=[0,0,0] is okay, we just don't want confident variants
302299

303300
final boolean hasSymbolicNonRef = vc.hasAllele(Allele.NON_REF_ALLELE);
304301
final int numberOfAllelesThatArentProperAlts = hasSymbolicNonRef ? 2 : 1;
@@ -308,11 +305,7 @@ public static List<Allele> calculateMostLikelyAlleles(final VariantContext vc, f
308305
return vc.getAlleles();
309306
}
310307

311-
final double[] likelihoodSums = calculateLikelihoodSums(vc, defaultPloidy, allHomRefData);
312-
if (MathUtils.sum(likelihoodSums) == 0.0 && !allHomRefData) {
313-
throw new IllegalStateException("No likelihood sum exceeded zero -- method was called for variant data " +
314-
"with no variant information.");
315-
}
308+
final double[] likelihoodSums = calculateLikelihoodSums(vc, defaultPloidy, ensureReturnContainsAlt);
316309
return filterToMaxNumberOfAltAllelesBasedOnScores(numAltAllelesToKeep, vc.getAlleles(), likelihoodSums);
317310
}
318311

@@ -342,17 +335,20 @@ public static List<Allele> filterToMaxNumberOfAltAllelesBasedOnScores(int numAlt
342335
*
343336
* Since GLs are log likelihoods, this quantity is thus
344337
* SUM_{samples whose likeliest genotype contains this alt allele} log(likelihood alt / likelihood hom ref)
338+
* @param vc
339+
* @param defaultPloidy
340+
* @param countAllelesWithoutHomRef true if we know the input is hom-ref, but we still want to know the most likely ALT
345341
*/
346342
@VisibleForTesting
347-
static double[] calculateLikelihoodSums(final VariantContext vc, final int defaultPloidy, final boolean allHomRefData) {
343+
static double[] calculateLikelihoodSums(final VariantContext vc, final int defaultPloidy, final boolean countAllelesWithoutHomRef) {
348344
final double[] likelihoodSums = new double[vc.getNAlleles()];
349345
for ( final Genotype genotype : vc.getGenotypes().iterateInSampleNameOrder() ) {
350346
final GenotypeLikelihoods gls = genotype.getLikelihoods();
351347
if (gls == null) {
352348
continue;
353349
}
354350
final double[] glsVector = gls.getAsVector();
355-
final int indexOfMostLikelyVariantGenotype = MathUtils.maxElementIndex(glsVector, allHomRefData ? 1 : 0, glsVector.length);
351+
final int indexOfMostLikelyVariantGenotype = MathUtils.maxElementIndex(glsVector, countAllelesWithoutHomRef ? 1 : 0, glsVector.length);
356352
final double GLDiffBetweenRefAndBestVariantGenotype = Math.abs(glsVector[indexOfMostLikelyVariantGenotype] - glsVector[PL_INDEX_OF_HOM_REF]);
357353
final int ploidy = genotype.getPloidy() > 0 ? genotype.getPloidy() : defaultPloidy;
358354

src/main/java/org/broadinstitute/hellbender/tools/walkers/genotyper/GenotypingEngine.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public VariantContext calculateGenotypes(final VariantContext vc, final Genotype
134134

135135
VariantContext reducedVC = vc;
136136
if (maxAltAlleles < vc.getAlternateAlleles().size()) {
137-
final List<Allele> allelesToKeep = AlleleSubsettingUtils.calculateMostLikelyAlleles(vc, defaultPloidy, maxAltAlleles);
137+
final List<Allele> allelesToKeep = AlleleSubsettingUtils.calculateMostLikelyAlleles(vc, defaultPloidy, maxAltAlleles, false);
138138
final GenotypesContext reducedGenotypes = allelesToKeep.size() == 1 ? GATKVariantContextUtils.subsetToRefOnly(vc, defaultPloidy) :
139139
AlleleSubsettingUtils.subsetAlleles(vc.getGenotypes(), defaultPloidy, vc.getAlleles(), allelesToKeep, gpc,
140140
GenotypeAssignmentMethod.BEST_MATCH_TO_ORIGINAL); //with no PLs in some reblocked GVCFs, no-calls are just going to cause problems, so keep 0/0 genotypes as such without trying to recall

src/main/java/org/broadinstitute/hellbender/tools/walkers/variantutils/ReblockGVCF.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -505,7 +505,8 @@ protected GenotypeBuilder changeCallToHomRefVersusNonRef(final VariantContext lo
505505
if (posteriorsKey != null && genotype.hasExtendedAttribute(posteriorsKey)) {
506506
subsetHomRefPosteriorsToRefVersusNonRef(lowQualVariant, gb);
507507
} else {
508-
final List<Allele> bestAlleles = AlleleSubsettingUtils.calculateMostLikelyAlleles(lowQualVariant, genotype.getPloidy(), 1);
508+
//find best ALT so we can use its likelihood for NON_REF
509+
final List<Allele> bestAlleles = AlleleSubsettingUtils.calculateMostLikelyAlleles(lowQualVariant, genotype.getPloidy(), 1, true);
509510
final Allele bestAlt = bestAlleles.stream().filter(a -> !a.isReference()).findFirst().orElse(Allele.NON_REF_ALLELE); //allow span dels
510511
//we care about the best alt even though it's getting removed because NON_REF should get the best likelihoods
511512
//it shouldn't matter that we're passing in different alt alleles since the GenotypesContext only knows
@@ -928,7 +929,7 @@ private static int[] getGenotypePosteriorsOtherwiseLikelihoods(final Genotype ge
928929
private void subsetHomRefPosteriorsToRefVersusNonRef(final VariantContext result, final GenotypeBuilder gb) {
929930
//TODO: bestAlleles needs to be modified for posteriors
930931
final Genotype genotype = result.getGenotype(0);
931-
final List<Allele> bestAlleles = AlleleSubsettingUtils.calculateMostLikelyAlleles(result, genotype.getPloidy(), 1);
932+
final List<Allele> bestAlleles = AlleleSubsettingUtils.calculateMostLikelyAlleles(result, genotype.getPloidy(), 1, false);
932933
final Allele bestAlt = bestAlleles.stream().filter(a -> !a.isReference()).findFirst().orElse(Allele.NON_REF_ALLELE); //allow span dels
933934
final int[] idxVector = result.getGLIndicesOfAlternateAllele(bestAlt);
934935
final int[] multiallelicPLs = getGenotypePosteriorsOtherwiseLikelihoods(genotype, posteriorsKey);

src/test/java/org/broadinstitute/hellbender/tools/walkers/GenotypeGVCFsIntegrationTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ public Object[][] gvcfsToGenotype() {
159159
b37_reference_20_21},
160160

161161
//23 highly multi-allelic sites across 54 1000G exomes to test allele subsetting and QUAL calculation
162+
//plus one 10-allele WGS variant that's all hom-ref with one GT that has unnormalized PLs from some sort of GenomicsDB corner case
162163
{getTestFile("multiallelicQualRegression.vcf "),
163164
getTestFile("multiallelicQualRegression.expected.vcf"),
164165
NO_EXTRA_ARGS, hg38Reference}

src/test/java/org/broadinstitute/hellbender/tools/walkers/genotyper/AlleleSubsettingUtilsUnitTest.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package org.broadinstitute.hellbender.tools.walkers.genotyper;
22

33
import htsjdk.variant.variantcontext.*;
4-
import htsjdk.variant.vcf.VCFConstants;
54
import org.broadinstitute.hellbender.exceptions.UserException;
65
import org.broadinstitute.hellbender.utils.MathUtils;
76
import org.broadinstitute.hellbender.GATKBaseTest;
@@ -288,7 +287,7 @@ public Object[][] makeUpdatePLsSACsAndADData() {
288287
public void testCalculateMostLikelyAllelesTieDoesntRemoveAllTiedAlleles(){
289288
VariantContext vc = new VariantContextBuilder(null, "1", 100, 100, Arrays.asList(Aref, C, G))
290289
.genotypes(Arrays.asList(new GenotypeBuilder("sample1", Arrays.asList(C,G)).PL( new double[]{5, 5, 5, 5, 0, 5}).make())).make();
291-
Assert.assertEquals(AlleleSubsettingUtils.calculateMostLikelyAlleles(vc, 2, 1), Arrays.asList(Aref,C)) ;
290+
Assert.assertEquals(AlleleSubsettingUtils.calculateMostLikelyAlleles(vc, 2, 1, false), Arrays.asList(Aref,C)) ;
292291
}
293292

294293
@DataProvider
@@ -315,9 +314,9 @@ public void testThatFilteringWorksCorrectly(int numToKeep, List<Allele> alleles,
315314
@Test
316315
public void testCalculateMostLikelyAllelesPreconditions(){
317316
VariantContext vc = new VariantContextBuilder(null, "1", 100, 100, Arrays.asList(Aref, C, G)).make();
318-
Assert.assertThrows(IllegalArgumentException.class, () -> AlleleSubsettingUtils.calculateMostLikelyAlleles(null, 2, 2));
319-
Assert.assertThrows(IllegalArgumentException.class, () -> AlleleSubsettingUtils.calculateMostLikelyAlleles(vc, 0, 2));
320-
Assert.assertThrows(IllegalArgumentException.class, () -> AlleleSubsettingUtils.calculateMostLikelyAlleles(vc, 2, 0));
317+
Assert.assertThrows(IllegalArgumentException.class, () -> AlleleSubsettingUtils.calculateMostLikelyAlleles(null, 2, 2, false));
318+
Assert.assertThrows(IllegalArgumentException.class, () -> AlleleSubsettingUtils.calculateMostLikelyAlleles(vc, 0, 2, false));
319+
Assert.assertThrows(IllegalArgumentException.class, () -> AlleleSubsettingUtils.calculateMostLikelyAlleles(vc, 2, 0, false));
321320
}
322321

323322
@Test

src/test/java/org/broadinstitute/hellbender/tools/walkers/haplotypecaller/HaplotypeCallerIntegrationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1404,7 +1404,7 @@ public void testMaxAlternateAlleles(final String bam, final String reference, fi
14041404
final Map<SimpleInterval, List<Allele>> expectedSubsettedAllelesByLocus = new HashMap<>();
14051405
for ( final VariantContext vc : callsNoMaxAlternateAlleles ) {
14061406
if ( getNumAltAllelesExcludingNonRef(vc) > maxAlternateAlleles ) {
1407-
final List<Allele> mostLikelyAlleles = AlleleSubsettingUtils.calculateMostLikelyAlleles(vc, HomoSapiensConstants.DEFAULT_PLOIDY, maxAlternateAlleles);
1407+
final List<Allele> mostLikelyAlleles = AlleleSubsettingUtils.calculateMostLikelyAlleles(vc, HomoSapiensConstants.DEFAULT_PLOIDY, maxAlternateAlleles, false);
14081408
expectedSubsettedAllelesByLocus.put(new SimpleInterval(vc), mostLikelyAlleles);
14091409
}
14101410
}

0 commit comments

Comments
 (0)