/*
* PatternListGenerator.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.app.beauti.generator;
import dr.app.beauti.components.ComponentFactory;
import dr.app.beauti.components.ancestralstates.AncestralStatesComponentOptions;
import dr.app.beauti.components.sequenceerror.SequenceErrorModelComponentOptions;
import dr.app.beauti.options.BeautiOptions;
import dr.app.beauti.options.PartitionData;
import dr.app.beauti.options.PartitionPattern;
import dr.app.beauti.options.PartitionSubstitutionModel;
import dr.app.beauti.types.BinaryModelType;
import dr.app.beauti.util.XMLWriter;
import dr.evolution.alignment.Alignment;
import dr.evolution.alignment.Patterns;
import dr.evolution.alignment.SitePatterns;
import dr.evolution.datatype.DataType;
import dr.evolution.datatype.Microsatellite;
import dr.evolution.datatype.Nucleotides;
import dr.evoxml.*;
import dr.util.Attribute;
import dr.xml.XMLParser;
import java.util.ArrayList;
import java.util.List;
/**
* @author Alexei Drummond
* @author Andrew Rambaut
* @author Walter Xie
*/
public class PatternListGenerator extends Generator {
public PatternListGenerator(BeautiOptions options, ComponentFactory[] components) {
super(options, components);
}
/**
* Writes the pattern lists
*
* @param partition the partition data to write the pattern lists for
* @param writer the writer
*/
public void writePatternList(PartitionData partition, XMLWriter writer) {
writer.writeText("");
AncestralStatesComponentOptions ancestralStatesOptions = (AncestralStatesComponentOptions) options
.getComponentOptions(AncestralStatesComponentOptions.class);
SequenceErrorModelComponentOptions sequenceErrorOptions = (SequenceErrorModelComponentOptions) options
.getComponentOptions(SequenceErrorModelComponentOptions.class);
PartitionSubstitutionModel model = partition.getPartitionSubstitutionModel();
String codonHeteroPattern = model.getCodonHeteroPattern();
int partitionCount = model.getCodonPartitionCount();
boolean isAncestralStatesModel = (!ancestralStatesOptions.usingAncestralStates(partition) &&
!sequenceErrorOptions.usingSequenceErrorModel(partition));
boolean isCovarionModel = model.getDataType().getType() == DataType.COVARION
&& model.getBinarySubstitutionModel() == BinaryModelType.BIN_COVARION;
boolean unique = isAncestralStatesModel || isCovarionModel;
boolean strip = isAncestralStatesModel || isCovarionModel;
if (model.getDataType().getType() == DataType.NUCLEOTIDES && codonHeteroPattern != null && partitionCount > 1) {
if (codonHeteroPattern.equals("112")) {
writer.writeComment("The " + (unique ? "unique " : "") + "patterns for codon positions 1 & 2");
writer.writeOpenTag(MergePatternsParser.MERGE_PATTERNS,
new Attribute[]{
new Attribute.Default<String>(XMLParser.ID, partition.getPrefix() + model.getPrefixCodon(1) + SitePatternsParser.PATTERNS),
}
);
writePatternList(partition, 0, 3, null, unique, strip, writer);
writePatternList(partition, 1, 3, null, unique, strip, writer);
writer.writeCloseTag(MergePatternsParser.MERGE_PATTERNS);
writer.writeComment("The " + (unique ? "unique " : "") + "patterns for codon position 3");
writePatternList(partition, 2, 3, model.getPrefixCodon(2), unique, strip, writer);
} else {
// pattern is 123
for (int i = 1; i <= 3; i++) {
writer.writeComment("The " + (unique ? "unique " : "") + "patterns for codon position " + i);
writePatternList(partition, i - 1, 3, model.getPrefixCodon(i), unique, strip, writer);
}
}// END: pattern is 123
} else {
writePatternList(partition, 0, 1, "", unique, strip, writer);
}
}
/**
* Write a single pattern list
*
* @param partition the partition to write a pattern list for
* @param offset offset by
* @param every skip every
* @param writer the writer
*/
private void writePatternList(final PartitionData partition, int offset, int every, final String codonPrefix, final boolean unique, final boolean strip, final XMLWriter writer) {
Alignment alignment = partition.getAlignment();
int from = partition.getFromSite();
int to = partition.getToSite();
int partEvery = partition.getEvery();
if (partEvery > 1 && every > 1) throw new IllegalArgumentException();
if (from < 1) from = 1;
every = Math.max(partEvery, every);
from += offset;
// this object is created solely to calculate the number of patterns in the alignment
SitePatterns patterns = new SitePatterns(alignment, null, from - 1, to - 1, every, strip, unique);
writer.writeComment("The " + (unique ? "unique " : "") + "patterns from " + from + " to " + (to > 0 ? to : "end") + ((every > 1) ? " every " + every : ""),
"npatterns=" + patterns.getPatternCount());
List<Attribute> attributes = new ArrayList<Attribute>();
// no 11 of 112 codon, which uses mergePatterns id instead
if (codonPrefix != null) {
attributes.add(new Attribute.Default<String>(XMLParser.ID, partition.getPrefix() + codonPrefix + SitePatternsParser.PATTERNS));
}
attributes.add(new Attribute.Default<String>("from", "" + from));
if (to >= 0) attributes.add(new Attribute.Default<String>("to", "" + to));
if (every > 1) {
attributes.add(new Attribute.Default<String>("every", "" + every));
}
if(!unique) {
attributes.add(new Attribute.Default<Boolean>(SitePatternsParser.UNIQUE, false));
}
if (strip) {
attributes.add(new Attribute.Default<Boolean>(SitePatternsParser.STRIP, false)); // default true
}
// generate <patterns>
writer.writeOpenTag(SitePatternsParser.PATTERNS, attributes);
writer.writeIDref(AlignmentParser.ALIGNMENT, alignment.getId());
writer.writeCloseTag(SitePatternsParser.PATTERNS);
}
/**
* Micro-sat
* @param partition
* @param microsatList
* @param writer
*/
public void writePatternList(PartitionPattern partition, List<Microsatellite> microsatList, XMLWriter writer) throws GeneratorException {
PartitionSubstitutionModel model = partition.getPartitionSubstitutionModel();
if (model.getDataType().getType() == DataType.MICRO_SAT) {
Patterns patterns = partition.getPatterns();
writer.writeComment("The patterns for microsatellite");
writer.writeOpenTag(MicrosatellitePatternParser.MICROSATPATTERN,
new Attribute[]{
new Attribute.Default<String>(XMLParser.ID, partition.getName()),
});
if (options.hasIdenticalTaxa() && !patterns.hasMask()) {
writer.writeIDref(TaxaParser.TAXA, TaxaParser.TAXA);
} else {
writer.writeIDref(TaxaParser.TAXA, partition.getName() + "." + TaxaParser.TAXA);
}
Microsatellite m = model.getMicrosatellite();
if (m == null) throw new GeneratorException("Microsatellite is null in partition:\n" + partition.getName());
if (!microsatList.contains(m)) {
microsatList.add(m);
writer.writeTag(MicrosatelliteParser.MICROSAT,
new Attribute[]{
new Attribute.Default<String>(XMLParser.ID, m.getName()),
new Attribute.Default<Integer>(MicrosatelliteParser.MAX, m.getMax()),
new Attribute.Default<Integer>(MicrosatelliteParser.MIN, m.getMin()),
new Attribute.Default<Integer>(MicrosatelliteParser.UNIT_LENGTH, m.getUnitLength()),
}, true);
} else {
writer.writeTag(MicrosatelliteParser.MICROSAT,
new Attribute[]{
new Attribute.Default<String>(XMLParser.IDREF, m.getName()),
}, true);
}
writer.writeOpenTag(MicrosatellitePatternParser.MICROSAT_SEQ);
String seq = "";
int c = 0;
for (int i = 0; i < patterns.getTaxonCount(); i++) {
if (!patterns.isMasked(i)) {
if (c > 0) seq += ",";
int state = patterns.getPatternState(i, 0);
if (state == Microsatellite.UNKNOWN_STATE_LENGTH) {
seq += Microsatellite.UNKNOWN_CHARACTER;
} else {
seq += Integer.toString(state);
}
c++;
}
}
writer.writeText(seq);
writer.writeCloseTag(MicrosatellitePatternParser.MICROSAT_SEQ);
writer.writeCloseTag(MicrosatellitePatternParser.MICROSATPATTERN);
} else {
}
}
}