99% working version of BCF2 encoder / decoder

-- fixed final bugs with PL encoding / decoding
-- Ready for testing by other members of the group
-- Current performance numbers aren't so great, but they will improve in the next phase of BCF2 optimizations
-- Fixed a nasty bug in the filter field
-- Not that some (many?) GATK tools won't work with BCF because they internally assume values are Strings not their true types

Read 1500 genotypes file in VCF -> VCF : 11 seconds
Read 1500 genotypes file in VCF -> BCF : 9.5 seconds

VariantEval 1500 genotypes file in VCF : 3 seconds
VariantEval 1500 genotypes file in BCF : 3 seconds
This commit is contained in:
Mark DePristo 2012-05-17 09:54:50 -04:00
parent b5bce8d3f9
commit 64d4238e2f
12 changed files with 206 additions and 118 deletions

View File

@ -266,13 +266,17 @@ public class BCF2Codec implements FeatureCodec<VariantContext> {
}
private void decodeFilter( final VariantContextBuilder builder ) {
final Object filters = decoder.decodeTypedValue();
final Object value = decoder.decodeTypedValue();
if ( filters == null ) {
if ( value == null )
builder.unfiltered();
}
else {
builder.filters(new LinkedHashSet<String>(asStrings(filters)));
if ( value instanceof Integer )
builder.filter(getDictionaryString((Integer)value));
else {
for ( int offset : (List<Integer>)value )
builder.filter(getDictionaryString(offset));
}
}
}
@ -320,12 +324,21 @@ public class BCF2Codec implements FeatureCodec<VariantContext> {
final Integer value = (Integer)values.get(i);
if ( value != BCF2Type.INT8.getMissingJavaValue() )
log10PError = value / -10.0;
} else if ( field.equals(VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY) ) {
final List<Integer> pls = (List<Integer>)values.get(i);
if ( pls != null ) { // we have a PL field
log10Likelihoods = new double[pls.size()];
for ( int j = 0; j < log10Likelihoods.length; j++ )
log10Likelihoods[j] = pls.get(j) / -10.0;
}
} else if ( field.equals(VCFConstants.GENOTYPE_FILTER_KEY) ) {
throw new ReviewedStingException("Genotype filters not implemented in GATK BCF2");
//filters = new HashSet<String>(values.get(i));
} else { // add to attributes
if ( attributes == null ) attributes = new HashMap<String, Object>(nFields);
attributes.put(field, values.get(i));
if ( values.get(i) != null ) { // don't add missing values
if ( attributes == null ) attributes = new HashMap<String, Object>(nFields);
attributes.put(field, values.get(i));
}
}
}
@ -370,7 +383,10 @@ public class BCF2Codec implements FeatureCodec<VariantContext> {
}
private final String getDictionaryString() {
final int offset = (Integer)decoder.decodeTypedValue();
return getDictionaryString((Integer) decoder.decodeTypedValue());
}
private final String getDictionaryString(final int offset) {
final String field = dictionary.get(offset);
return field;
}
@ -383,24 +399,4 @@ public class BCF2Codec implements FeatureCodec<VariantContext> {
throw new UserException.MalformedBCF2(String.format("No contig at index %d present in the sequence dictionary from the BCF2 header (%s)", contigOffset, contigNames));
}
}
// ----------------------------------------------------------------------
//
// Utility functions
//
// ----------------------------------------------------------------------
private final Collection<String> asStrings(final Object o) {
return asCollection(String.class, o);
}
private final <T> Collection<T> asCollection(final Class<T> c, final Object o) {
if ( o == null )
return Collections.emptyList();
else if ( o instanceof List ) {
return (List<T>)o;
} else {
return (Set<T>)Collections.singleton(o);
}
}
}

View File

@ -145,7 +145,7 @@ public class BCF2Decoder {
for ( int i = 0; i < size; i++ ) {
ints.add(decodeSingleValue(type));
}
return ints;
return ints.get(0) == null ? null : ints;
}
}

View File

