/*
* AncestralStatesOptionsPanel.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.ancestralStatesPanel;
import dr.app.beauti.components.ancestralstates.AncestralStatesComponentOptions;
import dr.app.beauti.components.sequenceerror.SequenceErrorModelComponentOptions;
import dr.app.beauti.options.AbstractPartitionData;
import dr.app.beauti.options.BeautiOptions;
import dr.app.beauti.types.SequenceErrorType;
import dr.app.beauti.util.PanelUtils;
import dr.app.util.OSType;
import dr.evolution.datatype.DataType;
import dr.evolution.util.Taxa;
import jam.panels.OptionsPanel;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
/**
* @author Alexei Drummond
* @author Andrew Rambaut
* @author Walter Xie
*/
public class AncestralStatesOptionsPanel extends OptionsPanel {
private static final String ROBUST_COUNTING_TOOL_TIP = "<html>"
+ "Enable counting of reconstructed number of substitutions as described in<br>"
+ "Minin & Suchard (2008). These will be annotated directly in the<br>"
+ "logged trees.</html>";
private static final String DNDS_ROBUST_COUNTING_TOOL_TIP = "<html>"
+ "Enable counting of synonymous and non-synonymous substitution as described in<br>"
+ "O'Brien, Minin & Suchard (2009) and Lemey, Minin, Bielejec, Kosakovsky-Pond &<br>"
+ "Suchard (2012). This model requires a 3-partition codon model to be<br>"
+ "selected in the Site model for this partition and NO Site Heterogeneity Model.</html>";
private static final String COMPLETE_HISTORY_LOGGING_TOOL_TIP = "<html>"
+ "Log a complete history of realised state changes to the tree log file.<br>"
+ "This can make the files very large but can be useful for post hoc analysis.</html>";
// Components
private static final long serialVersionUID = -1645661616353099424L;
private final AbstractPartitionData partition;
private JCheckBox ancestralReconstructionCheck = new JCheckBox(
"Reconstruct states at all ancestors");
private JCheckBox mrcaReconstructionCheck = new JCheckBox(
"Reconstruct states at ancestor:");
private JComboBox mrcaReconstructionCombo = new JComboBox();
private JCheckBox countingCheck = new JCheckBox(
"Reconstruct state change counts");
private JCheckBox dNdSRobustCountingCheck = new JCheckBox(
"Reconstruct synonymous/non-synonymous change counts");
private JCheckBox completeHistoryLoggingCheck = new JCheckBox(
"Reconstruct complete change history on tree");
private JTextArea dNnSText = new JTextArea(
"This model requires a 3-partition codon model to be selected in the Site model " +
"for this partition and NO Site Heterogeneity Model before it can be selected.");
// dNdS robust counting is automatic if RC is turned on for a codon
// partitioned data set.
// private JCheckBox dNdSCountingCheck = new JCheckBox(
// "Reconstruct synonymous/non-synonymous counts");
final BeautiOptions options;
// JComboBox errorModelCombo = new JComboBox(EnumSet.range(SequenceErrorType.NO_ERROR, SequenceErrorType.BASE_ALL).toArray());
JComboBox errorModelCombo = new JComboBox(SequenceErrorType.values());
AncestralStatesComponentOptions ancestralStatesComponent;
SequenceErrorModelComponentOptions sequenceErrorComponent;
public AncestralStatesOptionsPanel(final AncestralStatesPanel ancestralStatesPanel, final BeautiOptions options, final AbstractPartitionData partition) {
super(12, (OSType.isMac() ? 6 : 24));
setOpaque(false);
this.partition = partition;
this.options = options;
PanelUtils.setupComponent(ancestralReconstructionCheck);
ancestralReconstructionCheck
.setToolTipText("<html>"
+ "Reconstruct posterior realizations of the states at ancestral nodes.<br>" +
"These will be annotated directly in the logged trees.</html>");
PanelUtils.setupComponent(mrcaReconstructionCheck);
mrcaReconstructionCheck
.setToolTipText("<html>"
+ "Reconstruct posterior realizations of the states at a specific common<br>" +
"ancestor defined by a taxon set. This will be recorded in the log file.</html>");
PanelUtils.setupComponent(mrcaReconstructionCombo);
mrcaReconstructionCombo
.setToolTipText("<html>"
+ "Reconstruct posterior realizations of the states at a specific common.<br>" +
"ancestor defined by a taxon set. This will be recorded in the log file.</html>");
PanelUtils.setupComponent(countingCheck);
countingCheck.setToolTipText(ROBUST_COUNTING_TOOL_TIP);
PanelUtils.setupComponent(dNdSRobustCountingCheck);
dNdSRobustCountingCheck.setToolTipText(DNDS_ROBUST_COUNTING_TOOL_TIP);
PanelUtils.setupComponent(completeHistoryLoggingCheck);
completeHistoryLoggingCheck.setToolTipText(COMPLETE_HISTORY_LOGGING_TOOL_TIP);
// ////////////////////////
PanelUtils.setupComponent(errorModelCombo);
errorModelCombo.setToolTipText("<html>Select how to model sequence error or<br>"
+ "post-mortem DNA damage.</html>");
// Set the initial options
ancestralStatesComponent = (AncestralStatesComponentOptions)options.getComponentOptions(AncestralStatesComponentOptions.class);
// ancestralStatesComponent.createParameters(options);
ancestralReconstructionCheck.setSelected(ancestralStatesComponent.reconstructAtNodes(partition));
mrcaReconstructionCheck.setSelected(ancestralStatesComponent.reconstructAtMRCA(partition));
mrcaReconstructionCombo.setSelectedItem(ancestralStatesComponent.getMRCATaxonSet(partition));
countingCheck.setSelected(ancestralStatesComponent.isCountingStates(partition));
dNdSRobustCountingCheck.setSelected(ancestralStatesComponent.dNdSRobustCounting(partition));
completeHistoryLoggingCheck.setSelected(ancestralStatesComponent.isCompleteHistoryLogging(partition));
sequenceErrorComponent = (SequenceErrorModelComponentOptions)options.getComponentOptions(SequenceErrorModelComponentOptions.class);
// sequenceErrorComponent.createParameters(options); // this cannot create correct param here, because of improper design
errorModelCombo.setSelectedItem(sequenceErrorComponent.getSequenceErrorType(partition));
setupPanel();
ItemListener listener = new ItemListener() {
public void itemStateChanged(final ItemEvent itemEvent) {
optionsChanged();
ancestralStatesPanel.fireModelChanged();
// The following is only necessary is simpleCounting XOR robustCounting
// if (itemEvent.getItem() == countingCheck) {
// boolean enableRC = !countingCheck.isSelected() && ancestralStatesComponent.dNdSRobustCountingAvailable(partition);
// dNdSRobustCountingCheck.setEnabled(enableRC);
// dNnSText.setEnabled(enableRC);
// }
//
// if (itemEvent.getItem() == dNdSRobustCountingCheck) {
// boolean enableSimpleCounting = !dNdSRobustCountingCheck.isSelected();
// countingCheck.setEnabled(enableSimpleCounting);
// }
completeHistoryLoggingCheck.setEnabled(countingCheck.isSelected() || dNdSRobustCountingCheck.isSelected());
}
};
ancestralReconstructionCheck.addItemListener(listener);
mrcaReconstructionCheck.addItemListener(listener);
mrcaReconstructionCombo.addItemListener(listener);
countingCheck.addItemListener(listener);
dNdSRobustCountingCheck.addItemListener(listener);
completeHistoryLoggingCheck.addItemListener(listener);
errorModelCombo.addItemListener(listener);
}
private void optionsChanged() {
if (isUpdating) return;
ancestralStatesComponent.setReconstructAtNodes(partition, ancestralReconstructionCheck.isSelected());
ancestralStatesComponent.setReconstructAtMRCA(partition, mrcaReconstructionCheck.isSelected());
mrcaReconstructionCombo.setEnabled(mrcaReconstructionCheck.isSelected());
if (mrcaReconstructionCombo.getSelectedIndex() == 0) {
// root node
ancestralStatesComponent.setMRCATaxonSet(partition, null);
} else {
String text = (String) mrcaReconstructionCombo.getSelectedItem();
String taxonSetId = text.substring(5,text.length() - 1);
ancestralStatesComponent.setMRCATaxonSet(partition, taxonSetId);
}
ancestralStatesComponent.setCountingStates(partition, countingCheck.isSelected());
// ancestralStatesComponent.setDNdSRobustCounting(partition, robustCountingCheck.isSelected());
ancestralStatesComponent.setDNdSRobustCounting(partition, dNdSRobustCountingCheck.isSelected());
ancestralStatesComponent.setCompleteHistoryLogging(partition, completeHistoryLoggingCheck.isSelected());
sequenceErrorComponent.setSequenceErrorType(partition, (SequenceErrorType)errorModelCombo.getSelectedItem());
sequenceErrorComponent.createParameters(options);
}
/**
* Lays out the appropriate components in the panel for this partition
* model.
*/
void setupPanel() {
isUpdating = true;
String selectedItem = (String)mrcaReconstructionCombo.getSelectedItem();
if (mrcaReconstructionCombo.getItemCount() > 0) {
mrcaReconstructionCombo.removeAllItems();
}
mrcaReconstructionCombo.addItem("Tree Root");
if (options.taxonSets.size() > 0) {
for (Taxa taxonSet : options.taxonSets) {
mrcaReconstructionCombo.addItem("MRCA("+ taxonSet.getId() + ")");
}
if (selectedItem != null) {
mrcaReconstructionCombo.setSelectedItem(selectedItem);
}
}
mrcaReconstructionCombo.setEnabled(mrcaReconstructionCheck.isSelected());
boolean ancestralReconstructionAvailable = true;
boolean countingAvailable = true;
boolean dNdSRobustCountingAvailable = false;
boolean errorModelAvailable = false;
switch (partition.getDataType().getType()) {
case DataType.NUCLEOTIDES:
errorModelAvailable = true;
dNdSRobustCountingAvailable = true; // but will be disabled if not codon partitioned
break;
case DataType.AMINO_ACIDS:
case DataType.GENERAL:
case DataType.TWO_STATES:
break;
case DataType.CONTINUOUS:
countingAvailable = false;
break;
case DataType.MICRO_SAT:
ancestralReconstructionAvailable = false;
countingAvailable = false;
break;
default:
throw new IllegalArgumentException("Unsupported data type");
}
removeAll();
if (ancestralReconstructionAvailable) {
if (partition.getPartitionSubstitutionModel().getCodonPartitionCount() == 2) {
// mergedPatterns for codon positions 1&2 will always be compressed...
// so cannot do any of this stuff. Disable it and provide an explanation.
addSpanningComponent(new JLabel("<html>Unable to provide these options with the 1+2,3 codon<br>" +
"position model. Use a 1,2,3 codon position model instead.<br><html>"));
}
JLabel label1 = new JLabel("Ancestral State Reconstruction:");
addSpanningComponent(label1);
addComponent(ancestralReconstructionCheck);
FlowLayout layout = new FlowLayout(FlowLayout.LEFT);
layout.setHgap(0);
JPanel panel = new JPanel(layout);
panel.setOpaque(false);
panel.setBorder(BorderFactory.createEmptyBorder(0,0,0,0));
panel.add(mrcaReconstructionCheck);
panel.add(mrcaReconstructionCombo);
addComponent(panel);
boolean enabled = true;
if (partition.getPartitionSubstitutionModel().getCodonPartitionCount() == 2) {
// mergedPatterns for codon positions 1&2 will always be compressed...
// so cannot do any of this stuff. Disable it and provide an explanation.
ancestralReconstructionCheck.setEnabled(false);
enabled = false;
}
label1.setEnabled(enabled);
panel.setEnabled(enabled);
ancestralReconstructionCheck.setEnabled(enabled);
mrcaReconstructionCheck.setEnabled(enabled);
mrcaReconstructionCombo.setEnabled(enabled);
}
if (countingAvailable) {
if (ancestralReconstructionAvailable) {
addSeparator();
}
JLabel label2 = new JLabel("State Change Count Reconstruction:");
addSpanningComponent(label2);
JTextArea text1 = new JTextArea(
"Select this option to reconstruct counts of state changes using " +
"Markov Jumps. This approach is described in Minin & Suchard (2008).");
text1.setColumns(40);
PanelUtils.setupComponent(text1);
addComponent(text1);
addComponent(countingCheck);
boolean enableSimpleCounting = true;
// TODO Simple counting is currently not available for codon partitioned models due to BEAUti limitation
if (ancestralStatesComponent.dNdSRobustCountingAvailable(partition) || partition.getPartitionSubstitutionModel().getCodonPartitionCount() == 2) {
enableSimpleCounting = false;
countingCheck.setSelected(false);
}
countingCheck.setEnabled(enableSimpleCounting);
label2.setEnabled(enableSimpleCounting);
text1.setEnabled(enableSimpleCounting);
JTextArea text2 = null;
if (dNdSRobustCountingAvailable) {
// addSeparator();
text2 = new JTextArea(
"Renaissance counting: select this option to reconstruct counts of synonymous and nonsynonymous " +
"changes using Robust Counting. This approach is described in O'Brien, Minin " +
"& Suchard (2009) and Lemey, Minin, Bielejec, Kosakovsky-Pond & Suchard " +
"(2012):");
text2.setColumns(40);
PanelUtils.setupComponent(text2);
addComponent(text2);
addComponent(dNdSRobustCountingCheck);
dNnSText.setColumns(40);
dNnSText.setBorder(BorderFactory.createEmptyBorder(0, 32, 0, 0));
PanelUtils.setupComponent(dNnSText);
addComponent(dNnSText);
boolean enableRC = ancestralStatesComponent.dNdSRobustCountingAvailable(partition);
// && !ancestralStatesComponent.isCountingStates(partition);
dNdSRobustCountingCheck.setEnabled(enableRC);
ancestralStatesComponent.setDNdSRobustCounting(partition, enableRC && dNdSRobustCountingCheck.isSelected());
text2.setEnabled(enableRC);
dNnSText.setEnabled(enableRC);
if (!enableRC) {
dNdSRobustCountingCheck.setSelected(false);
}
}
addComponent(completeHistoryLoggingCheck);
completeHistoryLoggingCheck.setEnabled(countingCheck.isSelected() || dNdSRobustCountingCheck.isSelected());
}
if (errorModelAvailable) {
if (ancestralReconstructionAvailable || countingAvailable) {
addSeparator();
}
JLabel label3 = new JLabel("Sequence error model:");
addSpanningComponent(label3);
JLabel label4 = addComponentWithLabel("Error Model:", errorModelCombo);
boolean enabled = (partition.getPartitionSubstitutionModel().getCodonPartitionCount() != 2);
label3.setEnabled(enabled);
label4.setEnabled(enabled);
errorModelCombo.setEnabled(enabled);
}
isUpdating = false;
}
private boolean isUpdating = false;
}