From 38740b0ff5e70df6c5d760251d129ed22d0eeccb Mon Sep 17 00:00:00 2001 From: "Mark A. DePristo" Date: Mon, 4 Jul 2011 16:11:42 -0400 Subject: [PATCH 03/59] First working version of the DiffNode readers for VCF and BAM files. Unit tests confirm the readers are approximately working. Skeleton of a working DiffObjects walker that will be able to provide detailed information about how exactly two files of the same type differ, so long as the files are supported by the DiffNode structure. --- public/testdata/diffTestMaster.vcf | 11 +++++++++++ public/testdata/diffTestTest.vcf | 11 +++++++++++ 2 files changed, 22 insertions(+) create mode 100644 public/testdata/diffTestMaster.vcf create mode 100644 public/testdata/diffTestTest.vcf diff --git a/public/testdata/diffTestMaster.vcf b/public/testdata/diffTestMaster.vcf new file mode 100644 index 000000000..549f54345 --- /dev/null +++ b/public/testdata/diffTestMaster.vcf @@ -0,0 +1,11 @@ +##fileformat=VCFv4.0 +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT NA12878 +chr1 2646 rs62635284 G A 0.15 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:53,75:3:-12.40,-0.90,-0.00:9.03 +chr1 2979 rs62635286 T G 83.67 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:31,32:9:-33.61,-2.71,-0.00:27.09 +chr1 2981 rs62028691 A G 14.69 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:31,33:9:-32.12,-2.71,-0.00:27.08 +chr1 4536 rs11582131 G C 0.18 PASS AC=1;AF=0.50;AN=2 GT:AD:DP:GL:GQ 0/1:42,33:16:-41.67,-4.82,-26.29:99 +chr1 4562 rs11490464 C G 0.14 PASS AC=1;AF=0.50;AN=2 GT:AD:DP:GL:GQ 0/1:26,30:9:-19.64,-2.72,-14.87:99 +chr1 4770 rs6682375 A G 0.32 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:9,111:84:-306.27,-28.58,-3.46:99 +chr1 4793 rs6682385 A G 0.15 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:4,115:109:-350.74,-32.88,-0.10:99 +chr1 5074 rs11586607 T G 0.01 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:29,97:39:-130.41,-11.75,-3.82:79.31 +chr1 5137 rs62636497 A T 140.49 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:0,74:39:-148.99,-11.75,-0.01:99 diff --git a/public/testdata/diffTestTest.vcf b/public/testdata/diffTestTest.vcf new file mode 100644 index 000000000..8699ab253 --- /dev/null +++ b/public/testdata/diffTestTest.vcf @@ -0,0 +1,11 @@ +##fileformat=VCFv4.0 +#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT NA12878 +chr1 2646 rs62635284 G A 0.15 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:53,75:3:-12.40,-0.90,-0.00:9.03 +chr1 2979 rs62635286 T G 83.67 CHANGED_FILTER AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:31,32:9:-33.61,-2.71,-0.00:27.09 +chr1 2981 rs62028691 A G 14.69 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:31,33:9:-32.12,-2.71,-0.00:27.08 +chr1 4536 rs11582131 G C 0.18 PASS AC=2;AF=0.50;AN=2 GT:AD:DP:GL:GQ 0/1:42,33:16:-41.67,-4.82,-26.29:99 +chr1 4562 rs11490464 C G 0.14 PASS AC=1;AF=0.50;AN=2 GT:AD:DP:GL:GQ 1/1:26,30:9:-19.64,-2.72,-14.87:99 +chr1 4770 rs6682375 A G 0.32 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 0/1:9,111:84:-306.27,-28.58,-3.46:99 +chr1 4793 rs6682385 A G 0.15 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:4,114:109:-350.74,-32.88,-0.10:99 +chr1 5074 rs11586607 T G 0.01 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:29,97:39:-130.41,-11.74,-3.82:79.31 +chr1 5137 rs62636497 A T 140.49 PASS AC=2;AF=1.00;AN=2 GT:AD:DP:GL:GQ 1/1:0,74:39:-148.99,-11.75,-0.01:9 From 080875d5daf876ebd29b059c156890d7c349983f Mon Sep 17 00:00:00 2001 From: "Mark A. DePristo" Date: Tue, 5 Jul 2011 16:13:39 -0400 Subject: [PATCH 15/59] Refactored DiffNode/DiffElement/DiffValue class structure. DiffElement is now a pair of Name -> Value, where value is either a DiffValue or its subclass DiffNode. Code cleaned up, more tests added. DiffEngine is now working, with tests. DiffObjectWalker can now take two VCFs and itemize the difference between the two files correctly and concisely. --- build.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.xml b/build.xml index fe1723587..21066686f 100644 --- a/build.xml +++ b/build.xml @@ -741,8 +741,8 @@ - - + + From 7d3dfdfdf2e92f502c4394d338ca01c5de6287e5 Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Tue, 5 Jul 2011 16:30:10 -0400 Subject: [PATCH 16/59] Updating the MDCP to use the classpath for the GATK jar, removing -gatk parameter. --- .../queue/qscripts/MethodsDevelopmentCallingPipeline.scala | 4 ---- 1 file changed, 4 deletions(-) diff --git a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala index d71c6ae5d..f5b639185 100755 --- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala +++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala @@ -19,9 +19,6 @@ import org.broadinstitute.sting.gatk.phonehome.GATKRunReport class MethodsDevelopmentCallingPipeline extends QScript { qscript => - @Argument(shortName="gatk", doc="gatk jar file", required=true) - var gatkJarFile: File = _ - @Argument(shortName="outputDir", doc="output directory", required=true) var outputDir: String = "./" @@ -185,7 +182,6 @@ class MethodsDevelopmentCallingPipeline extends QScript { trait UNIVERSAL_GATK_ARGS extends CommandLineGATK { logging_level = "INFO"; - jarFile = gatkJarFile; memoryLimit = 4; phone_home = if ( LOCAL_ET ) GATKRunReport.PhoneHomeOption.STANDARD else GATKRunReport.PhoneHomeOption.AWS_S3 } From 5298e3a942be54c05a8d765c7aaca99e9c061f8a Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Tue, 5 Jul 2011 16:30:41 -0400 Subject: [PATCH 17/59] Making the outputDir optional. Default = ./ --- .../queue/qscripts/MethodsDevelopmentCallingPipeline.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala index f5b639185..a961beca1 100755 --- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala +++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala @@ -19,7 +19,7 @@ import org.broadinstitute.sting.gatk.phonehome.GATKRunReport class MethodsDevelopmentCallingPipeline extends QScript { qscript => - @Argument(shortName="outputDir", doc="output directory", required=true) + @Argument(shortName="outputDir", doc="output directory", required=false) var outputDir: String = "./" @Argument(shortName="skipCalling", doc="skip the calling part of the pipeline and only run VQSR on preset, gold standard VCF files", required=false) From 7b699f8b175f02bbf7ba2cb1aea420361dc4af0d Mon Sep 17 00:00:00 2001 From: Khalid Shakir Date: Tue, 5 Jul 2011 21:59:00 -0400 Subject: [PATCH 18/59] Switched GridEngine from looking from environment variable to using embedded jar. --- build.xml | 33 ++++++++---------- ivy.xml | 3 ++ settings/ivysettings.xml | 1 + .../drmaa-6.2u5p2-sources.jar | Bin 0 -> 110692 bytes .../net.sf.gridscheduler/drmaa-6.2u5p2.jar | Bin 0 -> 51479 bytes .../net.sf.gridscheduler/drmaa-6.2u5p2.xml | 3 ++ 6 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 settings/repository/net.sf.gridscheduler/drmaa-6.2u5p2-sources.jar create mode 100644 settings/repository/net.sf.gridscheduler/drmaa-6.2u5p2.jar create mode 100644 settings/repository/net.sf.gridscheduler/drmaa-6.2u5p2.xml diff --git a/build.xml b/build.xml index fe1723587..a78c4d738 100644 --- a/build.xml +++ b/build.xml @@ -69,8 +69,6 @@ - - @@ -146,11 +144,7 @@ - - - - - @@ -178,13 +172,23 @@ - - + + + + + - + + + + + + + + @@ -214,12 +218,6 @@ - - - - - - @@ -357,7 +355,6 @@ - @@ -374,7 +371,6 @@ - @@ -682,7 +678,6 @@ - diff --git a/ivy.xml b/ivy.xml index c2a6c4ccd..10e4ee570 100644 --- a/ivy.xml +++ b/ivy.xml @@ -48,6 +48,9 @@ + + + diff --git a/settings/ivysettings.xml b/settings/ivysettings.xml index 1e47fa847..2c8fc388f 100644 --- a/settings/ivysettings.xml +++ b/settings/ivysettings.xml @@ -25,5 +25,6 @@ + diff --git a/settings/repository/net.sf.gridscheduler/drmaa-6.2u5p2-sources.jar b/settings/repository/net.sf.gridscheduler/drmaa-6.2u5p2-sources.jar new file mode 100644 index 0000000000000000000000000000000000000000..dc77c7d333c61a79fa8c7be7d8ad3345678f811c GIT binary patch literal 110692 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 ml>~}UXkIdr+QWy?jjBsZE6)k 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 F{|_H6`St(+ literal 0 HcmV?d00001 diff --git a/settings/repository/net.sf.gridscheduler/drmaa-6.2u5p2.jar b/settings/repository/net.sf.gridscheduler/drmaa-6.2u5p2.jar new file mode 100644 index 0000000000000000000000000000000000000000..f267be4b58a1930909cdf7a37a031893c6bbb71a GIT binary patch literal 51479 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 + + From 81a4d18468b85b8cdc79e6f9d06c735f5ca16d9b Mon Sep 17 00:00:00 2001 From: Guillermo del Angel Date: Wed, 6 Jul 2011 09:56:38 -0400 Subject: [PATCH 20/59] Mark several indel-related arguments as @Hidden --- .../gatk/walkers/genotyper/UnifiedArgumentCollection.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedArgumentCollection.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedArgumentCollection.java index 1045ca371..055eb0b97 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedArgumentCollection.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedArgumentCollection.java @@ -92,25 +92,31 @@ public class UnifiedArgumentCollection { @Argument(fullName = "indel_heterozygosity", shortName = "indelHeterozygosity", doc = "Heterozygosity for indel calling", required = false) public double INDEL_HETEROZYGOSITY = 1.0/8000; + @Hidden @Argument(fullName = "indelGapContinuationPenalty", shortName = "indelGCP", doc = "Indel gap continuation penalty", required = false) public double INDEL_GAP_CONTINUATION_PENALTY = 10.0; + @Hidden @Argument(fullName = "indelGapOpenPenalty", shortName = "indelGOP", doc = "Indel gap open penalty", required = false) public double INDEL_GAP_OPEN_PENALTY = 45.0; + @Hidden @Argument(fullName = "indelHaplotypeSize", shortName = "indelHSize", doc = "Indel haplotype size", required = false) public int INDEL_HAPLOTYPE_SIZE = 80; + @Hidden @Argument(fullName = "doContextDependentGapPenalties", shortName = "doCDP", doc = "Vary gap penalties by context", required = false) public boolean DO_CONTEXT_DEPENDENT_PENALTIES = true; //gdebug+ - @Hidden // experimental arguments, NOT TO BE USED BY ANYONE WHOSE INITIALS AREN'T GDA!!! + @Hidden @Argument(fullName = "getGapPenaltiesFromData", shortName = "dataGP", doc = "Vary gap penalties by context - EXPERIMENTAL, DO NO USE", required = false) public boolean GET_GAP_PENALTIES_FROM_DATA = false; + @Hidden @Argument(fullName="indel_recal_file", shortName="recalFile", required=false, doc="Filename for the input covariates table recalibration .csv file - EXPERIMENTAL, DO NO USE") public File INDEL_RECAL_FILE = new File("indel.recal_data.csv"); + @Hidden @Argument(fullName = "indelDebug", shortName = "indelDebug", doc = "Output indel debug info", required = false) public boolean OUTPUT_DEBUG_INDEL_INFO = false; @Hidden From e8ed6b7f0f1c3e0e4f75d77349807b36ee37a3c7 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Wed, 6 Jul 2011 10:01:14 -0400 Subject: [PATCH 21/59] Adding more comments to main VQSR walker. Fixing copyright lines. Bug fix for default paths to now point to public/R/ instead of R/ Bug fix in VQSR for the path to the R scripts not ending in a slash. --- .../analyzecovariates/AnalyzeCovariates.java | 2 +- .../ApplyRecalibration.java | 2 +- .../GaussianMixtureModel.java | 25 ++++++++++++++++++ .../MultivariateGaussian.java | 25 ++++++++++++++++++ .../variantrecalibration/TrainingSet.java | 25 ++++++++++++++++++ .../walkers/variantrecalibration/Tranche.java | 2 +- .../variantrecalibration/TrancheManager.java | 25 ++++++++++++++++++ .../VQSRCalibrationCurve.java | 26 ++++++++++++++++++- .../VariantDataManager.java | 25 ++++++++++++++++++ .../variantrecalibration/VariantDatum.java | 25 ++++++++++++++++++ .../VariantRecalibrator.java | 15 ++++++++--- ...VariantRecalibratorArgumentCollection.java | 25 ++++++++++++++++++ .../VariantRecalibratorEngine.java | 25 ++++++++++++++++++ 13 files changed, 239 insertions(+), 8 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java b/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java index 3cf43c15a..f8e298d88 100755 --- a/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java +++ b/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java @@ -61,7 +61,7 @@ public class AnalyzeCovariates extends CommandLineProgram { @Argument(fullName = "path_to_Rscript", shortName = "Rscript", doc = "The path to your implementation of Rscript. For Broad users this is maybe /broad/tools/apps/R-2.6.0/bin/Rscript", required = false) private String PATH_TO_RSCRIPT = "Rscript"; @Argument(fullName = "path_to_resources", shortName = "resources", doc = "Path to resources folder holding the Sting R scripts.", required = false) - private String PATH_TO_RESOURCES = "R/"; + private String PATH_TO_RESOURCES = "public/R/"; @Argument(fullName = "ignoreQ", shortName = "ignoreQ", doc = "Ignore bases with reported quality less than this number.", required = false) private int IGNORE_QSCORES_LESS_THAN = 5; @Argument(fullName = "numRG", shortName = "numRG", doc = "Only process N read groups. Default value: -1 (process all read groups)", required = false) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/ApplyRecalibration.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/ApplyRecalibration.java index 9877781d1..7957d35cd 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/ApplyRecalibration.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/ApplyRecalibration.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 The Broad Institute + * Copyright (c) 2011 The Broad Institute * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java index 9ffe7be7a..acc1f24cc 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2011 The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + package org.broadinstitute.sting.gatk.walkers.variantrecalibration; import Jama.Matrix; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/MultivariateGaussian.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/MultivariateGaussian.java index 0b2edfd10..d077af78e 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/MultivariateGaussian.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/MultivariateGaussian.java @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2011 The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + package org.broadinstitute.sting.gatk.walkers.variantrecalibration; import Jama.Matrix; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/TrainingSet.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/TrainingSet.java index f3677421e..67132b133 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/TrainingSet.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/TrainingSet.java @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2011 The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + package org.broadinstitute.sting.gatk.walkers.variantrecalibration; import org.apache.log4j.Logger; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/Tranche.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/Tranche.java index fbee64fe2..64fe36637 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/Tranche.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/Tranche.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010 The Broad Institute + * Copyright (c) 2011 The Broad Institute * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/TrancheManager.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/TrancheManager.java index 08388db21..19c6d501b 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/TrancheManager.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/TrancheManager.java @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2011 The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + package org.broadinstitute.sting.gatk.walkers.variantrecalibration; import org.apache.log4j.Logger; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VQSRCalibrationCurve.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VQSRCalibrationCurve.java index 2914385a4..5deb5d8c2 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VQSRCalibrationCurve.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VQSRCalibrationCurve.java @@ -1,8 +1,32 @@ +/* + * Copyright (c) 2011 The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + package org.broadinstitute.sting.gatk.walkers.variantrecalibration; import org.apache.log4j.Logger; import org.broadinstitute.sting.utils.variantcontext.VariantContext; -import org.broadinstitute.sting.gatk.walkers.varianteval.evaluators.VariantQualityScore; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.broadinstitute.sting.utils.exceptions.UserException; import org.broadinstitute.sting.utils.text.XReadLines; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java index 2fd1326fe..f0a78280d 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2011 The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + package org.broadinstitute.sting.gatk.walkers.variantrecalibration; import cern.jet.random.Normal; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDatum.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDatum.java index ac875b645..8295ec205 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDatum.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDatum.java @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2011 The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + package org.broadinstitute.sting.gatk.walkers.variantrecalibration; /** diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java index e651b62e0..a77c5962c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java @@ -87,7 +87,7 @@ public class VariantRecalibrator extends RodWalker(Arrays.asList(USE_ANNOTATIONS)), VRAC ); if( IGNORE_INPUT_FILTERS != null ) { @@ -228,18 +229,22 @@ public class VariantRecalibrator extends RodWalker reduceSum ) { dataManager.setData( reduceSum ); dataManager.normalizeData(); // Each data point is now (x - mean) / standard deviation + + // Generate the positive model using the training data and evaluate each variant final GaussianMixtureModel goodModel = engine.generateModel( dataManager.getTrainingData() ); engine.evaluateData( dataManager.getData(), goodModel, false ); + + // Generate the negative model using the worst performing data and evaluate each variant contrastively final GaussianMixtureModel badModel = engine.generateModel( dataManager.selectWorstVariants( VRAC.PERCENT_BAD_VARIANTS, VRAC.MIN_NUM_BAD_VARIANTS ) ); engine.evaluateData( dataManager.getData(), badModel, true ); - final ExpandingArrayList randomData = dataManager.getRandomDataForPlotting( 6000 ); - + // Find the VQSLOD cutoff values which correspond to the various tranches of calls requested by the user final int nCallsAtTruth = TrancheManager.countCallsAtTruth( dataManager.getData(), Double.NEGATIVE_INFINITY ); final TrancheManager.SelectionMetric metric = new TrancheManager.TruthSensitivityMetric( nCallsAtTruth ); final List tranches = TrancheManager.findTranches( dataManager.getData(), TS_TRANCHES, metric ); tranchesStream.print(Tranche.tranchesString( tranches )); + // Find the filtering lodCutoff for display on the model PDFs. Red variants are those which were below the cutoff and filtered out of the final callset. double lodCutoff = 0.0; for( final Tranche tranche : tranches ) { if( MathUtils.compareDoubles(tranche.ts, TS_FILTER_LEVEL, 0.0001)==0 ) { @@ -251,7 +256,7 @@ public class VariantRecalibrator extends RodWalker Date: Wed, 6 Jul 2011 11:32:46 -0400 Subject: [PATCH 22/59] Fixed namespace of the pipeline classes. --- build.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.xml b/build.xml index fe1723587..2a23f74c1 100644 --- a/build.xml +++ b/build.xml @@ -464,7 +464,7 @@ - + From 14fee4ccbd990bd32956017624dfbcdddeee75ae Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 6 Jul 2011 12:51:44 -0400 Subject: [PATCH 26/59] Patch from Bob to deal with symbolic alleles: these weren't getting padded but they should be. --- .../sting/utils/variantcontext/VariantContext.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java index 3d375aba2..5787b591f 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java @@ -1343,6 +1343,15 @@ public class VariantContext implements Feature { // to enable tribble intergrati return (int)stop; } + private boolean hasSymbolicAlleles() { + for (Allele a: getAlleles()) { + if (a.isSymbolic()) { + return true; + } + } + return false; + } + public static VariantContext createVariantContextWithPaddedAlleles(VariantContext inputVC, byte inputRefBase, boolean refBaseShouldBeAppliedToEndOfAlleles) { Allele refAllele = inputVC.getReference(); @@ -1352,7 +1361,9 @@ public class VariantContext implements Feature { // to enable tribble intergrati // We need to pad a VC with a common base if the length of the reference allele is less than the length of the VariantContext. // This happens because the position of e.g. an indel is always one before the actual event (as per VCF convention). long locLength = (inputVC.getEnd() - inputVC.getStart()) + 1; - if (refAllele.length() == locLength) + if (inputVC.hasSymbolicAlleles()) + padVC = true; + else if (refAllele.length() == locLength) padVC = false; else if (refAllele.length() == locLength-1) padVC = true; From 54121eb082eb84920770d573ff143efbe32f0340 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 6 Jul 2011 16:05:08 -0400 Subject: [PATCH 29/59] Catch malformed bams that cause the writer to run in infinite loops --- .../gatk/walkers/indels/ConstrainedMateFixingManager.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/ConstrainedMateFixingManager.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/ConstrainedMateFixingManager.java index aebb0e3eb..df1f4f908 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/ConstrainedMateFixingManager.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/ConstrainedMateFixingManager.java @@ -5,6 +5,7 @@ import net.sf.samtools.*; import org.apache.log4j.Logger; import org.broadinstitute.sting.utils.GenomeLoc; import org.broadinstitute.sting.utils.GenomeLocParser; +import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.broadinstitute.sting.utils.exceptions.UserException; import java.util.*; @@ -113,9 +114,10 @@ public class ConstrainedMateFixingManager { HashMap forMateMatching = new HashMap(); TreeSet waitingReads = new TreeSet(comparer); - private T remove(TreeSet treeSet) { - final T first = treeSet.first(); - treeSet.remove(first); + private SAMRecord remove(TreeSet treeSet) { + final SAMRecord first = treeSet.first(); + if ( !treeSet.remove(first) ) + throw new UserException("Error caching SAM record " + first.getReadName() + ", which is usually caused by malformed SAM/BAM files in which multiple identical copies of a read are present."); return first; } From ccf34f7e45164cea70beccb4cf1b0791e6acde06 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 6 Jul 2011 21:57:22 -0400 Subject: [PATCH 30/59] (1) Added very useful helper class TestDataProvider to BaseTest that making creating data providers for TestNG far easier (2) DiffEngine now officially working with with summaries. Extensive UnitTests all around! --- .../org/broadinstitute/sting/BaseTest.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/public/java/test/org/broadinstitute/sting/BaseTest.java b/public/java/test/org/broadinstitute/sting/BaseTest.java index 61bb8b34b..b469c8a41 100755 --- a/public/java/test/org/broadinstitute/sting/BaseTest.java +++ b/public/java/test/org/broadinstitute/sting/BaseTest.java @@ -12,6 +12,10 @@ import java.io.*; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * @@ -107,6 +111,57 @@ public abstract class BaseTest { } } + /** + * Simple generic utility class to creating TestNG data providers: + * + * 1: inherit this class, as in + * + * private class SummarizeDifferenceTest extends TestDataProvider { + * public SummarizeDifferenceTest() { + * super(SummarizeDifferenceTest.class); + * } + * ... + * } + * + * Provide a reference to your class to the TestDataProvider constructor. + * + * 2: Create instances of your subclass. Return from it the call to getTests, providing + * the class type of your test + * + * @DataProvider(name = "summaries") + * public Object[][] createSummaries() { + * new SummarizeDifferenceTest().addDiff("A", "A").addSummary("A:2"); + * new SummarizeDifferenceTest().addDiff("A", "B").addSummary("A:1", "B:1"); + * return SummarizeDifferenceTest.getTests(SummarizeDifferenceTest.class); + * } + * + * This class magically tracks created objects of this + */ + public static class TestDataProvider { + private static final Map> tests = new HashMap>(); + + /** + * Create a new TestDataProvider instance bound to the class variable C + * @param c + */ + public TestDataProvider(Class c) { + if ( ! tests.containsKey(c) ) + tests.put(c, new ArrayList()); + tests.get(c).add(this); + } + + /** + * Return all of the data providers in the form expected by TestNG of type class C + * @param c + * @return + */ + public static Object[][] getTests(Class c) { + List params2 = new ArrayList(); + for ( Object x : tests.get(c) ) params2.add(new Object[]{x}); + return params2.toArray(new Object[][]{}); + } + } + /** * test if the file exists * From 50111db2b7920ba5b3cfe659e33f7135ebd277f5 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Thu, 7 Jul 2011 15:02:48 -0400 Subject: [PATCH 33/59] Fixing non-determinism in single-threaded VQSR by moving references to cern.Normal over to the static random generator available in GenomeAnalysisEngine --- .../GaussianMixtureModel.java | 24 ++++++++++--------- .../VariantDataManager.java | 3 +-- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java index acc1f24cc..41fea0896 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java @@ -26,7 +26,6 @@ package org.broadinstitute.sting.gatk.walkers.variantrecalibration; import Jama.Matrix; -import cern.jet.random.Normal; import org.apache.log4j.Logger; import org.broadinstitute.sting.gatk.GenomeAnalysisEngine; import org.broadinstitute.sting.utils.MathUtils; @@ -97,7 +96,7 @@ public class GaussianMixtureModel { int ttt = 0; while( ttt++ < numIterations ) { - // Estep: assign each variant to the nearest cluster + // E step: assign each variant to the nearest cluster for( final VariantDatum datum : data ) { double minDistance = Double.MAX_VALUE; MultivariateGaussian minGaussian = null; @@ -112,7 +111,7 @@ public class GaussianMixtureModel { datum.assignment = minGaussian; } - // Mstep: update gaussian means based on assigned variants + // M step: update gaussian means based on assigned variants for( final MultivariateGaussian gaussian : gaussians ) { gaussian.zeroOutMu(); int numAssigned = 0; @@ -216,26 +215,29 @@ public class GaussianMixtureModel { } public double evaluateDatumMarginalized( final VariantDatum datum ) { - int numVals = 0; + int numSamples = 0; double sumPVarInGaussian = 0.0; - int numIter = 10; + final int numIterPerMissingAnnotation = 10; // Trade off here between speed of computation and accuracy of the marginalization final double[] pVarInGaussianLog10 = new double[gaussians.size()]; + // for each dimension for( int iii = 0; iii < datum.annotations.length; iii++ ) { - // marginalize over the missing dimension by drawing X random values for the missing annotation and averaging the lod + // if it is missing marginalize over the missing dimension by drawing X random values for the missing annotation and averaging the lod if( datum.isNull[iii] ) { - for( int ttt = 0; ttt < numIter; ttt++ ) { - datum.annotations[iii] = Normal.staticNextDouble(0.0, 1.0); + for( int ttt = 0; ttt < numIterPerMissingAnnotation; ttt++ ) { + datum.annotations[iii] = GenomeAnalysisEngine.getRandomGenerator().nextGaussian(); // draw a random sample from the standard normal distribution + // evaluate this random data point int gaussianIndex = 0; for( final MultivariateGaussian gaussian : gaussians ) { pVarInGaussianLog10[gaussianIndex++] = gaussian.pMixtureLog10 + gaussian.evaluateDatumLog10( datum ); } - sumPVarInGaussian += Math.pow(10.0, MathUtils.log10sumLog10(pVarInGaussianLog10)); - numVals++; + // add this sample's probability to the pile in order to take an average in the end + sumPVarInGaussian += Math.pow(10.0, MathUtils.log10sumLog10(pVarInGaussianLog10)); // p = 10 ^ Sum(pi_k * p(v|n,k)) + numSamples++; } } } - return Math.log10( sumPVarInGaussian / ((double) numVals) ); + return Math.log10( sumPVarInGaussian / ((double) numSamples) ); } } \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java index f0a78280d..e1a076e76 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java @@ -25,7 +25,6 @@ package org.broadinstitute.sting.gatk.walkers.variantrecalibration; -import cern.jet.random.Normal; import org.apache.log4j.Logger; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import org.broadinstitute.sting.gatk.GenomeAnalysisEngine; @@ -91,7 +90,7 @@ public class VariantDataManager { meanVector[iii] = theMean; varianceVector[iii] = theSTD; for( final VariantDatum datum : data ) { - datum.annotations[iii] = ( datum.isNull[iii] ? Normal.staticNextDouble(0.0, 1.0) : ( datum.annotations[iii] - theMean ) / theSTD ); + datum.annotations[iii] = ( datum.isNull[iii] ? GenomeAnalysisEngine.getRandomGenerator().nextGaussian() : ( datum.annotations[iii] - theMean ) / theSTD ); // Each data point is now [ (x - mean) / standard deviation ] if( annotationKeys.get(iii).toLowerCase().contains("ranksum") && datum.isNull[iii] && datum.annotations[iii] > 0.0 ) { datum.annotations[iii] /= 3.0; From 212e9a1a0cf766f54f70d17d0786db570f8111ae Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Thu, 7 Jul 2011 15:18:57 -0400 Subject: [PATCH 34/59] Fixing unstable build after stable commit --- .../gatk/walkers/variantrecalibration/GaussianMixtureModel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java index b4a8c4c32..17461de2f 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java @@ -26,6 +26,7 @@ package org.broadinstitute.sting.gatk.walkers.variantrecalibration; import Jama.Matrix; +import cern.jet.random.Normal; import org.apache.log4j.Logger; import org.broadinstitute.sting.gatk.GenomeAnalysisEngine; import org.broadinstitute.sting.utils.MathUtils; From 3d4f0e9dd76d1ab23a7e7fdecd61067b57b8e7d7 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 7 Jul 2011 17:21:15 -0400 Subject: [PATCH 39/59] Now supports the case where you have multiple AC values in the info field. --- .../gatk/walkers/varianteval/stratifications/AlleleCount.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/AlleleCount.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/AlleleCount.java index ff59c9e29..2cbc66e31 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/AlleleCount.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/AlleleCount.java @@ -43,9 +43,9 @@ public class AlleleCount extends VariantStratifier { if (eval != null) { int AC = -1; - if ( eval.hasAttribute("AC") ) + if ( eval.hasAttribute("AC") && eval.getAttribute("AC") instanceof Integer ) { AC = eval.getAttributeAsInt("AC"); - else if ( eval.isVariant() ) { + } else if ( eval.isVariant() ) { for (Allele allele : eval.getAlternateAlleles()) AC = Math.max(AC, eval.getChromosomeCount(allele)); } else From 4cfe0dd857891f6c70f4d0e58b60d6c4c401a0cd Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 7 Jul 2011 23:01:03 -0400 Subject: [PATCH 42/59] Test for bad alleles so that we don't generate IndexOutOfBoundsExceptions --- .../sting/utils/codecs/vcf/AbstractVCFCodec.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java index cd0f52c68..01344a117 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java @@ -225,7 +225,7 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, loc = pos + alleles.get(0).length() - 1; } else if ( !isSingleNucleotideEvent(alleles) ) { ArrayList newAlleles = new ArrayList(); - loc = clipAlleles(pos, ref, alleles, newAlleles); + loc = clipAlleles(pos, ref, alleles, newAlleles, lineNo); alleles = newAlleles; } @@ -504,7 +504,7 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, * @param clippedAlleles output list of clipped alleles * @return a list of alleles, clipped to the reference */ - protected static long clipAlleles(long position, String ref, List unclippedAlleles, List clippedAlleles) { + protected static long clipAlleles(long position, String ref, List unclippedAlleles, List clippedAlleles, int lineNo) { // Note that the computation of forward clipping here is meant only to see whether there is a common // base to all alleles, and to correctly compute reverse clipping, @@ -522,6 +522,8 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, } if (a.length() - reverseClipped <= forwardClipping || a.length() - forwardClipping == 0) clipping = false; + else if (ref.length() == reverseClipped) + generateException("bad alleles encountered", lineNo); else if (a.getBases()[a.length()-reverseClipped-1] != ref.getBytes()[ref.length()-reverseClipped-1]) clipping = false; } From 2a4b3ae4a20ea3d5bbd3047f2c2f7842cd01e2ef Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Fri, 8 Jul 2011 12:48:33 -0400 Subject: [PATCH 45/59] Cleaning up / removing most of the monkeying around with annotation values that happens in VariantDataManager --- .../VariantDataManager.java | 34 +++++-------------- .../VariantRecalibrator.java | 6 ++-- ...VariantRecalibratorArgumentCollection.java | 2 +- 3 files changed, 13 insertions(+), 29 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java index 5f35c182c..ddeda1699 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java @@ -82,19 +82,11 @@ public class VariantDataManager { } foundZeroVarianceAnnotation = foundZeroVarianceAnnotation || (theSTD < 1E-6); - if( annotationKeys.get(iii).toLowerCase().contains("ranksum") ) { // BUGBUG: to clean up - for( final VariantDatum datum : data ) { - if( datum.annotations[iii] > 0.0 ) { datum.annotations[iii] /= 3.0; } - } - } meanVector[iii] = theMean; varianceVector[iii] = theSTD; for( final VariantDatum datum : data ) { + // Transform each data point via: (x - mean) / standard deviation datum.annotations[iii] = ( datum.isNull[iii] ? GenomeAnalysisEngine.getRandomGenerator().nextGaussian() : ( datum.annotations[iii] - theMean ) / theSTD ); - // Each data point is now [ (x - mean) / standard deviation ] - if( annotationKeys.get(iii).toLowerCase().contains("ranksum") && datum.isNull[iii] && datum.annotations[iii] > 0.0 ) { - datum.annotations[iii] /= 3.0; - } } } if( foundZeroVarianceAnnotation ) { @@ -163,7 +155,7 @@ public class VariantDataManager { final int numBadSitesAdded = trainingData.size(); logger.info( "Found " + numBadSitesAdded + " variants overlapping bad sites training tracks." ); - // Next, sort the variants by the LOD coming from the positive model and add to the list the bottom X percent of variants + // Next sort the variants by the LOD coming from the positive model and add to the list the bottom X percent of variants Collections.sort( data ); final int numToAdd = Math.max( minimumNumber - trainingData.size(), Math.round((float)bottomPercentage * data.size()) ); if( numToAdd > data.size() ) { @@ -241,23 +233,15 @@ public class VariantDataManager { double value; try { - if( annotationKey.equalsIgnoreCase("QUAL") ) { - value = vc.getPhredScaledQual(); - } else if( annotationKey.equalsIgnoreCase("DP") ) { - value = Double.parseDouble( (String)vc.getAttribute( "DP" ) ) / Double.parseDouble( (String)vc.getAttribute( "AN" ) ); - } else { - value = Double.parseDouble( (String)vc.getAttribute( annotationKey ) ); - if( Double.isInfinite(value) ) { value = Double.NaN; } - if( annotationKey.equalsIgnoreCase("InbreedingCoeff") && value > 0.05 ) { value = Double.NaN; } - if( jitter && annotationKey.equalsIgnoreCase("HRUN") ) { // Integer valued annotations must be jittered a bit to work in this GMM - value += -0.25 + 0.5 * GenomeAnalysisEngine.getRandomGenerator().nextDouble(); - } - if( annotationKey.equalsIgnoreCase("HaplotypeScore") && MathUtils.compareDoubles(value, 0.0, 0.0001) == 0 ) { value = -0.2 + 0.4*GenomeAnalysisEngine.getRandomGenerator().nextDouble(); } - if( annotationKey.equalsIgnoreCase("FS") && MathUtils.compareDoubles(value, 0.0, 0.01) == 0 ) { value = -0.2 + 0.4*GenomeAnalysisEngine.getRandomGenerator().nextDouble(); } + value = Double.parseDouble( (String)vc.getAttribute( annotationKey ) ); + if( Double.isInfinite(value) ) { value = Double.NaN; } + if( jitter && annotationKey.equalsIgnoreCase("HRUN") ) { // Integer valued annotations must be jittered a bit to work in this GMM + value += -0.25 + 0.5 * GenomeAnalysisEngine.getRandomGenerator().nextDouble(); } - + if( jitter && annotationKey.equalsIgnoreCase("HaplotypeScore") && MathUtils.compareDoubles(value, 0.0, 0.0001) == 0 ) { value = -0.2 + 0.4*GenomeAnalysisEngine.getRandomGenerator().nextDouble(); } + if( jitter && annotationKey.equalsIgnoreCase("FS") && MathUtils.compareDoubles(value, 0.0, 0.001) == 0 ) { value = -0.2 + 0.4*GenomeAnalysisEngine.getRandomGenerator().nextDouble(); } } catch( Exception e ) { - value = Double.NaN; // The VQSR works with missing data now by marginalizing over the missing dimension when evaluating Gaussians + value = Double.NaN; // The VQSR works with missing data by marginalizing over the missing dimension when evaluating the Gaussian mixture model } return value; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java index 2c51f02d6..2d0355d7d 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java @@ -284,7 +284,7 @@ public class VariantRecalibrator extends RodWalker Date: Fri, 8 Jul 2011 12:48:49 -0400 Subject: [PATCH 46/59] Bug fix: if we're genotyping a very long indel (>100 bp) fail gracefully instead of with an array out of bounds exception --- .../org/broadinstitute/sting/utils/genotype/Haplotype.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/genotype/Haplotype.java b/public/java/src/org/broadinstitute/sting/utils/genotype/Haplotype.java index cb6557408..31791e805 100755 --- a/public/java/src/org/broadinstitute/sting/utils/genotype/Haplotype.java +++ b/public/java/src/org/broadinstitute/sting/utils/genotype/Haplotype.java @@ -133,8 +133,12 @@ public class Haplotype { byte[] basesBeforeVariant = Arrays.copyOfRange(refBases,startIdxInReference,startIdxInReference+numPrefBases); + int startAfter = startIdxInReference+numPrefBases+ refAllele.getBases().length; + // protect against long events that overrun available reference context + if (startAfter > refBases.length) + startAfter = refBases.length; byte[] basesAfterVariant = Arrays.copyOfRange(refBases, - startIdxInReference+numPrefBases+ refAllele.getBases().length, refBases.length); + startAfter, refBases.length); // Create location for all haplotypes From a3c9d9c3ff561a0a910b5313e0ebb037b7883a4b Mon Sep 17 00:00:00 2001 From: David Roazen Date: Fri, 8 Jul 2011 15:34:39 -0400 Subject: [PATCH 51/59] Fixing Contracts for Java, and enabling contracts by default for unit/integration tests. The NullPointerException we were seeing when trying to run with contracts enabled was being caused by an outdated version of the asm library. To run tests without contracts and disable their compilation, pass in "-Duse.contracts=false" to ant. Also did some minor unrelated cleanup in build.xml --- build.xml | 70 +++++++++++------- ivy.xml | 4 + settings/ivysettings.xml | 1 + .../cofoja-1.0-20110609.jar | Bin .../cofoja-1.0-20110609.xml | 3 + 5 files changed, 51 insertions(+), 27 deletions(-) rename settings/repository/{com.google => com.google.code.cofoja}/cofoja-1.0-20110609.jar (100%) create mode 100644 settings/repository/com.google.code.cofoja/cofoja-1.0-20110609.xml diff --git a/build.xml b/build.xml index 2a23f74c1..986d89213 100644 --- a/build.xml +++ b/build.xml @@ -28,6 +28,7 @@ + @@ -44,11 +45,11 @@ - - + + - - + + @@ -69,7 +70,7 @@ - + @@ -103,7 +104,7 @@ - + @@ -128,14 +129,14 @@ - + - + - + @@ -147,7 +148,7 @@ - + + + + + @@ -252,7 +257,7 @@ - + @@ -287,7 +292,7 @@ depends="gatk.compile.public.source,gatk.compile.private.source,gatk.compile.external.source" description="compile the GATK source" /> - + @@ -299,7 +304,16 @@ - + + + + + + + + + + @@ -312,7 +326,7 @@ + description="create GATK contracts" if="include.contracts" /> @@ -452,7 +466,7 @@ - + @@ -663,7 +677,7 @@ - + - - + @@ -820,7 +836,7 @@ - + @@ -828,7 +844,7 @@ - + @@ -921,8 +937,8 @@ - - + + @@ -944,7 +960,7 @@ - + @@ -969,7 +985,7 @@ - + diff --git a/ivy.xml b/ivy.xml index c2a6c4ccd..ce724bc3c 100644 --- a/ivy.xml +++ b/ivy.xml @@ -60,6 +60,10 @@ + + + + diff --git a/settings/ivysettings.xml b/settings/ivysettings.xml index 1e47fa847..b77414df9 100644 --- a/settings/ivysettings.xml +++ b/settings/ivysettings.xml @@ -25,5 +25,6 @@ + diff --git a/settings/repository/com.google/cofoja-1.0-20110609.jar b/settings/repository/com.google.code.cofoja/cofoja-1.0-20110609.jar similarity index 100% rename from settings/repository/com.google/cofoja-1.0-20110609.jar rename to settings/repository/com.google.code.cofoja/cofoja-1.0-20110609.jar diff --git a/settings/repository/com.google.code.cofoja/cofoja-1.0-20110609.xml b/settings/repository/com.google.code.cofoja/cofoja-1.0-20110609.xml new file mode 100644 index 000000000..38d4e88f1 --- /dev/null +++ b/settings/repository/com.google.code.cofoja/cofoja-1.0-20110609.xml @@ -0,0 +1,3 @@ + + + From 8a78414432226daceccccb4b38463c0826fb0f19 Mon Sep 17 00:00:00 2001 From: David Roazen Date: Mon, 11 Jul 2011 12:10:11 -0400 Subject: [PATCH 52/59] Removed TileCovariate as a dependency for AnalyzeCovariates.jar --- public/packages/AnalyzeCovariates.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/public/packages/AnalyzeCovariates.xml b/public/packages/AnalyzeCovariates.xml index 1862d6cbb..7e31934df 100644 --- a/public/packages/AnalyzeCovariates.xml +++ b/public/packages/AnalyzeCovariates.xml @@ -10,7 +10,6 @@ - From 86890c63574eba41bbbce6c52e2614f6c81b6f27 Mon Sep 17 00:00:00 2001 From: Christopher Hartl Date: Mon, 11 Jul 2011 16:16:15 -0400 Subject: [PATCH 54/59] N and K (in binomial probability) got switched in RFA Walker with the last commit. No longer will NaNs be produced. Added: TableToVCF. Kind of a longer-term project, but there are lots of variant calls available in a weird tabular format. I used this to convert Ju Et Al small indels to VCF. I'll check against the 1000G ASN superpopulation calls to see if we see a good amount of recapitulation, and if so, i'll put them in unvalidated comparisons. Minor chances to the TableCodec and TableFeatures to allow for this (the codec can sometimes drop a column, and the feature now allows you to grab on to its header). --- .../sting/gatk/refdata/features/table/TableCodec.java | 0 .../sting/gatk/refdata/features/table/TableFeature.java | 6 +++++- .../broadinstitute/sting/utils/variantcontext/Allele.java | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) mode change 100644 => 100755 public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableCodec.java mode change 100644 => 100755 public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableFeature.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableCodec.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableCodec.java old mode 100644 new mode 100755 diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableFeature.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableFeature.java old mode 100644 new mode 100755 index 6ff0384a0..4b4ebe450 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableFeature.java +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableFeature.java @@ -55,10 +55,14 @@ public class TableFeature implements Feature { } public List getAllValues() { - return getValuesTo(values.size()-1); + return getValuesTo(values.size()); } public List getValuesTo(int columnPosition) { return values.subList(0,columnPosition); } + + public List getHeader() { + return keys; + } } diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/Allele.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/Allele.java index a9ba46159..901de6fae 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/Allele.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/Allele.java @@ -108,7 +108,7 @@ public class Allele implements Comparable { this.bases = bases; if ( ! acceptableAlleleBases(bases) ) - throw new IllegalArgumentException("Unexpected base in allele bases " + new String(bases)); + throw new IllegalArgumentException("Unexpected base in allele bases \'" + new String(bases)+"\'"); } private Allele(String bases, boolean isRef) { From e3748675dbd518042ad67cfc653c7f1a5f89b327 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Mon, 11 Jul 2011 17:40:45 -0400 Subject: [PATCH 55/59] Support for VCF 4.1 header counts --- .../annotator/DepthPerAlleleBySample.java | 3 +- .../ReadDepthAndAllelicFractionBySample.java | 5 +- .../gatk/walkers/annotator/SampleList.java | 3 +- .../utils/codecs/vcf/StandardVCFWriter.java | 11 +-- .../codecs/vcf/VCFCompoundHeaderLine.java | 91 +++++++++++++++---- .../sting/utils/codecs/vcf/VCFConstants.java | 2 + .../utils/codecs/vcf/VCFFormatHeaderLine.java | 4 + .../utils/codecs/vcf/VCFHeaderLineCount.java | 8 ++ .../utils/codecs/vcf/VCFInfoHeaderLine.java | 4 + 9 files changed, 101 insertions(+), 30 deletions(-) create mode 100644 public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFHeaderLineCount.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthPerAlleleBySample.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthPerAlleleBySample.java index 754d28dfd..ee66b50ee 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthPerAlleleBySample.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthPerAlleleBySample.java @@ -1,5 +1,6 @@ package org.broadinstitute.sting.gatk.walkers.annotator; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineCount; import org.broadinstitute.sting.utils.variantcontext.Allele; import org.broadinstitute.sting.utils.variantcontext.Genotype; import org.broadinstitute.sting.utils.variantcontext.VariantContext; @@ -142,5 +143,5 @@ public class DepthPerAlleleBySample implements GenotypeAnnotation, StandardAnnot // public String getIndelBases() public List getKeyNames() { return Arrays.asList("AD"); } - public List getDescriptions() { return Arrays.asList(new VCFFormatHeaderLine(getKeyNames().get(0), VCFCompoundHeaderLine.UNBOUNDED, VCFHeaderLineType.Integer, "Allelic depths for the ref and alt alleles in the order listed")); } + public List getDescriptions() { return Arrays.asList(new VCFFormatHeaderLine(getKeyNames().get(0), VCFHeaderLineCount.UNBOUNDED, VCFHeaderLineType.Integer, "Allelic depths for the ref and alt alleles in the order listed")); } } \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ReadDepthAndAllelicFractionBySample.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ReadDepthAndAllelicFractionBySample.java index f287549bb..a670532af 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ReadDepthAndAllelicFractionBySample.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ReadDepthAndAllelicFractionBySample.java @@ -29,6 +29,7 @@ import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.GenotypeAnnotation; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineCount; import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; import org.broadinstitute.sting.utils.pileup.PileupElement; import org.broadinstitute.sting.utils.pileup.ReadBackedExtendedEventPileup; @@ -200,8 +201,8 @@ public class ReadDepthAndAllelicFractionBySample implements GenotypeAnnotation { 1, VCFHeaderLineType.Integer, "Total read depth per sample, including MQ0"), - new VCFFormatHeaderLine(getKeyNames().get(1), - VCFCompoundHeaderLine.UNBOUNDED, + new VCFFormatHeaderLine(getKeyNames().get(1), + VCFHeaderLineCount.UNBOUNDED, VCFHeaderLineType.Float, "Fractions of reads (excluding MQ0 from both ref and alt) supporting each reported alternative allele, per sample")); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SampleList.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SampleList.java index 82f16be42..e2fd2a3d4 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SampleList.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SampleList.java @@ -25,6 +25,7 @@ package org.broadinstitute.sting.gatk.walkers.annotator; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineCount; import org.broadinstitute.sting.utils.variantcontext.Genotype; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineType; @@ -65,5 +66,5 @@ public class SampleList implements InfoFieldAnnotation { public List getKeyNames() { return Arrays.asList("Samples"); } - public List getDescriptions() { return Arrays.asList(new VCFInfoHeaderLine("Samples", VCFInfoHeaderLine.UNBOUNDED, VCFHeaderLineType.String, "List of polymorphic samples")); } + public List getDescriptions() { return Arrays.asList(new VCFInfoHeaderLine("Samples", VCFHeaderLineCount.UNBOUNDED, VCFHeaderLineType.String, "List of polymorphic samples")); } } diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java index 31251c089..230773310 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java @@ -360,14 +360,7 @@ public class StandardVCFWriter implements VCFWriter { if ( !entry.getValue().equals("") ) { int numVals = 1; VCFInfoHeaderLine metaData = mHeader.getInfoHeaderLine(key); - if ( metaData != null ) - numVals = metaData.getCount(); - - // take care of unbounded encoding - if ( numVals == VCFInfoHeaderLine.UNBOUNDED ) - numVals = 1; - - if ( numVals > 0 ) { + if ( metaData != null && (metaData.getCountType() != VCFHeaderLineCount.INTEGER || metaData.getCount() > 0) ) { mWriter.write("="); mWriter.write(entry.getValue()); } @@ -423,7 +416,7 @@ public class StandardVCFWriter implements VCFWriter { VCFFormatHeaderLine metaData = mHeader.getFormatHeaderLine(key); if ( metaData != null ) { - int numInFormatField = metaData.getCount(); + int numInFormatField = metaData.getCount(vc.getAlternateAlleles().size()); if ( numInFormatField > 1 && val.equals(VCFConstants.MISSING_VALUE_v4) ) { // If we have a missing field but multiple values are expected, we need to construct a new string with all fields. // For example, if Number=2, the string has to be ".,." diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCompoundHeaderLine.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCompoundHeaderLine.java index a799161ad..49f9ab184 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCompoundHeaderLine.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCompoundHeaderLine.java @@ -24,6 +24,8 @@ package org.broadinstitute.sting.utils.codecs.vcf; +import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; + import java.util.Arrays; import java.util.LinkedHashMap; import java.util.Map; @@ -43,26 +45,43 @@ public abstract class VCFCompoundHeaderLine extends VCFHeaderLine implements VCF // the field types private String name; - private int count; + private int count = -1; + private VCFHeaderLineCount countType; private String description; private VCFHeaderLineType type; // access methods public String getName() { return name; } - public int getCount() { return count; } public String getDescription() { return description; } public VCFHeaderLineType getType() { return type; } + public VCFHeaderLineCount getCountType() { return countType; } + public int getCount() { + if ( countType != VCFHeaderLineCount.INTEGER ) + throw new ReviewedStingException("Asking for header line count when type is not an integer"); + return count; + } - // - public void setNumberToUnbounded() { this.count = UNBOUNDED; } + // utility method + public int getCount(int numAltAlleles) { + int myCount; + switch ( countType ) { + case INTEGER: myCount = count; break; + case UNBOUNDED: myCount = -1; break; + case A: myCount = numAltAlleles; break; + case G: myCount = ((numAltAlleles + 1) * (numAltAlleles + 2) / 2); break; + default: throw new ReviewedStingException("Unknown count type: " + countType); + } + return myCount; + } + + public void setNumberToUnbounded() { + countType = VCFHeaderLineCount.UNBOUNDED; + count = -1; + } // our type of line, i.e. format, info, etc private final SupportedHeaderLineType lineType; - // line numerical values are allowed to be unbounded (or unknown), which is - // marked with a dot (.) - public static final int UNBOUNDED = -1; // the value we store internally for unbounded types - /** * create a VCF format header line * @@ -74,6 +93,7 @@ public abstract class VCFCompoundHeaderLine extends VCFHeaderLine implements VCF protected VCFCompoundHeaderLine(String name, int count, VCFHeaderLineType type, String description, SupportedHeaderLineType lineType) { super(lineType.toString(), ""); this.name = name; + this.countType = VCFHeaderLineCount.INTEGER; this.count = count; this.type = type; this.description = description; @@ -81,6 +101,24 @@ public abstract class VCFCompoundHeaderLine extends VCFHeaderLine implements VCF validate(); } + /** + * create a VCF format header line + * + * @param name the name for this header line + * @param count the count type for this header line + * @param type the type for this header line + * @param description the description for this header line + */ + protected VCFCompoundHeaderLine(String name, VCFHeaderLineCount count, VCFHeaderLineType type, String description, SupportedHeaderLineType lineType) { + super(lineType.toString(), ""); + this.name = name; + this.countType = count; + this.type = type; + this.description = description; + this.lineType = lineType; + validate(); + } + /** * create a VCF format header line * @@ -92,9 +130,22 @@ public abstract class VCFCompoundHeaderLine extends VCFHeaderLine implements VCF super(lineType.toString(), ""); Map mapping = VCFHeaderLineTranslator.parseLine(version,line, Arrays.asList("ID","Number","Type","Description")); name = mapping.get("ID"); - count = (version == VCFHeaderVersion.VCF4_0 || version == VCFHeaderVersion.VCF4_1) ? - mapping.get("Number").equals(VCFConstants.UNBOUNDED_ENCODING_v4) ? UNBOUNDED : Integer.valueOf(mapping.get("Number")) : - mapping.get("Number").equals(VCFConstants.UNBOUNDED_ENCODING_v3) ? UNBOUNDED : Integer.valueOf(mapping.get("Number")); + count = -1; + final String numberStr = mapping.get("Number"); + if ( numberStr.equals(VCFConstants.PER_ALLELE_COUNT) ) { + countType = VCFHeaderLineCount.A; + } else if ( numberStr.equals(VCFConstants.PER_GENOTYPE_COUNT) ) { + countType = VCFHeaderLineCount.G; + } else if ( ((version == VCFHeaderVersion.VCF4_0 || version == VCFHeaderVersion.VCF4_1) && + numberStr.equals(VCFConstants.UNBOUNDED_ENCODING_v4)) || + ((version == VCFHeaderVersion.VCF3_2 || version == VCFHeaderVersion.VCF3_3) && + numberStr.equals(VCFConstants.UNBOUNDED_ENCODING_v3)) ) { + countType = VCFHeaderLineCount.UNBOUNDED; + } else { + countType = VCFHeaderLineCount.INTEGER; + count = Integer.valueOf(numberStr); + + } type = VCFHeaderLineType.valueOf(mapping.get("Type")); if (type == VCFHeaderLineType.Flag && !allowFlagValues()) throw new IllegalArgumentException("Flag is an unsupported type for this kind of field"); @@ -121,7 +172,15 @@ public abstract class VCFCompoundHeaderLine extends VCFHeaderLine implements VCF protected String toStringEncoding() { Map map = new LinkedHashMap(); map.put("ID", name); - map.put("Number", count == UNBOUNDED ? VCFConstants.UNBOUNDED_ENCODING_v4 : count); + Object number; + switch ( countType ) { + case A: number = VCFConstants.PER_ALLELE_COUNT; break; + case G: number = VCFConstants.PER_GENOTYPE_COUNT; break; + case UNBOUNDED: number = VCFConstants.UNBOUNDED_ENCODING_v4; break; + case INTEGER: + default: number = count; + } + map.put("Number", number); map.put("Type", type); map.put("Description", description); return lineType.toString() + "=" + VCFHeaderLine.toStringEncoding(map); @@ -136,15 +195,13 @@ public abstract class VCFCompoundHeaderLine extends VCFHeaderLine implements VCF if ( !(o instanceof VCFCompoundHeaderLine) ) return false; VCFCompoundHeaderLine other = (VCFCompoundHeaderLine)o; - return name.equals(other.name) && - count == other.count && - description.equals(other.description) && - type == other.type && - lineType == other.lineType; + return equalsExcludingDescription(other) && + description.equals(other.description); } public boolean equalsExcludingDescription(VCFCompoundHeaderLine other) { return count == other.count && + countType == other.countType && type == other.type && lineType == other.lineType && name.equals(other.name); diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFConstants.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFConstants.java index 695c46c27..91cf86c70 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFConstants.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFConstants.java @@ -99,6 +99,8 @@ public final class VCFConstants { public static final String MISSING_DEPTH_v3 = "-1"; public static final String UNBOUNDED_ENCODING_v4 = "."; public static final String UNBOUNDED_ENCODING_v3 = "-1"; + public static final String PER_ALLELE_COUNT = "A"; + public static final String PER_GENOTYPE_COUNT = "G"; public static final String EMPTY_ALLELE = "."; public static final String EMPTY_GENOTYPE = "./."; public static final double MAX_GENOTYPE_QUAL = 99.0; diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFFormatHeaderLine.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFFormatHeaderLine.java index 352be3e97..f68cb670b 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFFormatHeaderLine.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFFormatHeaderLine.java @@ -16,6 +16,10 @@ public class VCFFormatHeaderLine extends VCFCompoundHeaderLine { throw new IllegalArgumentException("Flag is an unsupported type for format fields"); } + public VCFFormatHeaderLine(String name, VCFHeaderLineCount count, VCFHeaderLineType type, String description) { + super(name, count, type, description, SupportedHeaderLineType.INFO); + } + protected VCFFormatHeaderLine(String line, VCFHeaderVersion version) { super(line, version, SupportedHeaderLineType.FORMAT); } diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFHeaderLineCount.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFHeaderLineCount.java new file mode 100644 index 000000000..d615c7c78 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFHeaderLineCount.java @@ -0,0 +1,8 @@ +package org.broadinstitute.sting.utils.codecs.vcf; + +/** + * the count encodings we use for fields in VCF header lines + */ +public enum VCFHeaderLineCount { + INTEGER, A, G, UNBOUNDED; +} diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFInfoHeaderLine.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFInfoHeaderLine.java index 135a5c1a1..9b20f38a1 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFInfoHeaderLine.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFInfoHeaderLine.java @@ -13,6 +13,10 @@ public class VCFInfoHeaderLine extends VCFCompoundHeaderLine { super(name, count, type, description, SupportedHeaderLineType.INFO); } + public VCFInfoHeaderLine(String name, VCFHeaderLineCount count, VCFHeaderLineType type, String description) { + super(name, count, type, description, SupportedHeaderLineType.INFO); + } + protected VCFInfoHeaderLine(String line, VCFHeaderVersion version) { super(line, version, SupportedHeaderLineType.INFO); } From e93052a51e88ddb77c8ce71cf6a14a449ca3b1aa Mon Sep 17 00:00:00 2001 From: Khalid Shakir Date: Mon, 11 Jul 2011 19:17:58 -0400 Subject: [PATCH 56/59] When generating the QGraph, don't regenerate if there aren't scatter/gather jobs. Fixed a display issue with the number of milliseconds that Queue has tried to contact LSF. --- .../sting/queue/engine/QGraph.scala | 44 ++++++++++--------- .../queue/engine/lsf/Lsf706JobRunner.scala | 6 +-- 2 files changed, 26 insertions(+), 24 deletions(-) diff --git a/public/scala/src/org/broadinstitute/sting/queue/engine/QGraph.scala b/public/scala/src/org/broadinstitute/sting/queue/engine/QGraph.scala index bfcc4d48c..8ed3f84c1 100755 --- a/public/scala/src/org/broadinstitute/sting/queue/engine/QGraph.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/engine/QGraph.scala @@ -138,30 +138,32 @@ class QGraph extends Logging { validate() if (running && numMissingValues == 0) { - logger.info("Generating scatter gather jobs.") val scatterGathers = jobGraph.edgeSet.filter(edge => scatterGatherable(edge)) + if (!scatterGathers.isEmpty) { + logger.info("Generating scatter gather jobs.") - var addedFunctions = List.empty[QFunction] - for (scatterGather <- scatterGathers) { - val functions = scatterGather.asInstanceOf[FunctionEdge] - .function.asInstanceOf[ScatterGatherableFunction] - .generateFunctions() - addedFunctions ++= functions + var addedFunctions = List.empty[QFunction] + for (scatterGather <- scatterGathers) { + val functions = scatterGather.asInstanceOf[FunctionEdge] + .function.asInstanceOf[ScatterGatherableFunction] + .generateFunctions() + addedFunctions ++= functions + } + + logger.info("Removing original jobs.") + this.jobGraph.removeAllEdges(scatterGathers) + prune() + + logger.info("Adding scatter gather jobs.") + addedFunctions.foreach(function => if (running) this.add(function)) + + logger.info("Regenerating graph.") + fill + val scatterGatherDotFile = if (settings.expandedDotFile != null) settings.expandedDotFile else settings.dotFile + if (scatterGatherDotFile != null) + renderToDot(scatterGatherDotFile) + validate() } - - logger.info("Removing original jobs.") - this.jobGraph.removeAllEdges(scatterGathers) - prune() - - logger.info("Adding scatter gather jobs.") - addedFunctions.foreach(function => if (running) this.add(function)) - - logger.info("Regenerating graph.") - fill - val scatterGatherDotFile = if (settings.expandedDotFile != null) settings.expandedDotFile else settings.dotFile - if (scatterGatherDotFile != null) - renderToDot(scatterGatherDotFile) - validate() } } diff --git a/public/scala/src/org/broadinstitute/sting/queue/engine/lsf/Lsf706JobRunner.scala b/public/scala/src/org/broadinstitute/sting/queue/engine/lsf/Lsf706JobRunner.scala index 57d133dfe..ac2f036b4 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/engine/lsf/Lsf706JobRunner.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/engine/lsf/Lsf706JobRunner.scala @@ -286,11 +286,11 @@ object Lsf706JobRunner extends Logging { // LSB_SHAREDIR/cluster_name/logdir/lsb.acct (man bacct) // LSB_SHAREDIR/cluster_name/logdir/lsb.events (man bhist) logger.debug("Job Id %s status / exitStatus / exitInfo: ??? / ??? / ???".format(runner.jobId)) - val unknownStatusSeconds = (System.currentTimeMillis - runner.lastStatusUpdate) - if (unknownStatusSeconds > (unknownStatusMaxSeconds * 1000L)) { + val unknownStatusMillis = (System.currentTimeMillis - runner.lastStatusUpdate) + if (unknownStatusMillis > (unknownStatusMaxSeconds * 1000L)) { // Unknown status has been returned for a while now. runner.updateStatus(RunnerStatus.FAILED) - logger.error("Unable to read LSF status for %d minutes: job id %d: %s".format(unknownStatusSeconds/60, runner.jobId, runner.function.description)) + logger.error("Unable to read LSF status for %0.2f minutes: job id %d: %s".format(unknownStatusMillis/(60 * 1000D), runner.jobId, runner.function.description)) } } From 5e593793af43153e844f69c0f0df367b1d56a658 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Mon, 11 Jul 2011 23:10:27 -0400 Subject: [PATCH 57/59] DiffEngine utility function simpleDiffFiles printSummaryReport now uses GATKReport for nice formating Moved print formatting arguments into inner class provided to printing functions themselves, not the class BAMDiffableReader only reads 1000 entries to avoid performance issue. Work around for BAM files with non-unique names Uncommented all of the incorrectly commented out CombineVariants integrationtests BaseTest now uses DiffEngine to provide inline differences to VCF and BAM files --- .../org/broadinstitute/sting/BaseTest.java | 15 +- .../CombineVariantsIntegrationTest.java | 142 +++++++++--------- 2 files changed, 83 insertions(+), 74 deletions(-) diff --git a/public/java/test/org/broadinstitute/sting/BaseTest.java b/public/java/test/org/broadinstitute/sting/BaseTest.java index b469c8a41..b3e422ba9 100755 --- a/public/java/test/org/broadinstitute/sting/BaseTest.java +++ b/public/java/test/org/broadinstitute/sting/BaseTest.java @@ -4,6 +4,7 @@ import org.apache.commons.io.FileUtils; import org.apache.log4j.*; import org.apache.log4j.spi.LoggingEvent; import org.broadinstitute.sting.commandline.CommandLineUtils; +import org.broadinstitute.sting.gatk.walkers.diffengine.DiffEngine; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.testng.Assert; @@ -334,11 +335,14 @@ public abstract class BaseTest { if (parameterize || expectedMD5.equals("")) { // Don't assert - } else { - Assert.assertEquals(filemd5sum, expectedMD5, name + " Mismatching MD5s"); + } else if ( filemd5sum.equals(expectedMD5) ) { System.out.println(String.format(" => %s PASSED", name)); + } else { + Assert.fail(String.format("%s has mismatching MD5s: expected=%s observed=%s", name, expectedMD5, filemd5sum)); } + + return filemd5sum; } @@ -381,7 +385,12 @@ public abstract class BaseTest { System.out.printf("##### Path to calculated file (MD5=%s): %s%n", filemd5sum, pathToFileMD5File); System.out.printf("##### Diff command: diff %s %s%n", pathToExpectedMD5File, pathToFileMD5File); - // todo -- add support for simple inline display of the first N differences for text file + // inline differences + DiffEngine.SummaryReportParams params = new DiffEngine.SummaryReportParams(System.out, 20, 10, 0); + boolean success = DiffEngine.simpleDiffFiles(new File(pathToExpectedMD5File), new File(pathToFileMD5File), params); + if ( success ) + System.out.printf("Note that the above list is not comprehensive. At most 20 lines of output, and 10 specific differences will be listed. Please use -T DiffObjects -R public/testdata/exampleFASTA.fasta -m %s -t %s to explore the differences more freely%n", + pathToExpectedMD5File, pathToFileMD5File); } } diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java index 33a20f7b5..600718aa0 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java @@ -34,76 +34,76 @@ import java.util.Arrays; * Tests CombineVariants */ public class CombineVariantsIntegrationTest extends WalkerTest { -// public static String baseTestString(String args) { -// return "-T CombineVariants -NO_HEADER -L 1:1-50,000,000 -o %s -R " + b36KGReference + args; -// } -// -// public void test1InOut(String file, String md5, boolean vcf3) { -// test1InOut(file, md5, "", vcf3); -// } -// -// public void test1InOut(String file, String md5, String args, boolean vcf3) { -// WalkerTestSpec spec = new WalkerTestSpec( -// baseTestString(" -priority v1 -B:v1,VCF" + (vcf3 ? "3 " : " ") + validationDataLocation + file + args), -// 1, -// Arrays.asList(md5)); -// executeTest("testInOut1--" + file, spec); -// } -// -// public void combine2(String file1, String file2, String args, String md5, boolean vcf3) { -// WalkerTestSpec spec = new WalkerTestSpec( -// baseTestString(" -priority v1,v2 -B:v1,VCF" + (vcf3 ? "3 " : " ") + validationDataLocation + file1 + " -B:v2,VCF" + (vcf3 ? "3 " : " ") + validationDataLocation + file2 + args), -// 1, -// Arrays.asList(md5)); -// executeTest("combine2 1:" + new File(file1).getName() + " 2:" + new File(file2).getName(), spec); -// } -// -// public void combineSites(String args, String md5) { -// String file1 = "1000G_omni2.5.b37.sites.vcf"; -// String file2 = "hapmap_3.3.b37.sites.vcf"; -// WalkerTestSpec spec = new WalkerTestSpec( -// "-T CombineVariants -NO_HEADER -o %s -R " + b37KGReference -// + " -L 1:1-10,000,000 -B:omni,VCF " + validationDataLocation + file1 -// + " -B:hm3,VCF " + validationDataLocation + file2 + args, -// 1, -// Arrays.asList(md5)); -// executeTest("combineSites 1:" + new File(file1).getName() + " 2:" + new File(file2).getName() + " args = " + args, spec); -// } -// -// -// @Test public void test1SNP() { test1InOut("pilot2.snps.vcf4.genotypes.vcf", "2117fff6e0d182cd20be508e9661829c", true); } -// @Test public void test2SNP() { test1InOut("pilot2.snps.vcf4.genotypes.vcf", "2cfaf7af3dd119df08b8a9c1f72e2f93", " -setKey foo", true); } -// @Test public void test3SNP() { test1InOut("pilot2.snps.vcf4.genotypes.vcf", "1474ac0fde2ce42a3c24f1c97eab333e", " -setKey null", true); } -// @Test public void testOfficialCEUPilotCalls() { test1InOut("CEU.trio.2010_03.genotypes.vcf.gz", "7fc66df048a0ab08cf507906e1d4a308", false); } // official project VCF files in tabix format -// -// @Test public void test1Indel1() { test1InOut("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "ec9715f53dbf4531570557c212822f12", false); } -// @Test public void test1Indel2() { test1InOut("CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "f1072be5f5c6ee810276d9ca6537224d", false); } -// -// @Test public void combineTrioCalls() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", "", "b77a1eec725201d9d8e74ee0c45638d3", false); } // official project VCF files in tabix format -// @Test public void combineTrioCallsMin() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", " -minimalVCF", "802977fdfd2f4905b501bb06800f60af", false); } // official project VCF files in tabix format -// @Test public void combine2Indels() { combine2("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "a67157287dd2b24b5cdf7ebf8fcbbe9a", false); } -// -// @Test public void combineSNPsAndIndels() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "e1f4718a179f1196538a33863da04f53", false); } -// -// @Test public void uniqueSNPs() { combine2("pilot2.snps.vcf4.genotypes.vcf", "yri.trio.gatk_glftrio.intersection.annotated.filtered.chr1.vcf", "", "b3783384b7c8e877b971033e90beba48", true); } -// -// @Test public void omniHM3Union() { combineSites(" -filteredRecordsMergeType KEEP_IF_ANY_UNFILTERED", "902e541c87caa72134db6293fc46f0ad"); } -// @Test public void omniHM3Intersect() { combineSites(" -filteredRecordsMergeType KEEP_IF_ALL_UNFILTERED", "f339ad4bb5863b58b9c919ce7d040bb9"); } -// -// @Test public void threeWayWithRefs() { -// WalkerTestSpec spec = new WalkerTestSpec( -// baseTestString(" -B:NA19240_BGI,VCF "+validationDataLocation+"NA19240.BGI.RG.vcf" + -// " -B:NA19240_ILLUMINA,VCF "+validationDataLocation+"NA19240.ILLUMINA.RG.vcf" + -// " -B:NA19240_WUGSC,VCF "+validationDataLocation+"NA19240.WUGSC.RG.vcf" + -// " -B:denovoInfo,VCF "+validationDataLocation+"yri_merged_validation_data_240610.annotated.b36.vcf" + -// " -setKey centerSet" + -// " -filteredRecordsMergeType KEEP_IF_ANY_UNFILTERED" + -// " -priority NA19240_BGI,NA19240_ILLUMINA,NA19240_WUGSC,denovoInfo" + -// " -genotypeMergeOptions UNIQUIFY -L 1"), -// 1, -// Arrays.asList("a07995587b855f3214fb71940bf23c0f")); -// executeTest("threeWayWithRefs", spec); -// } + public static String baseTestString(String args) { + return "-T CombineVariants -NO_HEADER -L 1:1-50,000,000 -o %s -R " + b36KGReference + args; + } + + public void test1InOut(String file, String md5, boolean vcf3) { + test1InOut(file, md5, "", vcf3); + } + + public void test1InOut(String file, String md5, String args, boolean vcf3) { + WalkerTestSpec spec = new WalkerTestSpec( + baseTestString(" -priority v1 -B:v1,VCF" + (vcf3 ? "3 " : " ") + validationDataLocation + file + args), + 1, + Arrays.asList(md5)); + executeTest("testInOut1--" + file, spec); + } + + public void combine2(String file1, String file2, String args, String md5, boolean vcf3) { + WalkerTestSpec spec = new WalkerTestSpec( + baseTestString(" -priority v1,v2 -B:v1,VCF" + (vcf3 ? "3 " : " ") + validationDataLocation + file1 + " -B:v2,VCF" + (vcf3 ? "3 " : " ") + validationDataLocation + file2 + args), + 1, + Arrays.asList(md5)); + executeTest("combine2 1:" + new File(file1).getName() + " 2:" + new File(file2).getName(), spec); + } + + public void combineSites(String args, String md5) { + String file1 = "1000G_omni2.5.b37.sites.vcf"; + String file2 = "hapmap_3.3.b37.sites.vcf"; + WalkerTestSpec spec = new WalkerTestSpec( + "-T CombineVariants -NO_HEADER -o %s -R " + b37KGReference + + " -L 1:1-10,000,000 -B:omni,VCF " + validationDataLocation + file1 + + " -B:hm3,VCF " + validationDataLocation + file2 + args, + 1, + Arrays.asList(md5)); + executeTest("combineSites 1:" + new File(file1).getName() + " 2:" + new File(file2).getName() + " args = " + args, spec); + } + + + @Test public void test1SNP() { test1InOut("pilot2.snps.vcf4.genotypes.vcf", "2117fff6e0d182cd20be508e9661829c", true); } + @Test public void test2SNP() { test1InOut("pilot2.snps.vcf4.genotypes.vcf", "2cfaf7af3dd119df08b8a9c1f72e2f93", " -setKey foo", true); } + @Test public void test3SNP() { test1InOut("pilot2.snps.vcf4.genotypes.vcf", "1474ac0fde2ce42a3c24f1c97eab333e", " -setKey null", true); } + @Test public void testOfficialCEUPilotCalls() { test1InOut("CEU.trio.2010_03.genotypes.vcf.gz", "7fc66df048a0ab08cf507906e1d4a308", false); } // official project VCF files in tabix format + + @Test public void test1Indel1() { test1InOut("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "ec9715f53dbf4531570557c212822f12", false); } + @Test public void test1Indel2() { test1InOut("CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "f1072be5f5c6ee810276d9ca6537224d", false); } + + @Test public void combineTrioCalls() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", "", "b77a1eec725201d9d8e74ee0c45638d3", false); } // official project VCF files in tabix format + @Test public void combineTrioCallsMin() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", " -minimalVCF", "802977fdfd2f4905b501bb06800f60af", false); } // official project VCF files in tabix format + @Test public void combine2Indels() { combine2("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "a67157287dd2b24b5cdf7ebf8fcbbe9a", false); } + + @Test public void combineSNPsAndIndels() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "e1f4718a179f1196538a33863da04f53", false); } + + @Test public void uniqueSNPs() { combine2("pilot2.snps.vcf4.genotypes.vcf", "yri.trio.gatk_glftrio.intersection.annotated.filtered.chr1.vcf", "", "b3783384b7c8e877b971033e90beba48", true); } + + @Test public void omniHM3Union() { combineSites(" -filteredRecordsMergeType KEEP_IF_ANY_UNFILTERED", "902e541c87caa72134db6293fc46f0ad"); } + @Test public void omniHM3Intersect() { combineSites(" -filteredRecordsMergeType KEEP_IF_ALL_UNFILTERED", "f339ad4bb5863b58b9c919ce7d040bb9"); } + + @Test public void threeWayWithRefs() { + WalkerTestSpec spec = new WalkerTestSpec( + baseTestString(" -B:NA19240_BGI,VCF "+validationDataLocation+"NA19240.BGI.RG.vcf" + + " -B:NA19240_ILLUMINA,VCF "+validationDataLocation+"NA19240.ILLUMINA.RG.vcf" + + " -B:NA19240_WUGSC,VCF "+validationDataLocation+"NA19240.WUGSC.RG.vcf" + + " -B:denovoInfo,VCF "+validationDataLocation+"yri_merged_validation_data_240610.annotated.b36.vcf" + + " -setKey centerSet" + + " -filteredRecordsMergeType KEEP_IF_ANY_UNFILTERED" + + " -priority NA19240_BGI,NA19240_ILLUMINA,NA19240_WUGSC,denovoInfo" + + " -genotypeMergeOptions UNIQUIFY -L 1"), + 1, + Arrays.asList("a07995587b855f3214fb71940bf23c0f")); + executeTest("threeWayWithRefs", spec); + } // complex examples with filtering, indels, and multiple alleles @@ -119,7 +119,7 @@ public class CombineVariantsIntegrationTest extends WalkerTest { executeTest("combineComplexSites 1:" + new File(file1).getName() + " 2:" + new File(file2).getName() + " args = " + args, spec); } - @Test public void complexTestFull() { combineComplexSites("", "64b991fd3850f83614518f7d71f0532f"); } +// @Test public void complexTestFull() { combineComplexSites("", "64b991fd3850f83614518f7d71f0532f"); } @Test public void complexTestMinimal() { combineComplexSites(" -minimalVCF", "0db9ef50fe54b60426474273d7c7fa99"); } @Test public void complexTestSitesOnly() { combineComplexSites(" -sites_only", "d20acb3d53ba0a02ce92d540ebeda2a9"); } @Test public void complexTestSitesOnlyMinimal() { combineComplexSites(" -sites_only -minimalVCF", "8d1b3d120515f8b56b5a0d10bc5da713"); } From 893cc2e103e25daf01018c86382869ddac0e1f4a Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Mon, 11 Jul 2011 23:15:08 -0400 Subject: [PATCH 58/59] Making the package public, so there's no dependances from public -> private --- .../walkers/diffengine/BAMDiffableReader.java | 122 +++++ .../gatk/walkers/diffengine/DiffElement.java | 118 +++++ .../gatk/walkers/diffengine/DiffEngine.java | 423 ++++++++++++++++++ .../gatk/walkers/diffengine/DiffNode.java | 239 ++++++++++ .../walkers/diffengine/DiffObjectsWalker.java | 113 +++++ .../gatk/walkers/diffengine/DiffValue.java | 90 ++++ .../walkers/diffengine/DiffableReader.java | 50 +++ .../gatk/walkers/diffengine/Difference.java | 58 +++ .../walkers/diffengine/VCFDiffableReader.java | 119 +++++ 9 files changed, 1332 insertions(+) create mode 100644 public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/BAMDiffableReader.java create mode 100644 public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffElement.java create mode 100644 public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffEngine.java create mode 100644 public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffNode.java create mode 100644 public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffObjectsWalker.java create mode 100644 public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffValue.java create mode 100644 public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffableReader.java create mode 100644 public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/Difference.java create mode 100644 public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/VCFDiffableReader.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/BAMDiffableReader.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/BAMDiffableReader.java new file mode 100644 index 000000000..f7a395d9d --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/BAMDiffableReader.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.diffengine; + +import net.sf.samtools.*; +import net.sf.samtools.util.BlockCompressedInputStream; +import org.broad.tribble.readers.AsciiLineReader; +import org.broad.tribble.readers.LineReader; +import org.broadinstitute.sting.utils.codecs.vcf.VCFCodec; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeader; +import org.broadinstitute.sting.utils.variantcontext.Genotype; +import org.broadinstitute.sting.utils.variantcontext.VariantContext; + +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import java.util.zip.GZIPInputStream; + + +/** + * Created by IntelliJ IDEA. + * User: depristo + * Date: 7/4/11 + * Time: 1:09 PM + * + * Class implementing diffnode reader for VCF + */ +public class BAMDiffableReader implements DiffableReader { + private final static int MAX_RECORDS_TO_READ = 1000; + @Override + public String getName() { return "BAM"; } + + @Override + public DiffElement readFromFile(File file) { + final SAMFileReader reader = new SAMFileReader(file, null); // null because we don't want it to look for the index + reader.setValidationStringency(SAMFileReader.ValidationStringency.SILENT); + + DiffNode root = DiffNode.rooted(file.getName()); + SAMRecordIterator iterator = reader.iterator(); + + int count = 0; + while ( iterator.hasNext() ) { + if ( count++ > MAX_RECORDS_TO_READ ) + break; + final SAMRecord record = iterator.next(); + + // name is the read name + first of pair + String name = record.getReadName().replace('.', '_'); + if ( record.getReadPairedFlag() ) { + name += record.getFirstOfPairFlag() ? "_1" : "_2"; + } + + DiffNode readRoot = DiffNode.empty(name, root); + + // add fields + readRoot.add("NAME", record.getReadName()); + readRoot.add("FLAGS", record.getFlags()); + readRoot.add("RNAME", record.getReferenceName()); + readRoot.add("POS", record.getAlignmentStart()); + readRoot.add("MAPQ", record.getMappingQuality()); + readRoot.add("CIGAR", record.getCigarString()); + readRoot.add("RNEXT", record.getMateReferenceName()); + readRoot.add("PNEXT", record.getMateAlignmentStart()); + readRoot.add("TLEN", record.getInferredInsertSize()); + readRoot.add("SEQ", record.getReadString()); + readRoot.add("QUAL", record.getBaseQualityString()); + + for ( SAMRecord.SAMTagAndValue xt : record.getAttributes() ) { + readRoot.add(xt.tag, xt.value); + } + + // add record to root + if ( ! root.hasElement(name) ) + // protect ourselves from malformed files + root.add(readRoot); + } + + reader.close(); + + return root.getBinding(); + } + + @Override + public boolean canRead(File file) { + final byte[] BAM_MAGIC = "BAM\1".getBytes(); + final byte[] buffer = new byte[BAM_MAGIC.length]; + try { + FileInputStream fstream = new FileInputStream(file); + new BlockCompressedInputStream(fstream).read(buffer,0,BAM_MAGIC.length); + return Arrays.equals(buffer, BAM_MAGIC); + } catch ( IOException e ) { + return false; + } catch ( net.sf.samtools.FileTruncatedException e ) { + return false; + } + } +} diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffElement.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffElement.java new file mode 100644 index 000000000..eff24bb88 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffElement.java @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.diffengine; + +import com.google.java.contract.*; +import org.broadinstitute.sting.utils.Utils; +import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; + +/** + * Created by IntelliJ IDEA. + * User: depristo + * Date: 7/4/11 + * Time: 12:55 PM + * + * An interface that must be implemented to allow us to calculate differences + * between structured objects + */ +@Invariant({ + "name != null", + "value != null", + "parent != null || name.equals(\"ROOT\")", + "value == null || value.getBinding() == this"}) +public class DiffElement { + public final static DiffElement ROOT = new DiffElement(); + + final private String name; + final private DiffElement parent; + final private DiffValue value; + + /** + * For ROOT only + */ + private DiffElement() { + this.name = "ROOT"; + this.parent = null; + this.value = new DiffValue(this, "ROOT"); + } + + @Requires({"name != null", "parent != null", "value != null"}) + public DiffElement(String name, DiffElement parent, DiffValue value) { + if ( name.equals("ROOT") ) throw new IllegalArgumentException("Cannot use reserved name ROOT"); + this.name = name; + this.parent = parent; + this.value = value; + this.value.setBinding(this); + } + + @Ensures({"result != null"}) + public String getName() { + return name; + } + + public DiffElement getParent() { + return parent; + } + + @Ensures({"result != null"}) + public DiffValue getValue() { + return value; + } + + public boolean isRoot() { return this == ROOT; } + + @Ensures({"result != null"}) + @Override + public String toString() { + return getName() + "=" + getValue().toString(); + } + + public String toString(int offset) { + return (offset > 0 ? Utils.dupString(' ', offset) : 0) + getName() + "=" + getValue().toString(offset); + } + + @Ensures({"result != null"}) + public final String fullyQualifiedName() { + if ( isRoot() ) + return ""; + else if ( parent.isRoot() ) + return name; + else + return parent.fullyQualifiedName() + "." + name; + } + + @Ensures({"result != null"}) + public String toOneLineString() { + return getName() + "=" + getValue().toOneLineString(); + } + + @Ensures({"result != null"}) + public DiffNode getValueAsNode() { + if ( getValue().isCompound() ) + return (DiffNode)getValue(); + else + throw new ReviewedStingException("Illegal request conversion of a DiffValue into a DiffNode: " + this); + } +} diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffEngine.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffEngine.java new file mode 100644 index 000000000..ba2713bff --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffEngine.java @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.diffengine; + +import com.google.java.contract.Requires; +import org.apache.log4j.Logger; +import org.broadinstitute.sting.gatk.report.GATKReport; +import org.broadinstitute.sting.gatk.report.GATKReportTable; +import org.broadinstitute.sting.gatk.walkers.varianteval.stratifications.VariantStratifier; +import org.broadinstitute.sting.utils.Utils; +import org.broadinstitute.sting.utils.classloader.PluginManager; +import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; +import org.broadinstitute.sting.utils.exceptions.UserException; + +import java.io.File; +import java.io.PrintStream; +import java.util.*; + +/** + * Created by IntelliJ IDEA. + * User: depristo + * Date: 7/4/11 + * Time: 12:51 PM + * A generic engine for comparing tree-structured objects + */ +public class DiffEngine { + final protected static Logger logger = Logger.getLogger(DiffEngine.class); + + private final Map readers = new HashMap(); + + public DiffEngine() { + loadDiffableReaders(); + } + + // -------------------------------------------------------------------------------- + // + // difference calculation + // + // -------------------------------------------------------------------------------- + + public List diff(DiffElement master, DiffElement test) { + DiffValue masterValue = master.getValue(); + DiffValue testValue = test.getValue(); + + if ( masterValue.isCompound() && masterValue.isCompound() ) { + return diff(master.getValueAsNode(), test.getValueAsNode()); + } else if ( masterValue.isAtomic() && testValue.isAtomic() ) { + return diff(masterValue, testValue); + } else { + // structural difference in types. one is node, other is leaf + return Arrays.asList(new Difference(master, test)); + } + } + + public List diff(DiffNode master, DiffNode test) { + Set allNames = new HashSet(master.getElementNames()); + allNames.addAll(test.getElementNames()); + List diffs = new ArrayList(); + + for ( String name : allNames ) { + DiffElement masterElt = master.getElement(name); + DiffElement testElt = test.getElement(name); + if ( masterElt == null && testElt == null ) { + throw new ReviewedStingException("BUG: unexceptedly got two null elements for field: " + name); + } else if ( masterElt == null || testElt == null ) { // if either is null, we are missing a value + // todo -- should one of these be a special MISSING item? + diffs.add(new Difference(masterElt, testElt)); + } else { + diffs.addAll(diff(masterElt, testElt)); + } + } + + return diffs; + } + + public List diff(DiffValue master, DiffValue test) { + if ( master.getValue().equals(test.getValue()) ) { + return Collections.emptyList(); + } else { + return Arrays.asList(new Difference(master.getBinding(), test.getBinding())); + } + } + + // -------------------------------------------------------------------------------- + // + // Summarizing differences + // + // -------------------------------------------------------------------------------- + + /** + * Emits a summary of the diffs to out. Suppose you have the following three differences: + * + * A.X.Z:1!=2 + * A.Y.Z:3!=4 + * B.X.Z:5!=6 + * + * The above is the itemized list of the differences. The summary looks for common differences + * in the name hierarchy, counts those shared elements, and emits the differences that occur + * in order of decreasing counts. + * + * So, in the above example, what are the shared elements? + * + * A.X.Z and B.X.Z share X.Z, so there's a *.X.Z with count 2 + * A.X.Z, A.Y.Z, and B.X.Z all share *.*.Z, with count 3 + * Each of A.X.Z, A.Y.Z, and B.X.Z are individually unique, with count 1 + * + * So we would emit the following summary: + * + * *.*.Z: 3 + * *.X.Z: 2 + * A.X.Z: 1 [specific difference: 1!=2] + * A.Y.Z: 1 [specific difference: 3!=4] + * B.X.Z: 1 [specific difference: 5!=6] + * + * The algorithm to accomplish this calculation is relatively simple. Start with all of the + * concrete differences. For each pair of differences A1.A2....AN and B1.B2....BN: + * + * find the longest common subsequence Si.Si+1...SN where Ai = Bi = Si + * If i == 0, then there's no shared substructure + * If i > 0, then generate the summarized value X = *.*...Si.Si+1...SN + * if X is a known summary, increment it's count, otherwise set its count to 1 + * + * Not that only pairs of the same length are considered as potentially equivalent + * + * @param params determines how we display the items + * @param diffs + */ + public void reportSummarizedDifferences(List diffs, SummaryReportParams params ) { + printSummaryReport(summarizeDifferences(diffs), params ); + } + + public List summarizeDifferences(List diffs) { + List diffPaths = new ArrayList(diffs.size()); + + for ( Difference diff1 : diffs ) { + diffPaths.add(diffNameToPath(diff1.getFullyQualifiedName())); + } + + return summarizedDifferencesOfPaths(diffPaths); + } + + final protected static String[] diffNameToPath(String diffName) { + return diffName.split("\\."); + } + + protected List summarizedDifferencesOfPaths(List diffPaths) { + Map summaries = new HashMap(); + + // create the initial set of differences + for ( int i = 0; i < diffPaths.size(); i++ ) { + for ( int j = 0; j <= i; j++ ) { + String[] diffPath1 = diffPaths.get(i); + String[] diffPath2 = diffPaths.get(j); + if ( diffPath1.length == diffPath2.length ) { + int lcp = longestCommonPostfix(diffPath1, diffPath2); + String path = lcp > 0 ? summarizedPath(diffPath2, lcp) : Utils.join(".", diffPath2); + addSummary(summaries, path, true); + } + } + } + + // count differences + for ( String[] diffPath : diffPaths ) { + for ( SummarizedDifference sumDiff : summaries.values() ) { + if ( sumDiff.matches(diffPath) ) + addSummary(summaries, sumDiff.getPath(), false); + } + } + + List sortedSummaries = new ArrayList(summaries.values()); + Collections.sort(sortedSummaries); + return sortedSummaries; + } + + private static void addSummary(Map summaries, String path, boolean onlyCatalog) { + if ( summaries.containsKey(path) ) { + if ( ! onlyCatalog ) + summaries.get(path).incCount(); + } else { + SummarizedDifference sumDiff = new SummarizedDifference(path); + summaries.put(sumDiff.getPath(), sumDiff); + } + } + + protected void printSummaryReport(List sortedSummaries, SummaryReportParams params ) { + GATKReport report = new GATKReport(); + final String tableName = "diffences"; + report.addTable(tableName, "Summarized differences between the master and test files.\nSee http://www.broadinstitute.org/gsa/wiki/index.php/DiffObjectsWalker_and_SummarizedDifferences for more information"); + GATKReportTable table = report.getTable(tableName); + table.addPrimaryKey("Difference", true); + table.addColumn("NumberOfOccurrences", 0); + + int count = 0, count1 = 0; + for ( SummarizedDifference diff : sortedSummaries ) { + if ( diff.getCount() < params.minSumDiffToShow ) + // in order, so break as soon as the count is too low + break; + + if ( params.maxItemsToDisplay != 0 && count++ > params.maxItemsToDisplay ) + break; + + if ( diff.getCount() == 1 ) { + count1++; + if ( params.maxCountOneItems != 0 && count1 > params.maxCountOneItems ) + break; + } + + table.set(diff.getPath(), "NumberOfOccurrences", diff.getCount()); + } + + table.write(params.out); + } + + protected static int longestCommonPostfix(String[] diffPath1, String[] diffPath2) { + int i = 0; + for ( ; i < diffPath1.length; i++ ) { + int j = diffPath1.length - i - 1; + if ( ! diffPath1[j].equals(diffPath2[j]) ) + break; + } + return i; + } + + /** + * parts is [A B C D] + * commonPostfixLength: how many parts are shared at the end, suppose its 2 + * We want to create a string *.*.C.D + * + * @param parts + * @param commonPostfixLength + * @return + */ + protected static String summarizedPath(String[] parts, int commonPostfixLength) { + int stop = parts.length - commonPostfixLength; + if ( stop > 0 ) parts = parts.clone(); + for ( int i = 0; i < stop; i++ ) { + parts[i] = "*"; + } + return Utils.join(".", parts); + } + + /** + * TODO -- all of the algorithms above should use SummarizedDifference instead + * TODO -- of some SummarizedDifferences and some low-level String[] + */ + public static class SummarizedDifference implements Comparable { + final String path; // X.Y.Z + final String[] parts; + int count = 0; + + public SummarizedDifference(String path) { + this.path = path; + this.parts = diffNameToPath(path); + } + + public void incCount() { count++; } + + public int getCount() { + return count; + } + + /** + * The fully qualified path object A.B.C etc + * @return + */ + public String getPath() { + return path; + } + + /** + * @return the length of the parts of this summary + */ + public int length() { + return this.parts.length; + } + + /** + * Returns true if the string parts matches this summary. Matches are + * must be equal() everywhere where this summary isn't *. + * @param otherParts + * @return + */ + public boolean matches(String[] otherParts) { + if ( otherParts.length != length() ) + return false; + + // TODO optimization: can start at right most non-star element + for ( int i = 0; i < length(); i++ ) { + String part = parts[i]; + if ( ! part.equals("*") && ! part.equals(otherParts[i]) ) + return false; + } + + return true; + } + + @Override + public String toString() { + return String.format("%s:%d", getPath(), getCount()); + } + + @Override + public int compareTo(SummarizedDifference other) { + // sort first highest to lowest count, then by lowest to highest path + int countCmp = Integer.valueOf(count).compareTo(other.count); + return countCmp != 0 ? -1 * countCmp : path.compareTo(other.path); + } + + + } + + // -------------------------------------------------------------------------------- + // + // plugin manager + // + // -------------------------------------------------------------------------------- + + public void loadDiffableReaders() { + List> drClasses = new PluginManager( DiffableReader.class ).getPlugins(); + + logger.info("Loading diffable modules:"); + for (Class drClass : drClasses ) { + logger.info("\t" + drClass.getSimpleName()); + + try { + DiffableReader dr = drClass.newInstance(); + readers.put(dr.getName(), dr); + } catch (InstantiationException e) { + throw new ReviewedStingException("Unable to instantiate module '" + drClass.getSimpleName() + "'"); + } catch (IllegalAccessException e) { + throw new ReviewedStingException("Illegal access error when trying to instantiate '" + drClass.getSimpleName() + "'"); + } + } + } + + protected Map getReaders() { + return readers; + } + + protected DiffableReader getReader(String name) { + return readers.get(name); + } + + /** + * Returns a reader appropriate for this file, or null if no such reader exists + * @param file + * @return + */ + public DiffableReader findReaderForFile(File file) { + for ( DiffableReader reader : readers.values() ) + if (reader.canRead(file) ) + return reader; + + return null; + } + + /** + * Returns true if reader appropriate for this file, or false if no such reader exists + * @param file + * @return + */ + public boolean canRead(File file) { + return findReaderForFile(file) != null; + } + + public DiffElement createDiffableFromFile(File file) { + DiffableReader reader = findReaderForFile(file); + if ( reader == null ) + throw new UserException("Unsupported file type: " + file); + else + return reader.readFromFile(file); + } + + public static boolean simpleDiffFiles(File masterFile, File testFile, DiffEngine.SummaryReportParams params) { + DiffEngine diffEngine = new DiffEngine(); + + if ( diffEngine.canRead(masterFile) && diffEngine.canRead(testFile) ) { + DiffElement master = diffEngine.createDiffableFromFile(masterFile); + DiffElement test = diffEngine.createDiffableFromFile(testFile); + List diffs = diffEngine.diff(master, test); + diffEngine.reportSummarizedDifferences(diffs, params); + return true; + } else { + return false; + } + } + + public static class SummaryReportParams { + PrintStream out = System.out; + int maxItemsToDisplay = 0; + int maxCountOneItems = 0; + int minSumDiffToShow = 0; + + public SummaryReportParams(PrintStream out, int maxItemsToDisplay, int maxCountOneItems, int minSumDiffToShow) { + this.out = out; + this.maxItemsToDisplay = maxItemsToDisplay; + this.maxCountOneItems = maxCountOneItems; + this.minSumDiffToShow = minSumDiffToShow; + } + } +} diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffNode.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffNode.java new file mode 100644 index 000000000..0720e18c0 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffNode.java @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.diffengine; + +import com.google.java.contract.Requires; +import org.broadinstitute.sting.utils.Utils; +import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; + +import java.util.*; + +/** + * Created by IntelliJ IDEA. + * User: depristo + * Date: 7/4/11 + * Time: 12:55 PM + * + * An interface that must be implemented to allow us to calculate differences + * between structured objects + */ +public class DiffNode extends DiffValue { + private Map getElementMap() { + return (Map)super.getValue(); + } + private static Map emptyElements() { return new HashMap(); } + + private DiffNode(Map elements) { + super(elements); + } + + private DiffNode(DiffElement binding, Map elements) { + super(binding, elements); + } + + // --------------------------------------------------------------------------- + // + // constructors + // + // --------------------------------------------------------------------------- + + public static DiffNode rooted(String name) { + return empty(name, DiffElement.ROOT); + } + + public static DiffNode empty(String name, DiffElement parent) { + DiffNode df = new DiffNode(emptyElements()); + DiffElement elt = new DiffElement(name, parent, df); + df.setBinding(elt); + return df; + } + + public static DiffNode empty(String name, DiffValue parent) { + return empty(name, parent.getBinding()); + } + + // --------------------------------------------------------------------------- + // + // accessors + // + // --------------------------------------------------------------------------- + + @Override + public boolean isAtomic() { return false; } + + public Collection getElementNames() { + return getElementMap().keySet(); + } + + public Collection getElements() { + return getElementMap().values(); + } + + private Collection getElements(boolean atomicOnly) { + List elts = new ArrayList(); + for ( DiffElement elt : getElements() ) + if ( (atomicOnly && elt.getValue().isAtomic()) || (! atomicOnly && elt.getValue().isCompound())) + elts.add(elt); + return elts; + } + + public Collection getAtomicElements() { + return getElements(true); + } + + public Collection getCompoundElements() { + return getElements(false); + } + + public DiffElement getElement(String name) { + for ( DiffElement elt : getElements() ) + if ( elt.getName().equals(name) ) + return elt; + return null; + } + + /** + * Returns true if name is bound in this node + * @param name + * @return + */ + public boolean hasElement(String name) { + return getElement(name) != null; + } + + // --------------------------------------------------------------------------- + // + // add + // + // --------------------------------------------------------------------------- + + @Requires("elt != null") + public void add(DiffElement elt) { + if ( getElementMap().containsKey(elt.getName()) ) + throw new IllegalArgumentException("Attempting to rebind already existing binding: " + elt + " node=" + this); + getElementMap().put(elt.getName(), elt); + } + + @Requires("elt != null") + public void add(DiffValue elt) { + add(elt.getBinding()); + } + + @Requires("elts != null") + public void add(Collection elts) { + for ( DiffElement e : elts ) + add(e); + } + + public void add(String name, Object value) { + add(new DiffElement(name, this.getBinding(), new DiffValue(value))); + } + + // --------------------------------------------------------------------------- + // + // toString + // + // --------------------------------------------------------------------------- + + @Override + public String toString() { + return toString(0); + } + + @Override + public String toString(int offset) { + String off = offset > 0 ? Utils.dupString(' ', offset) : ""; + StringBuilder b = new StringBuilder(); + + b.append("(").append("\n"); + Collection atomicElts = getAtomicElements(); + for ( DiffElement elt : atomicElts ) { + b.append(elt.toString(offset + 2)).append('\n'); + } + + for ( DiffElement elt : getCompoundElements() ) { + b.append(elt.toString(offset + 4)).append('\n'); + } + b.append(off).append(")").append("\n"); + + return b.toString(); + } + + @Override + public String toOneLineString() { + StringBuilder b = new StringBuilder(); + + b.append('('); + List parts = new ArrayList(); + for ( DiffElement elt : getElements() ) + parts.add(elt.toOneLineString()); + b.append(Utils.join(" ", parts)); + b.append(')'); + + return b.toString(); + } + + // -------------------------------------------------------------------------------- + // + // fromString and toOneLineString + // + // -------------------------------------------------------------------------------- + + public static DiffElement fromString(String tree) { + return fromString(tree, DiffElement.ROOT); + } + + /** + * Doesn't support full tree structure parsing + * @param tree + * @param parent + * @return + */ + private static DiffElement fromString(String tree, DiffElement parent) { + // X=(A=A B=B C=(D=D)) + String[] parts = tree.split("=", 2); + if ( parts.length != 2 ) + throw new ReviewedStingException("Unexpected tree structure: " + tree + " parts=" + parts); + String name = parts[0]; + String value = parts[1]; + + if ( value.length() == 0 ) + throw new ReviewedStingException("Illegal tree structure: " + value + " at " + tree); + + if ( value.charAt(0) == '(' ) { + if ( ! value.endsWith(")") ) + throw new ReviewedStingException("Illegal tree structure. Missing ): " + value + " at " + tree); + String subtree = value.substring(1, value.length()-1); + DiffNode rec = DiffNode.empty(name, parent); + String[] subParts = subtree.split(" "); + for ( String subPart : subParts ) { + rec.add(fromString(subPart, rec.getBinding())); + } + return rec.getBinding(); + } else { + return new DiffValue(name, parent, value).getBinding(); + } + } +} diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffObjectsWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffObjectsWalker.java new file mode 100644 index 000000000..a08108db2 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffObjectsWalker.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.diffengine; + +import org.apache.xmlbeans.impl.tool.Diff; +import org.broadinstitute.sting.commandline.Argument; +import org.broadinstitute.sting.commandline.Output; +import org.broadinstitute.sting.gatk.contexts.AlignmentContext; +import org.broadinstitute.sting.gatk.contexts.ReferenceContext; +import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; +import org.broadinstitute.sting.gatk.walkers.Requires; +import org.broadinstitute.sting.gatk.walkers.RodWalker; + +import java.io.File; +import java.io.PrintStream; +import java.util.List; + +/** + * Compares two record-oriented files, itemizing specific difference between equivalent + * records in the two files. Reports both itemized and summarized differences. + * @author Mark DePristo + * @version 0.1 + */ +@Requires(value={}) +public class DiffObjectsWalker extends RodWalker { + @Output(doc="File to which results should be written",required=true) + protected PrintStream out; + + @Argument(fullName="maxRecords", shortName="M", doc="Max. number of records to process", required=false) + int MAX_RECORDS = 0; + + @Argument(fullName="maxCount1Records", shortName="M1", doc="Max. number of records occuring exactly once in the file to process", required=false) + int MAX_COUNT1_RECORDS = 0; + + @Argument(fullName="minCountForDiff", shortName="MCFD", doc="Min number of observations for a records to display", required=false) + int minCountForDiff = 1; + + @Argument(fullName="showItemizedDifferences", shortName="SID", doc="Should we enumerate all differences between the files?", required=false) + boolean showItemizedDifferences = false; + + @Argument(fullName="master", shortName="m", doc="Master file: expected results", required=true) + File masterFile; + + @Argument(fullName="test", shortName="t", doc="Test file: new results to compare to the master file", required=true) + File testFile; + + final DiffEngine diffEngine = new DiffEngine(); + + @Override + public void initialize() { + + } + + @Override + public Integer map(RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { + return 0; + } + + @Override + public Integer reduceInit() { + return 0; + } + + @Override + public Integer reduce(Integer counter, Integer sum) { + return counter + sum; + } + + @Override + public void onTraversalDone(Integer sum) { + out.printf("Reading master file %s%n", masterFile); + DiffElement master = diffEngine.createDiffableFromFile(masterFile); + out.printf("Reading test file %s%n", testFile); + DiffElement test = diffEngine.createDiffableFromFile(testFile); + +// out.printf("Master diff objects%n"); +// out.println(master.toString()); +// out.printf("Test diff objects%n"); +// out.println(test.toString()); + + List diffs = diffEngine.diff(master, test); + if ( showItemizedDifferences ) { + out.printf("Itemized results%n"); + for ( Difference diff : diffs ) + out.printf("DIFF: %s%n", diff.toString()); + } + + DiffEngine.SummaryReportParams params = new DiffEngine.SummaryReportParams(out, MAX_RECORDS, MAX_COUNT1_RECORDS, minCountForDiff); + diffEngine.reportSummarizedDifferences(diffs, params); + } +} \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffValue.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffValue.java new file mode 100644 index 000000000..7245e9e8d --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffValue.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.diffengine; + +import org.broadinstitute.sting.utils.Utils; + +/** + * Created by IntelliJ IDEA. + * User: depristo + * Date: 7/4/11 + * Time: 12:55 PM + * + * An interface that must be implemented to allow us to calculate differences + * between structured objects + */ +public class DiffValue { + private DiffElement binding = null; + final private Object value; + + public DiffValue(Object value) { + this.value = value; + } + + public DiffValue(DiffElement binding, Object value) { + this.binding = binding; + this.value = value; + } + + public DiffValue(DiffValue parent, Object value) { + this(parent.getBinding(), value); + } + + public DiffValue(String name, DiffElement parent, Object value) { + this.binding = new DiffElement(name, parent, this); + this.value = value; + } + + public DiffValue(String name, DiffValue parent, Object value) { + this(name, parent.getBinding(), value); + } + + public DiffElement getBinding() { + return binding; + } + + protected void setBinding(DiffElement binding) { + this.binding = binding; + } + + public Object getValue() { + return value; + } + + public String toString() { + return getValue().toString(); + } + + public String toString(int offset) { + return toString(); + } + + public String toOneLineString() { + return getValue().toString(); + } + + public boolean isAtomic() { return true; } + public boolean isCompound() { return ! isAtomic(); } +} diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffableReader.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffableReader.java new file mode 100644 index 000000000..84c2eed10 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/DiffableReader.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.diffengine; + +import com.google.java.contract.Ensures; +import com.google.java.contract.Requires; + +import java.io.File; + +/** + * Created by IntelliJ IDEA. + * User: depristo + * Date: 7/4/11 + * Time: 1:09 PM + * + * Interface for readers creating diffable objects from a file + */ +public interface DiffableReader { + @Ensures("result != null") + public String getName(); + + @Ensures("result != null") + @Requires("file != null") + public DiffElement readFromFile(File file); + + @Requires("file != null") + public boolean canRead(File file); +} diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/Difference.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/Difference.java new file mode 100644 index 000000000..6627a4cc5 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/Difference.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.diffengine; + +/** + * Created by IntelliJ IDEA. + * User: depristo + * Date: 7/4/11 + * Time: 12:53 PM + * + * Represents a specific difference between two specific DiffElements + */ +public class Difference { + DiffElement master, test; + + public Difference(DiffElement master, DiffElement test) { + if ( master == null && test == null ) throw new IllegalArgumentException("Master and test both cannot be null"); + this.master = master; + this.test = test; + } + + public String toString() { + return String.format("%s:%s!=%s", + getFullyQualifiedName(), + getOneLineString(master), + getOneLineString(test)); + } + + public String getFullyQualifiedName() { + return (master == null ? test : master).fullyQualifiedName(); + } + + private static String getOneLineString(DiffElement elt) { + return elt == null ? "MISSING" : elt.getValue().toOneLineString(); + } +} diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/VCFDiffableReader.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/VCFDiffableReader.java new file mode 100644 index 000000000..743178538 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/diffengine/VCFDiffableReader.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.diffengine; + +import org.broad.tribble.readers.AsciiLineReader; +import org.broad.tribble.readers.LineReader; +import org.broadinstitute.sting.utils.codecs.vcf.VCFCodec; +import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeader; +import org.broadinstitute.sting.utils.variantcontext.Genotype; +import org.broadinstitute.sting.utils.variantcontext.VariantContext; + +import java.io.*; +import java.util.Arrays; +import java.util.Map; +import java.util.zip.GZIPInputStream; + + +/** + * Created by IntelliJ IDEA. + * User: depristo + * Date: 7/4/11 + * Time: 1:09 PM + * + * Class implementing diffnode reader for VCF + */ +public class VCFDiffableReader implements DiffableReader { + @Override + public String getName() { return "VCF"; } + + @Override + public DiffElement readFromFile(File file) { + DiffNode root = DiffNode.rooted(file.getName()); + try { + LineReader lineReader = new AsciiLineReader(new FileInputStream(file)); + VCFCodec vcfCodec = new VCFCodec(); + VCFHeader header = (VCFHeader)vcfCodec.readHeader(lineReader); + + String line = lineReader.readLine(); + while ( line != null ) { + VariantContext vc = (VariantContext)vcfCodec.decode(line); + String name = vc.getChr() + ":" + vc.getStart(); + DiffNode vcRoot = DiffNode.empty(name, root); + + // add fields + vcRoot.add("CHROM", vc.getChr()); + vcRoot.add("POS", vc.getStart()); + vcRoot.add("ID", vc.hasID() ? vc.getID() : VCFConstants.MISSING_VALUE_v4); + vcRoot.add("REF", vc.getReference()); + vcRoot.add("ALT", vc.getAlternateAlleles()); + vcRoot.add("QUAL", vc.hasNegLog10PError() ? vc.getNegLog10PError() * 10 : VCFConstants.MISSING_VALUE_v4); + vcRoot.add("FILTER", vc.getFilters()); + + // add info fields + for (Map.Entry attribute : vc.getAttributes().entrySet()) { + if ( ! attribute.getKey().startsWith("_") && ! attribute.getKey().equals(VariantContext.ID_KEY)) + vcRoot.add(attribute.getKey(), attribute.getValue()); + } + + for (Genotype g : vc.getGenotypes().values() ) { + DiffNode gRoot = DiffNode.empty(g.getSampleName(), vcRoot); + gRoot.add("GT", g.getGenotypeString()); + gRoot.add("GQ", g.hasNegLog10PError() ? g.getNegLog10PError() * 10 : VCFConstants.MISSING_VALUE_v4 ); + + for (Map.Entry attribute : g.getAttributes().entrySet()) { + if ( ! attribute.getKey().startsWith("_") ) + gRoot.add(attribute.getKey(), attribute.getValue()); + } + + vcRoot.add(gRoot); + } + + root.add(vcRoot); + line = lineReader.readLine(); + } + + lineReader.close(); + } catch ( IOException e ) { + return null; + } + + return root.getBinding(); + } + + @Override + public boolean canRead(File file) { + try { + final String VCF4_HEADER = "##fileformat=VCFv4"; + char[] buff = new char[VCF4_HEADER.length()]; + new FileReader(file).read(buff, 0, VCF4_HEADER.length()); + String firstLine = new String(buff); + return firstLine.startsWith(VCF4_HEADER); + } catch ( IOException e ) { + return false; + } + } +} From d7d15019dd543decffa2169f074b123633fe5984 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Tue, 12 Jul 2011 01:16:21 -0400 Subject: [PATCH 59/59] Adding support for other simple header line types (e.g. ALT) and cleaning up the interface a bit. --- .../walkers/annotator/ChromosomeCounts.java | 5 +- .../walkers/genotyper/UnifiedGenotyper.java | 17 +++- .../utils/codecs/vcf/StandardVCFWriter.java | 3 +- .../utils/codecs/vcf/VCFAltHeaderLine.java | 28 +++++++ .../codecs/vcf/VCFCompoundHeaderLine.java | 3 + .../utils/codecs/vcf/VCFFilterHeaderLine.java | 48 +---------- .../utils/codecs/vcf/VCFFormatHeaderLine.java | 2 +- .../utils/codecs/vcf/VCFSimpleHeaderLine.java | 81 +++++++++++++++++++ .../sting/utils/codecs/vcf/VCFUtils.java | 15 ---- 9 files changed, 135 insertions(+), 67 deletions(-) create mode 100644 public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFAltHeaderLine.java create mode 100644 public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFSimpleHeaderLine.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ChromosomeCounts.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ChromosomeCounts.java index 143722d7c..ed10d2072 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ChromosomeCounts.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ChromosomeCounts.java @@ -25,6 +25,7 @@ package org.broadinstitute.sting.gatk.walkers.annotator; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineCount; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineType; import org.broadinstitute.sting.utils.codecs.vcf.VCFInfoHeaderLine; @@ -41,8 +42,8 @@ import java.util.*; public class ChromosomeCounts implements InfoFieldAnnotation, StandardAnnotation { private String[] keyNames = { VCFConstants.ALLELE_NUMBER_KEY, VCFConstants.ALLELE_COUNT_KEY, VCFConstants.ALLELE_FREQUENCY_KEY }; - private VCFInfoHeaderLine[] descriptions = { new VCFInfoHeaderLine(VCFConstants.ALLELE_FREQUENCY_KEY, -1, VCFHeaderLineType.Float, "Allele Frequency, for each ALT allele, in the same order as listed"), - new VCFInfoHeaderLine(VCFConstants.ALLELE_COUNT_KEY, -1, VCFHeaderLineType.Integer, "Allele count in genotypes, for each ALT allele, in the same order as listed"), + private VCFInfoHeaderLine[] descriptions = { new VCFInfoHeaderLine(VCFConstants.ALLELE_FREQUENCY_KEY, VCFHeaderLineCount.UNBOUNDED, VCFHeaderLineType.Float, "Allele Frequency, for each ALT allele, in the same order as listed"), + new VCFInfoHeaderLine(VCFConstants.ALLELE_COUNT_KEY, VCFHeaderLineCount.UNBOUNDED, VCFHeaderLineType.Integer, "Allele count in genotypes, for each ALT allele, in the same order as listed"), new VCFInfoHeaderLine(VCFConstants.ALLELE_NUMBER_KEY, 1, VCFHeaderLineType.Integer, "Total number of alleles in called genotypes") }; public Map annotate(RefMetaDataTracker tracker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyper.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyper.java index 7a765c602..fe0084a19 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyper.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyper.java @@ -37,7 +37,6 @@ import org.broadinstitute.sting.gatk.datasources.rmd.ReferenceOrderedDataSource; import org.broadinstitute.sting.utils.*; import org.broadinstitute.sting.utils.baq.BAQ; import org.broadinstitute.sting.commandline.*; -import org.broadinstitute.sting.utils.codecs.vcf.VCFUtils; import java.util.*; import java.io.PrintStream; @@ -158,7 +157,7 @@ public class UnifiedGenotyper extends LocusWalker getSupportedHeaderStrings() { + Set result = new HashSet(); + result.add(new VCFFormatHeaderLine(VCFConstants.GENOTYPE_KEY, 1, VCFHeaderLineType.String, "Genotype")); + result.add(new VCFFormatHeaderLine(VCFConstants.GENOTYPE_QUALITY_KEY, 1, VCFHeaderLineType.Float, "Genotype Quality")); + result.add(new VCFFormatHeaderLine(VCFConstants.DEPTH_KEY, 1, VCFHeaderLineType.Integer, "Read Depth (only filtered reads used for calling)")); + result.add(new VCFFormatHeaderLine(VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY, VCFHeaderLineCount.UNBOUNDED, VCFHeaderLineType.Float, "Normalized, Phred-scaled likelihoods for AA,AB,BB genotypes where A=ref and B=alt; if site is not biallelic, number of likelihoods if n*(n+1)/2")); + + return result; + } + /** * Compute at a given locus. * diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java index 230773310..f4996b487 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java @@ -358,9 +358,8 @@ public class StandardVCFWriter implements VCFWriter { mWriter.write(key); if ( !entry.getValue().equals("") ) { - int numVals = 1; VCFInfoHeaderLine metaData = mHeader.getInfoHeaderLine(key); - if ( metaData != null && (metaData.getCountType() != VCFHeaderLineCount.INTEGER || metaData.getCount() > 0) ) { + if ( metaData == null || metaData.getCountType() != VCFHeaderLineCount.INTEGER || metaData.getCount() != 0 ) { mWriter.write("="); mWriter.write(entry.getValue()); } diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFAltHeaderLine.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFAltHeaderLine.java new file mode 100644 index 000000000..a9de949d8 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFAltHeaderLine.java @@ -0,0 +1,28 @@ +package org.broadinstitute.sting.utils.codecs.vcf; + +/** + * @author ebanks + * A class representing a key=value entry for ALT fields in the VCF header + */ +public class VCFAltHeaderLine extends VCFSimpleHeaderLine { + + /** + * create a VCF filter header line + * + * @param name the name for this header line + * @param description the description for this header line + */ + public VCFAltHeaderLine(String name, String description) { + super(name, description, SupportedHeaderLineType.ALT); + } + + /** + * create a VCF info header line + * + * @param line the header line + * @param version the vcf header version + */ + protected VCFAltHeaderLine(String line, VCFHeaderVersion version) { + super(line, version, SupportedHeaderLineType.ALT); + } +} \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCompoundHeaderLine.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCompoundHeaderLine.java index 49f9ab184..bb822f2ed 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCompoundHeaderLine.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCompoundHeaderLine.java @@ -89,6 +89,7 @@ public abstract class VCFCompoundHeaderLine extends VCFHeaderLine implements VCF * @param count the count for this header line * @param type the type for this header line * @param description the description for this header line + * @param lineType the header line type */ protected VCFCompoundHeaderLine(String name, int count, VCFHeaderLineType type, String description, SupportedHeaderLineType lineType) { super(lineType.toString(), ""); @@ -108,6 +109,7 @@ public abstract class VCFCompoundHeaderLine extends VCFHeaderLine implements VCF * @param count the count type for this header line * @param type the type for this header line * @param description the description for this header line + * @param lineType the header line type */ protected VCFCompoundHeaderLine(String name, VCFHeaderLineCount count, VCFHeaderLineType type, String description, SupportedHeaderLineType lineType) { super(lineType.toString(), ""); @@ -124,6 +126,7 @@ public abstract class VCFCompoundHeaderLine extends VCFHeaderLine implements VCF * * @param line the header line * @param version the VCF header version + * @param lineType the header line type * */ protected VCFCompoundHeaderLine(String line, VCFHeaderVersion version, SupportedHeaderLineType lineType) { diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFFilterHeaderLine.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFFilterHeaderLine.java index 9176fc16e..418b80074 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFFilterHeaderLine.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFFilterHeaderLine.java @@ -1,19 +1,10 @@ package org.broadinstitute.sting.utils.codecs.vcf; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.Map; - - /** * @author ebanks * A class representing a key=value entry for FILTER fields in the VCF header */ -public class VCFFilterHeaderLine extends VCFHeaderLine implements VCFNamedHeaderLine { - - private String name; - private String description; - +public class VCFFilterHeaderLine extends VCFSimpleHeaderLine { /** * create a VCF filter header line @@ -22,12 +13,7 @@ public class VCFFilterHeaderLine extends VCFHeaderLine implements VCFNamedHeader * @param description the description for this header line */ public VCFFilterHeaderLine(String name, String description) { - super("FILTER", ""); - this.name = name; - this.description = description; - - if ( name == null || description == null ) - throw new IllegalArgumentException(String.format("Invalid VCFCompoundHeaderLine: key=%s name=%s desc=%s", super.getKey(), name, description )); + super(name, description, SupportedHeaderLineType.FILTER); } /** @@ -37,34 +23,6 @@ public class VCFFilterHeaderLine extends VCFHeaderLine implements VCFNamedHeader * @param version the vcf header version */ protected VCFFilterHeaderLine(String line, VCFHeaderVersion version) { - super("FILTER", ""); - Map mapping = VCFHeaderLineTranslator.parseLine(version,line, Arrays.asList("ID","Description")); - name = mapping.get("ID"); - description = mapping.get("Description"); - if ( description == null && ALLOW_UNBOUND_DESCRIPTIONS ) // handle the case where there's no description provided - description = UNBOUND_DESCRIPTION; - } - - protected String toStringEncoding() { - Map map = new LinkedHashMap(); - map.put("ID", name); - map.put("Description", description); - return "FILTER=" + VCFHeaderLine.toStringEncoding(map); - } - - public boolean equals(Object o) { - if ( !(o instanceof VCFFilterHeaderLine) ) - return false; - VCFFilterHeaderLine other = (VCFFilterHeaderLine)o; - return name.equals(other.name) && - description.equals(other.description); - } - - public String getName() { - return name; - } - - public String getDescription() { - return description; + super(line, version, SupportedHeaderLineType.FILTER); } } \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFFormatHeaderLine.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFFormatHeaderLine.java index f68cb670b..474c8dd14 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFFormatHeaderLine.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFFormatHeaderLine.java @@ -17,7 +17,7 @@ public class VCFFormatHeaderLine extends VCFCompoundHeaderLine { } public VCFFormatHeaderLine(String name, VCFHeaderLineCount count, VCFHeaderLineType type, String description) { - super(name, count, type, description, SupportedHeaderLineType.INFO); + super(name, count, type, description, SupportedHeaderLineType.FORMAT); } protected VCFFormatHeaderLine(String line, VCFHeaderVersion version) { diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFSimpleHeaderLine.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFSimpleHeaderLine.java new file mode 100644 index 000000000..152043f28 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFSimpleHeaderLine.java @@ -0,0 +1,81 @@ +package org.broadinstitute.sting.utils.codecs.vcf; + +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.Map; + + +/** + * @author ebanks + * A class representing a key=value entry for simple VCF header types + */ +public abstract class VCFSimpleHeaderLine extends VCFHeaderLine implements VCFNamedHeaderLine { + + public enum SupportedHeaderLineType { + FILTER, ALT; + } + + private String name; + private String description; + + // our type of line, i.e. filter, alt, etc + private final SupportedHeaderLineType lineType; + + + /** + * create a VCF filter header line + * + * @param name the name for this header line + * @param description the description for this header line + * @param lineType the header line type + */ + public VCFSimpleHeaderLine(String name, String description, SupportedHeaderLineType lineType) { + super(lineType.toString(), ""); + this.lineType = lineType; + this.name = name; + this.description = description; + + if ( name == null || description == null ) + throw new IllegalArgumentException(String.format("Invalid VCFSimpleHeaderLine: key=%s name=%s desc=%s", super.getKey(), name, description )); + } + + /** + * create a VCF info header line + * + * @param line the header line + * @param version the vcf header version + * @param lineType the header line type + */ + protected VCFSimpleHeaderLine(String line, VCFHeaderVersion version, SupportedHeaderLineType lineType) { + super(lineType.toString(), ""); + this.lineType = lineType; + Map mapping = VCFHeaderLineTranslator.parseLine(version,line, Arrays.asList("ID","Description")); + name = mapping.get("ID"); + description = mapping.get("Description"); + if ( description == null && ALLOW_UNBOUND_DESCRIPTIONS ) // handle the case where there's no description provided + description = UNBOUND_DESCRIPTION; + } + + protected String toStringEncoding() { + Map map = new LinkedHashMap(); + map.put("ID", name); + map.put("Description", description); + return lineType.toString() + "=" + VCFHeaderLine.toStringEncoding(map); + } + + public boolean equals(Object o) { + if ( !(o instanceof VCFSimpleHeaderLine) ) + return false; + VCFSimpleHeaderLine other = (VCFSimpleHeaderLine)o; + return name.equals(other.name) && + description.equals(other.description); + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } +} \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFUtils.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFUtils.java index ecede068e..4037f75b9 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFUtils.java @@ -180,19 +180,4 @@ public class VCFUtils { return new HashSet(map.values()); } - - /** - * return a set of supported format lines; what we currently support for output in the genotype fields of a VCF - * @return a set of VCF format lines - */ - public static Set getSupportedHeaderStrings() { - Set result = new HashSet(); - result.add(new VCFFormatHeaderLine(VCFConstants.GENOTYPE_KEY, 1, VCFHeaderLineType.String, "Genotype")); - result.add(new VCFFormatHeaderLine(VCFConstants.GENOTYPE_QUALITY_KEY, 1, VCFHeaderLineType.Float, "Genotype Quality")); - result.add(new VCFFormatHeaderLine(VCFConstants.DEPTH_KEY, 1, VCFHeaderLineType.Integer, "Read Depth (only filtered reads used for calling)")); - result.add(new VCFFormatHeaderLine(VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY, -1, VCFHeaderLineType.Float, "Normalized, Phred-scaled likelihoods for AA,AB,BB genotypes where A=ref and B=alt; if site is not biallelic, number of likelihoods if n*(n+1)/2")); - - return result; - } - } \ No newline at end of file