Skip to content

Commit b70ba65

Browse files
committed
Improved migration rate matrix and popsize GUI.
1 parent 130f342 commit b70ba65

File tree

6 files changed

+145
-42
lines changed

6 files changed

+145
-42
lines changed

src/beast/app/multitypetree/beauti/InitMigrationModelConnector.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,7 @@
2525
import beast.evolution.tree.StructuredCoalescentMultiTypeTree;
2626
import beast.evolution.tree.TraitSet;
2727

28-
import java.util.HashSet;
29-
import java.util.List;
30-
import java.util.Set;
28+
import java.util.*;
3129

3230

3331
/**
@@ -41,15 +39,15 @@
4139
*/
4240
public class InitMigrationModelConnector {
4341

44-
public static int uniqueTraitsInData(StructuredCoalescentMultiTypeTree scTree) {
45-
Set<String> uniqueTypes = new HashSet<>();
42+
public static List<String> uniqueTraitsInData(StructuredCoalescentMultiTypeTree scTree) {
43+
SortedSet<String> uniqueTypes = new TreeSet<>();
4644
TraitSet typeTraitSet = scTree.typeTraitInput.get();
4745
for (String taxonName : typeTraitSet.taxaInput.get().getTaxaNames())
4846
uniqueTypes.add(typeTraitSet.getStringValue(taxonName));
4947

50-
return uniqueTypes.size();
48+
return new ArrayList<>(uniqueTypes);
5149
}
52-
50+
5351
public static boolean customConnector(BeautiDoc doc) {
5452

5553
for (BEASTInterface p : doc.getPartitions("Tree")) {
@@ -69,7 +67,7 @@ public static boolean customConnector(BeautiDoc doc) {
6967
String popSizesStr = getParameterString((RealParameter)migModel.popSizesInput.get());
7068

7169
// Ensure model has minimum number of demes
72-
int uniqueTraitCount = uniqueTraitsInData(tree);
70+
int uniqueTraitCount = uniqueTraitsInData(tree).size();
7371
StringBuilder rateMatrixStrBuilder = new StringBuilder();
7472
StringBuilder popSizesStrBuilder = new StringBuilder();
7573
if (migModel.getNTypes()<uniqueTraitCount) {

src/beast/app/multitypetree/beauti/MigrationModelInputEditor.java

Lines changed: 123 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,26 +22,26 @@
2222
import beast.core.Input;
2323
import beast.core.parameter.RealParameter;
2424
import beast.evolution.tree.SCMigrationModel;
25-
import java.awt.Color;
26-
import java.awt.Component;
27-
import java.awt.Dimension;
28-
import java.awt.GridBagConstraints;
29-
import java.awt.GridBagLayout;
30-
import java.awt.Insets;
25+
import beast.evolution.tree.StructuredCoalescentMultiTypeTree;
26+
import com.sun.org.apache.xml.internal.security.Init;
27+
import multitypetree.distributions.StructuredCoalescentTreeDensity;
28+
29+
import java.awt.*;
3130
import java.awt.event.ItemEvent;
32-
import javax.swing.JCheckBox;
33-
import javax.swing.JLabel;
34-
import javax.swing.JPanel;
35-
import javax.swing.JSpinner;
36-
import javax.swing.JTable;
37-
import javax.swing.ListSelectionModel;
38-
import javax.swing.SpinnerNumberModel;
31+
import java.awt.font.TextLayout;
32+
import java.awt.image.BufferedImage;
33+
import java.util.ArrayList;
34+
import java.util.List;
35+
import javax.swing.*;
36+
import javax.swing.border.Border;
37+
import javax.swing.border.CompoundBorder;
3938
import javax.swing.border.EtchedBorder;
4039
import javax.swing.event.ChangeEvent;
4140
import javax.swing.event.TableModelEvent;
4241
import javax.swing.table.DefaultTableCellRenderer;
4342
import javax.swing.table.DefaultTableModel;
4443
import javax.swing.table.TableCellRenderer;
44+
import javax.swing.table.TableColumn;
4545

4646
/**
4747
* A BEAUti input editor for MigrationModels.
@@ -58,6 +58,8 @@ public class MigrationModelInputEditor extends InputEditor.Base {
5858

5959
boolean dimChangeInProgress = false;
6060

61+
List<String> rowNames = new ArrayList<>();
62+
6163
public MigrationModelInputEditor(BeautiDoc doc) {
6264
super(doc);
6365
}
@@ -87,7 +89,7 @@ public void init(Input<?> input, BEASTInterface beastObject, int itemNr,
8789
rateMatrixModel = new DefaultTableModel() {
8890
@Override
8991
public boolean isCellEditable(int row, int column) {
90-
return row != column;
92+
return row != column && column != migModel.getNTypes();
9193
}
9294
};
9395
popSizeEstCheckBox = new JCheckBox("estimate");
@@ -107,6 +109,7 @@ public boolean isCellEditable(int row, int column) {
107109
c.weightx = 0.0;
108110
c.anchor = GridBagConstraints.LINE_END;
109111
panel.add(new JLabel("Number of demes: "), c);
112+
110113
JSpinner dimSpinner = new JSpinner(nTypesModel);
111114
dimSpinner.setMaximumSize(new Dimension(100, Short.MAX_VALUE));
112115
c.gridx = 1;
@@ -121,7 +124,22 @@ public boolean isCellEditable(int row, int column) {
121124
c.weightx = 0.0;
122125
c.anchor = GridBagConstraints.LINE_END;
123126
panel.add(new JLabel("Population sizes: "), c);
124-
JTable popSizeTable = new JTable(popSizeModel);
127+
128+
JTable popSizeTable = new JTable(popSizeModel) {
129+
@Override
130+
public TableCellRenderer getCellRenderer(int row, int column) {
131+
return new DefaultTableCellRenderer() {
132+
@Override
133+
public Component getTableCellRendererComponent(
134+
JTable table, Object value, boolean isSelected,
135+
boolean hasFocus, int row, int column) {
136+
setHorizontalAlignment(SwingConstants.CENTER);
137+
return super.getTableCellRendererComponent(
138+
table, value, isSelected, hasFocus, row, column);
139+
}
140+
};
141+
}
142+
};
125143
popSizeTable.setShowVerticalLines(true);
126144
popSizeTable.setCellSelectionEnabled(true);
127145
popSizeTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
@@ -133,6 +151,7 @@ public boolean isCellEditable(int row, int column) {
133151
c.anchor = GridBagConstraints.LINE_START;
134152
panel.add(popSizeTable, c);
135153
popSizeEstCheckBox.setSelected(((RealParameter)migModel.popSizesInput.get()).isEstimatedInput.get());
154+
136155
c.gridx = 2;
137156
c.gridy = 1;
138157
c.anchor = GridBagConstraints.LINE_END;
@@ -149,40 +168,92 @@ public boolean isCellEditable(int row, int column) {
149168
JTable rateMatrixTable = new JTable(rateMatrixModel) {
150169
@Override
151170
public TableCellRenderer getCellRenderer(int row, int column) {
152-
if (row != column)
153-
return super.getCellRenderer(row, column);
154-
else
155-
return new DefaultTableCellRenderer() {
156-
@Override
157-
public Component getTableCellRendererComponent(
158-
JTable table, Object value, boolean isSelected,
159-
boolean hasFocus, int row, int column) {
160-
JLabel label = new JLabel();
161-
label.setOpaque(true);
162-
label.setBackground(Color.GRAY);
163-
return label;
164-
}
165-
};
171+
172+
return new DefaultTableCellRenderer() {
173+
@Override
174+
public Component getTableCellRendererComponent(
175+
JTable table, Object value, boolean isSelected,
176+
boolean hasFocus, int row, int column) {
177+
178+
179+
180+
if (row == column) {
181+
JLabel label = new JLabel();
182+
label.setOpaque(true);
183+
label.setBackground(Color.GRAY);
184+
185+
return label;
186+
187+
} else {
188+
189+
Component c = super.getTableCellRendererComponent(
190+
table, value, isSelected, hasFocus, row, column);
191+
192+
JComponent jc = (JComponent)c;
193+
if (column == migModel.getNTypes()) {
194+
c.setBackground(panel.getBackground());
195+
c.setForeground(Color.gray);
196+
setHorizontalAlignment(SwingConstants.LEFT);
197+
} else {
198+
int l = 1, r = 1, t = 1, b=1;
199+
if (column>0)
200+
l = 0;
201+
if (row>0)
202+
t = 0;
203+
204+
setBorder(BorderFactory.createMatteBorder(t, l, b, r, Color.GRAY));
205+
setHorizontalAlignment(SwingConstants.CENTER);
206+
}
207+
return c;
208+
}
209+
}
210+
};
166211
}
167212
};
168-
rateMatrixTable.setShowGrid(true);
213+
rateMatrixTable.setShowGrid(false);
214+
rateMatrixTable.setIntercellSpacing(new Dimension(0,0));
169215
rateMatrixTable.setCellSelectionEnabled(true);
170216
rateMatrixTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
171-
rateMatrixTable.setMaximumSize(new Dimension(100, Short.MAX_VALUE));
217+
rateMatrixTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
218+
TableColumn col = rateMatrixTable.getColumnModel().getColumn(migModel.getNTypes());
219+
220+
221+
FontMetrics metrics = new Canvas().getFontMetrics(getFont());
222+
int maxWidth = 0;
223+
for (String rowName : rowNames)
224+
maxWidth = Math.max(maxWidth, metrics.stringWidth(rowName + "M"));
225+
226+
col.setPreferredWidth(maxWidth);
227+
// rateMatrixTable.setMaximumSize(new Dimension(100, Short.MAX_VALUE));
172228

173229
c.gridx = 1;
174230
c.gridy = 2;
175231
c.anchor = GridBagConstraints.LINE_START;
176232
c.weightx = 1.0;
177233
panel.add(rateMatrixTable, c);
178-
rateMatrixEstCheckBox.setSelected(((RealParameter)migModel.rateMatrixInput.get()).isEstimatedInput.get());
179234

235+
rateMatrixEstCheckBox.setSelected(((RealParameter)migModel.rateMatrixInput.get()).isEstimatedInput.get());
180236
c.gridx = 2;
181237
c.gridy = 2;
182238
c.anchor = GridBagConstraints.LINE_END;
183239
c.weightx = 1.0;
184240
panel.add(rateMatrixEstCheckBox, c);
185241

242+
c.gridx = 1;
243+
c.gridy = 3;
244+
c.anchor = GridBagConstraints.LINE_START;
245+
c.weightx = 1.0;
246+
panel.add(new JLabel("Rows: sources, columns: sinks (backwards in time)"), c);
247+
248+
c.gridx = 1;
249+
c.gridy = 4;
250+
c.anchor = GridBagConstraints.LINE_START;
251+
c.weightx = 1.0;
252+
JLabel multilineLabel = new JLabel();
253+
multilineLabel.setText("<html><body>Correspondence between row/col indices<br>"
254+
+ "and deme names shown to right of matrix.</body></html>");
255+
panel.add(multilineLabel, c);
256+
186257
add(panel);
187258

188259

@@ -247,7 +318,23 @@ public void loadFromMigrationModel() {
247318
popSizeModel.setRowCount(1);
248319
popSizeModel.setColumnCount(migModel.getNTypes());
249320
rateMatrixModel.setRowCount(migModel.getNTypes());
250-
rateMatrixModel.setColumnCount(migModel.getNTypes());
321+
rateMatrixModel.setColumnCount(migModel.getNTypes()+1);
322+
323+
List<String> traitNames = null;
324+
if (m_beastObject instanceof StructuredCoalescentTreeDensity) {
325+
StructuredCoalescentTreeDensity scDensity = (StructuredCoalescentTreeDensity)m_beastObject;
326+
StructuredCoalescentMultiTypeTree scTree = (StructuredCoalescentMultiTypeTree)(scDensity.mtTreeInput.get());
327+
traitNames = InitMigrationModelConnector.uniqueTraitsInData(scTree);
328+
nTypesModel.setMinimum(Math.max(traitNames.size(),2));
329+
};
330+
331+
rowNames.clear();
332+
for (int i = 0; i < migModel.getNTypes(); i++) {
333+
if (traitNames != null && i<traitNames.size())
334+
rowNames.add(" " + traitNames.get(i) + " (" + String.valueOf(i) + ") ");
335+
else
336+
rowNames.add(" (" + String.valueOf(i) + ") ");
337+
}
251338

252339
for (int i=0; i<migModel.getNTypes(); i++) {
253340
popSizeModel.setValueAt(migModel.getPopSize(i), 0, i);
@@ -256,6 +343,8 @@ public void loadFromMigrationModel() {
256343
continue;
257344
rateMatrixModel.setValueAt(migModel.getBackwardRate(i, j), i, j);
258345
}
346+
347+
rateMatrixModel.setValueAt(rowNames.get(i), i, migModel.getNTypes());
259348
}
260349

261350
popSizeEstCheckBox.setSelected(((RealParameter)migModel.popSizesInput.get()).isEstimatedInput.get());
@@ -281,7 +370,7 @@ public void saveToMigrationModel() {
281370
StringBuilder sbRateMatrix = new StringBuilder();
282371
boolean first = true;
283372
for (int i=0; i<rateMatrixModel.getRowCount(); i++) {
284-
for (int j=0; j<rateMatrixModel.getColumnCount(); j++) {
373+
for (int j=0; j<rateMatrixModel.getColumnCount()-1; j++) {
285374
if (i == j)
286375
continue;
287376

src/beast/evolution/tree/MultiTypeTree.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,9 @@ public String getTypeString(int type) {
269269
if (!traitsProcessed)
270270
processTraits(m_traitList.get());
271271

272+
if (type>=typeList.size())
273+
throw new IllegalArgumentException("Requested name of unknown type index.");
274+
272275
return typeList.get(type);
273276
}
274277

src/multitypetree/distributions/StructuredCoalescentTreeDensity.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ public void initAndValidate() {
7575

7676
eventList = new ArrayList<>();
7777
lineageCountList = new ArrayList<>();
78+
79+
// Ensure tree and migration model are compatible
80+
if (mtTree.hasTypeTrait() && mtTree.getTypeList().size()>migrationModel.getNTypes())
81+
throw new IllegalArgumentException("There are " + mtTree.getTypeList().size()
82+
+ " unique leaf types but the model only includes "
83+
+ migrationModel.getNTypes() + " unique types!");
7884
}
7985

8086
@Override

src/multitypetree/util/TreeRootTypeLogger.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ public void initAndValidate() {
5151

5252
@Override
5353
public void init(PrintStream out) {
54+
5455
if (getID() == null || getID().matches("\\s*")) {
5556
out.print(mtTree.getID() + ".rootColor\t");
5657
} else {

src/multitypetree/util/TypeLengths.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,12 @@ public void init(PrintStream out) {
115115

116116
String idString = mtTree.getID();
117117

118+
if (!mtTree.hasTypeTrait() || mtTree.getTypeList().size() != nTypes) {
119+
throw new IllegalArgumentException(
120+
"TypeLengths logger needs MultiTypeTree to have named types,\n"
121+
+ "and as many named types as there are demes in the model.");
122+
}
123+
118124
for (int type = 0; type < nTypes; type++)
119125
out.print(idString + ".length_" + mtTree.getTypeString(type) + "\t");
120126
}

0 commit comments

Comments
 (0)