@ -185,6 +185,21 @@ public class BCF2Encoder {
//
// --------------------------------------------------------------------------------
public final BCF2Type determineIntegerType(final int[] values) {
// literally a copy of the code below, but there's no general way to unify lists and arrays in java
BCF2Type maxType = BCF2Type.INT8;
for ( final int value : values ) {
final BCF2Type type1 = determineIntegerType(value);
switch ( type1 ) {
case INT8: break;
case INT16: maxType = BCF2Type.INT16; break;
case INT32: return BCF2Type.INT32; // fast path for largest possible value
default: throw new ReviewedStingException("Unexpected integer type " + type1 );
}
}
return maxType;
}
public final BCF2Type determineIntegerType(final List<Integer> values) {
BCF2Type maxType = BCF2Type.INT8;
for ( final int value : values ) {

View File

@ -214,9 +214,7 @@ public class Genotype implements Comparable<Genotype> {
return x;
else {
x = getLikelihoods(VCFConstants.GENOTYPE_LIKELIHOODS_KEY, false);
if ( x != null ) return x;
else
throw new IllegalStateException("BUG: genotype likelihood field in " + this.getSampleName() + " sample are not either a string or a genotype likelihood class!");
return x;
}
}

View File

@ -24,19 +24,15 @@
package org.broadinstitute.sting.utils.variantcontext;
import org.apache.commons.lang.ArrayUtils;
import org.broad.tribble.TribbleException;
import org.broadinstitute.sting.utils.MathUtils;
import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants;
import org.broadinstitute.sting.utils.exceptions.ReviewedStingException;
import org.broadinstitute.sting.utils.exceptions.UserException;
import java.util.*;
public class GenotypeLikelihoods implements List<Double> {
public static final boolean CAP_PLS = false;
public static final int PL_CAP = 255;
import java.util.EnumMap;
public class GenotypeLikelihoods {
//
// There are two objects here because we are lazy in creating both representations
// for this object: a vector of log10 Probs and the PL phred-scaled string. Supports
@ -57,14 +53,18 @@ public class GenotypeLikelihoods implements List<Double> {
return new GenotypeLikelihoods(log10Likelihoods);
}
public final static GenotypeLikelihoods fromPLs(final int[] pls) {
return new GenotypeLikelihoods(PLsToGLs(pls));
}
//
// You must use the factory methods now
//
protected GenotypeLikelihoods(String asString) {
private GenotypeLikelihoods(String asString) {
likelihoodsAsString_PLs = asString;
}
protected GenotypeLikelihoods(double[] asVector) {
private GenotypeLikelihoods(double[] asVector) {
log10Likelihoods = asVector;
}
@ -85,6 +85,11 @@ public class GenotypeLikelihoods implements List<Double> {
return log10Likelihoods;
}
public int[] getAsPLs() {
final double[] GLs = getAsVector();
return GLs == null ? null : GLsToPLs(GLs);
}
public String toString() {
return getAsString();
}
@ -181,66 +186,84 @@ public class GenotypeLikelihoods implements List<Double> {
return null;
}
private final static String convertLikelihoodsToPLString(double[] GLs) {
private final static String convertLikelihoodsToPLString(final double[] GLs) {
if ( GLs == null )
return VCFConstants.MISSING_VALUE_v4;
StringBuilder s = new StringBuilder();
double adjust = Double.NEGATIVE_INFINITY;
for ( double l : GLs ) adjust = Math.max(adjust, l);
final StringBuilder s = new StringBuilder();
boolean first = true;
for ( double l : GLs ) {
for ( final int pl : GLsToPLs(GLs) ) {
if ( ! first )
s.append(",");
else
first = false;
long PL = Math.round(-10 * (l - adjust));
if ( CAP_PLS )
PL = Math.min(PL, PL_CAP);
s.append(Long.toString(PL));
s.append(pl);
}
return s.toString();
}
// -------------------------------------------------------------------------------------
//
// List interface functions
//
// -------------------------------------------------------------------------------------
private final static int[] GLsToPLs(final double[] GLs) {
final int[] pls = new int[GLs.length];
final double adjust = maxPL(GLs);
private final void notImplemented() {
throw new ReviewedStingException("BUG: code not implemented");
for ( int i = 0; i < GLs.length; i++ ) {
pls[i] = (int)Math.round(-10 * (GLs[i] - adjust));
}
return pls;
}
@Override public int size() { return this.log10Likelihoods.length; }
@Override public Double get(final int i) { return log10Likelihoods[i];}
@Override public Double set(final int i, final Double aDouble) { return log10Likelihoods[i] = aDouble; }
@Override public boolean isEmpty() { return false; }
@Override public Iterator<Double> iterator() { return Arrays.asList(ArrayUtils.toObject(log10Likelihoods)).iterator(); }
@Override public Object[] toArray() { return ArrayUtils.toObject(log10Likelihoods); }
private final static double maxPL(final double[] GLs) {
double adjust = Double.NEGATIVE_INFINITY;
for ( double l : GLs ) adjust = Math.max(adjust, l);
return adjust;
}
// none of these are implemented
@Override public boolean contains(final Object o) { notImplemented(); return false; }
@Override public <T> T[] toArray(final T[] ts) { notImplemented(); return null; }
@Override public boolean add(final Double aDouble) { notImplemented(); return false; }
@Override public boolean remove(final Object o) {notImplemented(); return false; }
@Override public boolean containsAll(final Collection<?> objects) { notImplemented(); return false; }
@Override public boolean addAll(final Collection<? extends Double> doubles) { notImplemented(); return false; }
@Override public boolean addAll(final int i, final Collection<? extends Double> doubles) { notImplemented(); return false; }
@Override public boolean removeAll(final Collection<?> objects) { notImplemented(); return false; }
@Override public boolean retainAll(final Collection<?> objects) { notImplemented(); return false; }
@Override public void clear() { notImplemented(); }
@Override public void add(final int i, final Double aDouble) { notImplemented(); }
@Override public Double remove(final int i) { notImplemented(); return null; }
@Override public int indexOf(final Object o) { notImplemented(); return -1; }
@Override public int lastIndexOf(final Object o) { notImplemented(); return 0; }
@Override public ListIterator<Double> listIterator() { notImplemented(); return null; }
@Override public ListIterator<Double> listIterator(final int i) { notImplemented(); return null; }
@Override public List<Double> subList(final int i, final int i1) { notImplemented(); return null; }
private final static double[] PLsToGLs(final int pls[]) {
double[] likelihoodsAsVector = new double[pls.length];
for ( int i = 0; i < pls.length; i++ ) {
likelihoodsAsVector[i] = pls[i] / -10.0;
}
return likelihoodsAsVector;
}
// // -------------------------------------------------------------------------------------
// //
// // List interface functions
// //
// // -------------------------------------------------------------------------------------
//
// private final void notImplemented() {
// throw new ReviewedStingException("BUG: code not implemented");
// }
//
// @Override public int size() { return getAsVector().length; }
// @Override public Double get(final int i) { return getAsVector()[i];}
// @Override public Double set(final int i, final Double aDouble) { return getAsVector()[i] = aDouble; }
// @Override public boolean isEmpty() { return false; }
// @Override public Iterator<Double> iterator() { return Arrays.asList(ArrayUtils.toObject(getAsVector())).iterator(); }
// @Override public Object[] toArray() { return ArrayUtils.toObject(getAsVector()); }
//
// // none of these are implemented
// @Override public boolean contains(final Object o) { notImplemented(); return false; }
// @Override public <T> T[] toArray(final T[] ts) { notImplemented(); return null; }
// @Override public boolean add(final Double aDouble) { notImplemented(); return false; }
// @Override public boolean remove(final Object o) {notImplemented(); return false; }
// @Override public boolean containsAll(final Collection<?> objects) { notImplemented(); return false; }
// @Override public boolean addAll(final Collection<? extends Double> doubles) { notImplemented(); return false; }
// @Override public boolean addAll(final int i, final Collection<? extends Double> doubles) { notImplemented(); return false; }
// @Override public boolean removeAll(final Collection<?> objects) { notImplemented(); return false; }
// @Override public boolean retainAll(final Collection<?> objects) { notImplemented(); return false; }
// @Override public void clear() { notImplemented(); }
// @Override public void add(final int i, final Double aDouble) { notImplemented(); }
// @Override public Double remove(final int i) { notImplemented(); return null; }
// @Override public int indexOf(final Object o) { notImplemented(); return -1; }
// @Override public int lastIndexOf(final Object o) { notImplemented(); return 0; }
// @Override public ListIterator<Double> listIterator() { notImplemented(); return null; }
// @Override public ListIterator<Double> listIterator(final int i) { notImplemented(); return null; }
// @Override public List<Double> subList(final int i, final int i1) { notImplemented(); return null; }
// -------------------------------------------------------------------------------------
//

View File

@ -1296,7 +1296,7 @@ public class VariantContext implements Feature { // to enable tribble integratio
private final Object decodeValue(final String field, final Object value, final VCFCompoundHeaderLine format) {
if ( value instanceof String ) {
if ( field.equals(VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY) )
return value.equals(".") ? null : new GenotypeLikelihoods((String)value);
return GenotypeLikelihoods.fromPLField((String)value);
final String string = (String)value;
if ( string.indexOf(",") != -1 ) {

View File

@ -242,7 +242,13 @@ public class VariantContextBuilder {
* @return
*/
public VariantContextBuilder filters(final String ... filters) {
filters(new HashSet<String>(Arrays.asList(filters)));
filters(new LinkedHashSet<String>(Arrays.asList(filters)));
return this;
}
public VariantContextBuilder filter(final String filter) {
if ( this.filters == null ) this.filters = new LinkedHashSet<String>(1);
this.filters.add(filter);
return this;
}

View File

@ -209,8 +209,8 @@ class BCF2Writer extends IndexingVariantContextWriter {
addGQ(vc);
} else if ( field.equals(VCFConstants.GENOTYPE_FILTER_KEY) ) {
addGenotypeFilters(vc);
// } else if ( field.equals(VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY) ) {
// addPLs(vc);
} else if ( field.equals(VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY) ) {
addPLs(vc);
} else {
addGenericGenotypeField(vc, field);
}
@ -350,18 +350,33 @@ class BCF2Writer extends IndexingVariantContextWriter {
}
}
// private final void addPLs(final VariantContext vc) throws IOException {
// startGenotypeField(VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY, 1, BCF2Type.INT16);
// for ( final Genotype g : vc.getGenotypes() ) {
// if ( g.hasLog10PError() ) {
// final int GQ = (int)Math.round(Math.min(g.getPhredScaledQual(), VCFConstants.MAX_GENOTYPE_QUAL));
// if ( GQ > VCFConstants.MAX_GENOTYPE_QUAL ) throw new ReviewedStingException("Unexpectedly large GQ " + GQ + " at " + vc);
// encoder.encodeRawValue(GQ, BCF2Type.INT8);
// } else {
// encoder.encodeRawMissingValues(1, BCF2Type.INT8);
// }
// }
// }
/**
* Horrible special case to deal with the GenotypeLikelihoods class
* @param vc
* @throws IOException
*/
private final void addPLs(final VariantContext vc) throws IOException {
final String field = VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY;
final int numPLs = getNGenotypeFieldValues(field, vc);
final int[] allPLs = new int[numPLs * vc.getNSamples()];
// collect all of the PLs into a single vector of values
int i = 0;
for ( final Genotype g : vc.getGenotypes() ) {
final GenotypeLikelihoods gls = g.getLikelihoods();
final int[] pls = gls != null ? g.getLikelihoods().getAsPLs() : null;
if ( pls == null )
for ( int j = 0; j < numPLs; j++) allPLs[i++] = -1;
else
for ( int pl : pls ) allPLs[i++] = pl;
}
// determine the best size
final BCF2Type type = encoder.determineIntegerType(allPLs);
startGenotypeField(field, numPLs, type);
for ( int pl : allPLs )
encoder.encodePrimitive(pl == -1 ? type.getMissingBytes() : pl, type);
}
private final void addGenotypes(final VariantContext vc) throws IOException {
if ( vc.getNAlleles() > 127 )

View File

@ -29,6 +29,7 @@ package org.broadinstitute.sting.utils.codecs.bcf2;
// the imports for unit testing.
import org.apache.commons.lang.ArrayUtils;
import org.broadinstitute.sting.BaseTest;
import org.broadinstitute.sting.utils.exceptions.StingException;
import org.testng.Assert;
@ -239,6 +240,32 @@ public class BCF2EncoderDecoderUnitTest extends BaseTest {
}
}
@DataProvider(name = "BestIntTypeTests")
public Object[][] BestIntTypeTests() {
List<Object[]> tests = new ArrayList<Object[]>();
tests.add(new Object[]{Arrays.asList(1), BCF2Type.INT8});
tests.add(new Object[]{Arrays.asList(1, 10), BCF2Type.INT8});
tests.add(new Object[]{Arrays.asList(1, 10, 100), BCF2Type.INT8});
tests.add(new Object[]{Arrays.asList(1, -1), BCF2Type.INT8});
tests.add(new Object[]{Arrays.asList(1, 1000), BCF2Type.INT16});
tests.add(new Object[]{Arrays.asList(1, 1000, 10), BCF2Type.INT16});
tests.add(new Object[]{Arrays.asList(1, 1000, 100), BCF2Type.INT16});
tests.add(new Object[]{Arrays.asList(1000), BCF2Type.INT16});
tests.add(new Object[]{Arrays.asList(100000), BCF2Type.INT32});
tests.add(new Object[]{Arrays.asList(100000, 10), BCF2Type.INT32});
tests.add(new Object[]{Arrays.asList(100000, 100), BCF2Type.INT32});
tests.add(new Object[]{Arrays.asList(100000, 1, -10), BCF2Type.INT32});
tests.add(new Object[]{Arrays.asList(-100000, 1, -10), BCF2Type.INT32});
return tests.toArray(new Object[][]{});
}
@Test(dataProvider = "BestIntTypeTests")
public void determineBestEncoding(final List<Integer> ints, final BCF2Type expectedType) throws IOException {
BCF2Encoder encoder = new BCF2Encoder();
Assert.assertEquals(encoder.determineIntegerType(ints), expectedType);
Assert.assertEquals(encoder.determineIntegerType(ArrayUtils.toPrimitive(ints.toArray(new Integer[0]))), expectedType);
}
@Test(dataProvider = "BCF2EncodingTestProviderBasicTypes")
public void testBCF2EncodingVectorsWithMissing(final List<BCF2TypedValue> toEncode) throws IOException {
for ( final BCF2TypedValue tv : toEncode ) {

View File

@ -47,14 +47,14 @@ public class GenotypeLikelihoodsUnitTest {
@Test
public void testFromVector2() {
GenotypeLikelihoods gl = new GenotypeLikelihoods(v);
GenotypeLikelihoods gl = GenotypeLikelihoods.fromLog10Likelihoods(v);
assertDoubleArraysAreEqual(gl.getAsVector(), v);
Assert.assertEquals(gl.getAsString(), vPLString);
}
@Test
public void testFromString1() {
GenotypeLikelihoods gl = new GenotypeLikelihoods(vPLString);
GenotypeLikelihoods gl = GenotypeLikelihoods.fromPLField(vPLString);
assertDoubleArraysAreEqual(gl.getAsVector(), new double[]{-9.3, 0, -3.9});
Assert.assertEquals(gl.getAsString(), vPLString);
}
@ -68,13 +68,13 @@ public class GenotypeLikelihoodsUnitTest {
@Test (expectedExceptions = UserException.MalformedVCF.class)
public void testErrorBadFormat() {
GenotypeLikelihoods gl = new GenotypeLikelihoods("adf,b,c");
GenotypeLikelihoods gl = GenotypeLikelihoods.fromPLField("adf,b,c");
gl.getAsVector();
}
@Test
public void testGetAsMap(){
GenotypeLikelihoods gl = new GenotypeLikelihoods(v);
GenotypeLikelihoods gl = GenotypeLikelihoods.fromLog10Likelihoods(v);
//Log scale
EnumMap<Genotype.Type,Double> glMap = gl.getAsMap(false);
Assert.assertEquals(v[Genotype.Type.HOM_REF.ordinal()-1],glMap.get(Genotype.Type.HOM_REF));
@ -89,7 +89,7 @@ public class GenotypeLikelihoodsUnitTest {
Assert.assertEquals(vl[Genotype.Type.HOM_VAR.ordinal()-1],glMap.get(Genotype.Type.HOM_VAR));
//Test missing likelihoods
gl = new GenotypeLikelihoods(".");
gl = GenotypeLikelihoods.fromPLField(".");
glMap = gl.getAsMap(false);
Assert.assertNull(glMap);
@ -108,7 +108,7 @@ public class GenotypeLikelihoodsUnitTest {
@Test
public void testGetLog10GQ(){
GenotypeLikelihoods gl = new GenotypeLikelihoods(vPLString);
GenotypeLikelihoods gl = GenotypeLikelihoods.fromPLField(vPLString);
//GQ for the best guess genotype
Assert.assertEquals(gl.getLog10GQ(Genotype.Type.HET),-3.9);
@ -120,7 +120,7 @@ public class GenotypeLikelihoodsUnitTest {
Assert.assertEquals(gl.getLog10GQ(Genotype.Type.HOM_VAR), Math.log10(1.0 - test[Genotype.Type.HOM_VAR.ordinal()-1]));
//Test missing likelihoods
gl = new GenotypeLikelihoods(".");
gl = GenotypeLikelihoods.fromPLField(".");
Assert.assertEquals(gl.getLog10GQ(Genotype.Type.HOM_REF),Double.NEGATIVE_INFINITY);
Assert.assertEquals(gl.getLog10GQ(Genotype.Type.HET),Double.NEGATIVE_INFINITY);
Assert.assertEquals(gl.getLog10GQ(Genotype.Type.HOM_VAR),Double.NEGATIVE_INFINITY);

View File

@ -50,7 +50,7 @@ public class VCFJarClassLoadingUnitTest {
ClassLoader classLoader = new URLClassLoader(jarURLs, null);
classLoader.loadClass("org.broadinstitute.sting.utils.variantcontext.VariantContext");
classLoader.loadClass("org.broadinstitute.sting.utils.codecs.bcf.BCF2Codec");
classLoader.loadClass("org.broadinstitute.sting.utils.codecs.bcf2.BCF2Codec");
classLoader.loadClass("org.broadinstitute.sting.utils.codecs.vcf.VCFCodec");
classLoader.loadClass("org.broadinstitute.sting.utils.codecs.vcf.VCF3Codec");
classLoader.loadClass("org.broadinstitute.sting.utils.variantcontext.writer.VariantContextWriter");

View File

@ -296,22 +296,30 @@ public class VariantContextTestProvider {
}
if ( ENABLE_PL_TESTS ) {
// testing PLs
addGenotypeTests(site,
new Genotype("g1", Arrays.asList(ref, ref), -1, new double[]{0, -1, -2}),
new Genotype("g2", Arrays.asList(ref, ref), -1, new double[]{0, -2, -3}));
if ( site.getNAlleles() == 2 ) {
// testing PLs
addGenotypeTests(site,
new Genotype("g1", Arrays.asList(ref, ref), -1, new double[]{0, -1, -2}),
new Genotype("g2", Arrays.asList(ref, ref), -1, new double[]{0, -2, -3}));
addGenotypeTests(site,
new Genotype("g1", Arrays.asList(ref, ref), -1, new double[]{-1, 0, -2}),
new Genotype("g2", Arrays.asList(ref, ref), -1, new double[]{0, -2, -3}));
addGenotypeTests(site,
new Genotype("g1", Arrays.asList(ref, ref), -1, new double[]{-1, 0, -2}),
new Genotype("g2", Arrays.asList(ref, ref), -1, new double[]{0, -2, -3}));
addGenotypeTests(site,
new Genotype("g1", Arrays.asList(ref, ref), -1, new double[]{-1, 0, -2}),
new Genotype("g2", Arrays.asList(ref, ref), -1, new double[]{0, -2000, -1000}));
addGenotypeTests(site,
new Genotype("g1", Arrays.asList(ref, ref), -1, new double[]{-1, 0, -2}),
new Genotype("g2", Arrays.asList(ref, ref), -1, new double[]{0, -2000, -1000}));
addGenotypeTests(site, // missing PLs
new Genotype("g1", Arrays.asList(ref, ref), -1, new double[]{-1, 0, -2}),
new Genotype("g2", Arrays.asList(ref, ref), -1));
addGenotypeTests(site, // missing PLs
new Genotype("g1", Arrays.asList(ref, ref), -1, new double[]{-1, 0, -2}),
new Genotype("g2", Arrays.asList(ref, ref), -1));
}
else if ( site.getNAlleles() == 3 ) {
// testing PLs
addGenotypeTests(site,
new Genotype("g1", Arrays.asList(ref, ref), -1, new double[]{0, -1, -2, -3, -4, -5}),
new Genotype("g2", Arrays.asList(ref, ref), -1, new double[]{0, -2, -3, -4, -5, -6}));
}
}
// test attributes