QualityUtils now uses runtime argument checks instead of contract

-- There's some runtime cost for these tests, but it's not big enough to outweigh the value of catching errors quickly
This commit is contained in:
Mark DePristo 2013-02-11 11:03:15 -08:00
parent 3231031c1a
commit b393c27f07
1 changed files with 12 additions and 19 deletions

View File

@ -26,7 +26,6 @@
package org.broadinstitute.sting.utils; package org.broadinstitute.sting.utils;
import com.google.java.contract.Ensures; import com.google.java.contract.Ensures;
import com.google.java.contract.Requires;
import net.sf.samtools.SAMUtils; import net.sf.samtools.SAMUtils;
/** /**
@ -110,9 +109,9 @@ public class QualityUtils {
* @param qual a phred-scaled quality score encoded as a double. Can be non-integer values (30.5) * @param qual a phred-scaled quality score encoded as a double. Can be non-integer values (30.5)
* @return a probability (0.0-1.0) * @return a probability (0.0-1.0)
*/ */
@Requires("qual >= 0.0")
@Ensures("result >= 0.0 && result <= 1.0") @Ensures("result >= 0.0 && result <= 1.0")
public static double qualToProb(final double qual) { public static double qualToProb(final double qual) {
if ( qual < 0.0 ) throw new IllegalArgumentException("qual must be >= 0.0 but got " + qual);
return 1.0 - qualToErrorProb(qual); return 1.0 - qualToErrorProb(qual);
} }
@ -144,9 +143,9 @@ public class QualityUtils {
* @param qual a phred-scaled quality score encoded as a double. Can be non-integer values (30.5) * @param qual a phred-scaled quality score encoded as a double. Can be non-integer values (30.5)
* @return a probability (0.0-1.0) * @return a probability (0.0-1.0)
*/ */
@Requires("qual >= 0.0")
@Ensures("result >= 0.0 && result <= 1.0") @Ensures("result >= 0.0 && result <= 1.0")
public static double qualToErrorProb(final double qual) { public static double qualToErrorProb(final double qual) {
if ( qual < 0.0 ) throw new IllegalArgumentException("qual must be >= 0.0 but got " + qual);
return Math.pow(10.0, qual / -10.0); return Math.pow(10.0, qual / -10.0);
} }
@ -199,6 +198,7 @@ public class QualityUtils {
*/ */
@Ensures("result <= 0.0") @Ensures("result <= 0.0")
public static double qualToErrorProbLog10(final double qual) { public static double qualToErrorProbLog10(final double qual) {
if ( qual < 0.0 ) throw new IllegalArgumentException("qual must be >= 0.0 but got " + qual);
return qual / -10.0; return qual / -10.0;
} }
@ -217,7 +217,6 @@ public class QualityUtils {
* @param errorRate a probability (0.0-1.0) of being wrong (i.e., 0.01 is 1% change of being wrong) * @param errorRate a probability (0.0-1.0) of being wrong (i.e., 0.01 is 1% change of being wrong)
* @return a quality score (0-MAX_SAM_QUAL_SCORE) * @return a quality score (0-MAX_SAM_QUAL_SCORE)
*/ */
@Requires("errorRate >= 0.0 && errorRate <= 1.0")
public static byte errorProbToQual(final double errorRate) { public static byte errorProbToQual(final double errorRate) {
return errorProbToQual(errorRate, MAX_SAM_QUAL_SCORE); return errorProbToQual(errorRate, MAX_SAM_QUAL_SCORE);
} }
@ -234,8 +233,8 @@ public class QualityUtils {
* @param errorRate a probability (0.0-1.0) of being wrong (i.e., 0.01 is 1% change of being wrong) * @param errorRate a probability (0.0-1.0) of being wrong (i.e., 0.01 is 1% change of being wrong)
* @return a quality score (0-maxQual) * @return a quality score (0-maxQual)
*/ */
@Requires("errorRate >= 0.0 && errorRate <= 1.0")
public static byte errorProbToQual(final double errorRate, final byte maxQual) { public static byte errorProbToQual(final double errorRate, final byte maxQual) {
if ( ! MathUtils.goodProbability(errorRate) ) throw new IllegalArgumentException("errorRate must be good probability but got " + errorRate);
final double d = Math.round(-10.0*Math.log10(errorRate)); final double d = Math.round(-10.0*Math.log10(errorRate));
return boundQual((int)d, maxQual); return boundQual((int)d, maxQual);
} }
@ -243,8 +242,8 @@ public class QualityUtils {
/** /**
* @see #errorProbToQual(double, byte) with proper conversion of maxQual integer to a byte * @see #errorProbToQual(double, byte) with proper conversion of maxQual integer to a byte
*/ */
@Requires("maxQual >= 0 && maxQual < 255")
public static byte errorProbToQual(final double prob, final int maxQual) { public static byte errorProbToQual(final double prob, final int maxQual) {
if ( maxQual < 0 || maxQual > 255 ) throw new IllegalArgumentException("maxQual must be between 0-255 but got " + maxQual);
return errorProbToQual(prob, (byte)(maxQual & 0xFF)); return errorProbToQual(prob, (byte)(maxQual & 0xFF));
} }
@ -257,7 +256,6 @@ public class QualityUtils {
* @param prob a probability (0.0-1.0) of being right * @param prob a probability (0.0-1.0) of being right
* @return a quality score (0-MAX_SAM_QUAL_SCORE) * @return a quality score (0-MAX_SAM_QUAL_SCORE)
*/ */
@Requires("prob >= 0.0 && prob <= 1.0")
public static byte trueProbToQual(final double prob) { public static byte trueProbToQual(final double prob) {
return trueProbToQual(prob, MAX_SAM_QUAL_SCORE); return trueProbToQual(prob, MAX_SAM_QUAL_SCORE);
} }
@ -275,24 +273,22 @@ public class QualityUtils {
* WARNING -- because this function takes a byte for maxQual, you must be careful in converting * WARNING -- because this function takes a byte for maxQual, you must be careful in converting
* integers to byte. The appropriate way to do this is ((byte)(myInt & 0xFF)) * integers to byte. The appropriate way to do this is ((byte)(myInt & 0xFF))
* *
* @param prob a probability (0.0-1.0) of being right * @param trueProb a probability (0.0-1.0) of being right
* @param maxQual the maximum quality score we are allowed to emit here, regardless of the error rate * @param maxQual the maximum quality score we are allowed to emit here, regardless of the error rate
* @return a phred-scaled quality score (0-maxQualScore) as a byte * @return a phred-scaled quality score (0-maxQualScore) as a byte
*/ */
@Requires({
"prob >= 0.0 && prob <= 1.0"
})
@Ensures("(result & 0xFF) >= 1 && (result & 0xFF) <= (maxQual & 0xFF)") @Ensures("(result & 0xFF) >= 1 && (result & 0xFF) <= (maxQual & 0xFF)")
public static byte trueProbToQual(final double prob, final byte maxQual) { public static byte trueProbToQual(final double trueProb, final byte maxQual) {
final double lp = Math.round(-10.0*MathUtils.log10OneMinusX(prob)); if ( ! MathUtils.goodProbability(trueProb) ) throw new IllegalArgumentException("trueProb must be good probability but got " + trueProb);
final double lp = Math.round(-10.0*MathUtils.log10OneMinusX(trueProb));
return boundQual((int)lp, maxQual); return boundQual((int)lp, maxQual);
} }
/** /**
* @see #trueProbToQual(double, byte) with proper conversion of maxQual to a byte * @see #trueProbToQual(double, byte) with proper conversion of maxQual to a byte
*/ */
@Requires("maxQual >= 0 && maxQual < 255")
public static byte trueProbToQual(final double prob, final int maxQual) { public static byte trueProbToQual(final double prob, final int maxQual) {
if ( maxQual < 0 || maxQual > 255 ) throw new IllegalArgumentException("maxQual must be between 0-255 but got " + maxQual);
return trueProbToQual(prob, (byte)(maxQual & 0xFF)); return trueProbToQual(prob, (byte)(maxQual & 0xFF));
} }
@ -305,7 +301,6 @@ public class QualityUtils {
* @param trueRate the probability of being right (0.0-1.0) * @param trueRate the probability of being right (0.0-1.0)
* @return a phred-scaled version of the error rate implied by trueRate * @return a phred-scaled version of the error rate implied by trueRate
*/ */
@Requires("MathUtils.goodProbability(trueRate)")
@Ensures("result >= 0.0") @Ensures("result >= 0.0")
public static double phredScaleCorrectRate(final double trueRate) { public static double phredScaleCorrectRate(final double trueRate) {
return phredScaleLog10ErrorRate(MathUtils.log10OneMinusX(trueRate)); return phredScaleLog10ErrorRate(MathUtils.log10OneMinusX(trueRate));
@ -320,7 +315,6 @@ public class QualityUtils {
* @param trueRateLog10 the probability of being right (0.0-1.0) * @param trueRateLog10 the probability of being right (0.0-1.0)
* @return a phred-scaled version of the error rate implied by trueRate * @return a phred-scaled version of the error rate implied by trueRate
*/ */
@Requires("MathUtils.goodLog10Probability(trueRateLog10)")
@Ensures("result >= 0.0") @Ensures("result >= 0.0")
public static double phredScaleLog10CorrectRate(final double trueRateLog10) { public static double phredScaleLog10CorrectRate(final double trueRateLog10) {
return phredScaleCorrectRate(Math.pow(10.0, trueRateLog10)); return phredScaleCorrectRate(Math.pow(10.0, trueRateLog10));
@ -335,7 +329,6 @@ public class QualityUtils {
* @param errorRate the probability of being wrong (0.0-1.0) * @param errorRate the probability of being wrong (0.0-1.0)
* @return a phred-scaled version of the error rate * @return a phred-scaled version of the error rate
*/ */
@Requires("MathUtils.goodProbability(errorRate)")
@Ensures("result >= 0.0") @Ensures("result >= 0.0")
public static double phredScaleErrorRate(final double errorRate) { public static double phredScaleErrorRate(final double errorRate) {
return phredScaleLog10ErrorRate(Math.log10(errorRate)); return phredScaleLog10ErrorRate(Math.log10(errorRate));
@ -352,6 +345,7 @@ public class QualityUtils {
*/ */
@Ensures("result >= 0.0") @Ensures("result >= 0.0")
public static double phredScaleLog10ErrorRate(final double errorRateLog10) { public static double phredScaleLog10ErrorRate(final double errorRateLog10) {
if ( ! MathUtils.goodLog10Probability(errorRateLog10) ) throw new IllegalArgumentException("errorRateLog10 must be good probability but got " + errorRateLog10);
// abs is necessary for edge base with errorRateLog10 = 0 producing -0.0 doubles // abs is necessary for edge base with errorRateLog10 = 0 producing -0.0 doubles
return Math.abs(-10.0 * Math.max(errorRateLog10, RAW_MIN_PHRED_SCALED_QUAL)); return Math.abs(-10.0 * Math.max(errorRateLog10, RAW_MIN_PHRED_SCALED_QUAL));
} }
@ -368,7 +362,6 @@ public class QualityUtils {
* @param qual the uncapped quality score as an integer * @param qual the uncapped quality score as an integer
* @return the bounded quality score * @return the bounded quality score
*/ */
@Requires("qual >= 0")
@Ensures("(result & 0xFF) >= 1 && (result & 0xFF) <= (MAX_SAM_QUAL_SCORE & 0xFF)") @Ensures("(result & 0xFF) >= 1 && (result & 0xFF) <= (MAX_SAM_QUAL_SCORE & 0xFF)")
public static byte boundQual(int qual) { public static byte boundQual(int qual) {
return boundQual(qual, MAX_SAM_QUAL_SCORE); return boundQual(qual, MAX_SAM_QUAL_SCORE);
@ -384,9 +377,9 @@ public class QualityUtils {
* @param maxQual the maximum quality score, must be less < 255 * @param maxQual the maximum quality score, must be less < 255
* @return the bounded quality score * @return the bounded quality score
*/ */
@Requires({"qual >= 0"})
@Ensures("(result & 0xFF) >= 1 && (result & 0xFF) <= (maxQual & 0xFF)") @Ensures("(result & 0xFF) >= 1 && (result & 0xFF) <= (maxQual & 0xFF)")
public static byte boundQual(final int qual, final byte maxQual) { public static byte boundQual(final int qual, final byte maxQual) {
if ( qual < 0 ) throw new IllegalArgumentException("qual must be >= 0 " + qual);
return (byte) (Math.max(Math.min(qual, maxQual & 0xFF), 1) & 0xFF); return (byte) (Math.max(Math.min(qual, maxQual & 0xFF), 1) & 0xFF);
} }
} }