16ca70d13b7ae7c9ee0b1079948854fc08e79a68
[jalview.git] / test / jalview / analysis / scoremodels / FeatureDistanceModelTest.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.analysis.scoremodels;
22
23 import static org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertTrue;
25
26 import jalview.api.analysis.ScoreModelI;
27 import jalview.api.analysis.SimilarityParamsI;
28 import jalview.datamodel.Alignment;
29 import jalview.datamodel.AlignmentI;
30 import jalview.datamodel.AlignmentView;
31 import jalview.datamodel.Sequence;
32 import jalview.datamodel.SequenceFeature;
33 import jalview.datamodel.SequenceI;
34 import jalview.gui.AlignFrame;
35 import jalview.gui.AlignViewport;
36 import jalview.gui.JvOptionPane;
37 import jalview.io.DataSourceType;
38 import jalview.io.FileLoader;
39 import jalview.math.MatrixI;
40
41 import java.util.Arrays;
42
43 import org.testng.Assert;
44 import org.testng.annotations.BeforeClass;
45 import org.testng.annotations.Test;
46
47 public class FeatureDistanceModelTest
48 {
49
50   @BeforeClass(alwaysRun = true)
51   public void setUpJvOptionPane()
52   {
53     JvOptionPane.setInteractiveMode(false);
54     JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
55   }
56
57   public static String alntestFile = "FER1_MESCR/72-76 DVYIL\nFER1_SPIOL/71-75 DVYIL\nFER3_RAPSA/21-25 DVYVL\nFER1_MAIZE/73-77 DVYIL\n";
58
59   int[] sf1 = new int[] { 74, 74, 73, 73, 23, 23, -1, -1 };
60
61   int[] sf2 = new int[] { -1, -1, 74, 75, -1, -1, 76, 77 };
62
63   int[] sf3 = new int[] { -1, -1, -1, -1, -1, -1, 76, 77 };
64
65   /**
66    * <pre>
67    * Load test alignment and add features to sequences: 
68    *      FER1_MESCR FER1_SPIOL FER3_RAPSA FER1_MAIZE 
69    *  sf1     X          X          X  
70    *  sf2                X                     X 
71    *  sf3                                      X
72    * </pre>
73    * 
74    * @return
75    */
76   public AlignFrame getTestAlignmentFrame()
77   {
78     AlignFrame alf = new FileLoader(false).LoadFileWaitTillLoaded(
79             alntestFile, DataSourceType.PASTE);
80     AlignmentI al = alf.getViewport().getAlignment();
81     Assert.assertEquals(al.getHeight(), 4);
82     Assert.assertEquals(al.getWidth(), 5);
83     for (int i = 0; i < 4; i++)
84     {
85       SequenceI ds = al.getSequenceAt(i).getDatasetSequence();
86       if (sf1[i * 2] > 0)
87       {
88         ds.addSequenceFeature(new SequenceFeature("sf1", "sf1", sf1[i * 2],
89                 sf1[i * 2 + 1], "sf1"));
90       }
91       if (sf2[i * 2] > 0)
92       {
93         ds.addSequenceFeature(new SequenceFeature("sf2", "sf2", sf2[i * 2],
94                 sf2[i * 2 + 1], "sf2"));
95       }
96       if (sf3[i * 2] > 0)
97       {
98         ds.addSequenceFeature(new SequenceFeature("sf3", "sf3", sf3[i * 2],
99                 sf3[i * 2 + 1], "sf3"));
100       }
101     }
102     alf.setShowSeqFeatures(true);
103     alf.getFeatureRenderer().setVisible("sf1");
104     alf.getFeatureRenderer().setVisible("sf2");
105     alf.getFeatureRenderer().setVisible("sf3");
106     alf.getFeatureRenderer().findAllFeatures(true);
107     Assert.assertEquals(alf.getFeatureRenderer().getDisplayedFeatureTypes()
108             .size(), 3, "Number of feature types");
109     assertTrue(alf.getCurrentView().areFeaturesDisplayed());
110     return alf;
111   }
112
113   @Test(groups = { "Functional" })
114   public void testFeatureScoreModel() throws Exception
115   {
116     AlignFrame alf = getTestAlignmentFrame();
117     ScoreModelI sm = new FeatureDistanceModel();
118     sm = ScoreModels.getInstance().getScoreModel(sm.getName(),
119             alf.getCurrentView().getAlignPanel());
120     alf.selectAllSequenceMenuItem_actionPerformed(null);
121
122     MatrixI dm = sm.findDistances(
123             alf.getViewport().getAlignmentView(true),
124             SimilarityParams.Jalview);
125     assertEquals(dm.getValue(0, 2), 0d,
126             "FER1_MESCR (0) should be identical with RAPSA (2)");
127     assertTrue(dm.getValue(0, 1) > dm.getValue(0, 2),
128             "FER1_MESCR (0) should be further from SPIOL (1) than it is from RAPSA (2)");
129   }
130
131   @Test(groups = { "Functional" })
132   public void testFeatureScoreModel_hiddenFirstColumn() throws Exception
133   {
134     AlignFrame alf = getTestAlignmentFrame();
135     // hiding first two columns shouldn't affect the tree
136     alf.getViewport().hideColumns(0, 1);
137     ScoreModelI sm = new FeatureDistanceModel();
138     sm = ScoreModels.getInstance().getScoreModel(sm.getName(),
139             alf.getCurrentView().getAlignPanel());
140     alf.selectAllSequenceMenuItem_actionPerformed(null);
141     MatrixI dm = sm.findDistances(
142             alf.getViewport().getAlignmentView(true),
143             SimilarityParams.Jalview);
144     assertEquals(dm.getValue(0, 2), 0d,
145             "FER1_MESCR (0) should be identical with RAPSA (2)");
146     assertTrue(dm.getValue(0, 1) > dm.getValue(0, 2),
147             "FER1_MESCR (0) should be further from SPIOL (1) than it is from RAPSA (2)");
148   }
149
150   @Test(groups = { "Functional" })
151   public void testFeatureScoreModel_HiddenColumns() throws Exception
152   {
153     AlignFrame alf = getTestAlignmentFrame();
154     // hide columns and check tree changes
155     alf.getViewport().hideColumns(3, 4);
156     alf.getViewport().hideColumns(0, 1);
157     // getName() can become static in Java 8
158     ScoreModelI sm = new FeatureDistanceModel();
159     sm = ScoreModels.getInstance().getScoreModel(sm.getName(),
160             alf.getCurrentView().getAlignPanel());
161     alf.selectAllSequenceMenuItem_actionPerformed(null);
162     MatrixI dm = sm.findDistances(
163             alf.getViewport().getAlignmentView(true),
164             SimilarityParams.Jalview);
165     assertEquals(
166             dm.getValue(0, 2),
167             0d,
168             "After hiding last two columns FER1_MESCR (0) should still be identical with RAPSA (2)");
169     assertEquals(
170             dm.getValue(0, 1),
171             0d,
172             "After hiding last two columns FER1_MESCR (0) should now also be identical with SPIOL (1)");
173     for (int s = 0; s < 3; s++)
174     {
175       assertTrue(dm.getValue(s, 3) > 0d, "After hiding last two columns "
176               + alf.getViewport().getAlignment().getSequenceAt(s).getName()
177               + "(" + s + ") should still be distinct from FER1_MAIZE (3)");
178     }
179   }
180
181   /**
182    * Check findFeatureAt doesn't return contact features except at contact
183    * points TODO:move to under the FeatureRendererModel test suite
184    */
185   @Test(groups = { "Functional" })
186   public void testFindFeatureAt_PointFeature() throws Exception
187   {
188     String alignment = "a CCCCCCGGGGGGCCCCCC\n" + "b CCCCCCGGGGGGCCCCCC\n"
189             + "c CCCCCCGGGGGGCCCCCC\n";
190     AlignFrame af = new jalview.io.FileLoader(false)
191             .LoadFileWaitTillLoaded(alignment, DataSourceType.PASTE);
192     SequenceI aseq = af.getViewport().getAlignment().getSequenceAt(0);
193     SequenceFeature sf = null;
194     sf = new SequenceFeature("disulphide bond", "", 2, 5, Float.NaN, "");
195     aseq.addSequenceFeature(sf);
196     assertTrue(sf.isContactFeature());
197     af.refreshFeatureUI(true);
198     af.getFeatureRenderer().setAllVisible(Arrays.asList("disulphide bond"));
199     Assert.assertEquals(af.getFeatureRenderer().getDisplayedFeatureTypes()
200             .size(), 1, "Should be just one feature type displayed");
201     // step through and check for pointwise feature presence/absence
202     Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtColumn(aseq, 1)
203             .size(), 0);
204     // step through and check for pointwise feature presence/absence
205     Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtColumn(aseq, 2)
206             .size(), 1);
207     // step through and check for pointwise feature presence/absence
208     Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtColumn(aseq, 3)
209             .size(), 0);
210     // step through and check for pointwise feature presence/absence
211     Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtColumn(aseq, 4)
212             .size(), 0);
213     // step through and check for pointwise feature presence/absence
214     Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtColumn(aseq, 5)
215             .size(), 1);
216     // step through and check for pointwise feature presence/absence
217     Assert.assertEquals(af.getFeatureRenderer().findFeaturesAtColumn(aseq, 6)
218             .size(), 0);
219   }
220
221   @Test(groups = { "Functional" })
222   public void testFindDistances() throws Exception
223   {
224     String seqs = ">s1\nABCDE\n>seq2\nABCDE\n";
225     AlignFrame alf = new FileLoader().LoadFileWaitTillLoaded(seqs,
226             DataSourceType.PASTE);
227     SequenceI s1 = alf.getViewport().getAlignment().getSequenceAt(0);
228     SequenceI s2 = alf.getViewport().getAlignment().getSequenceAt(1);
229
230     /*
231      * set domain and variant features thus:
232      *     ----5
233      *  s1 ddd..
234      *  s1 .vvv.
235      *  s1 ..vvv    
236      *  s2 .ddd. 
237      *  s2 vv..v
238      *  The number of unshared feature types per column is
239      *     20120 (two features of the same type doesn't affect score)
240      *  giving an average (pairwise distance) of 5/5 or 1.0 
241      */
242     s1.addSequenceFeature(new SequenceFeature("domain", null, 1, 3, 0f,
243             null));
244     s1.addSequenceFeature(new SequenceFeature("variant", null, 2, 4, 0f,
245             null));
246     s1.addSequenceFeature(new SequenceFeature("variant", null, 3, 5, 0f,
247             null));
248     s2.addSequenceFeature(new SequenceFeature("domain", null, 2, 4, 0f,
249             null));
250     s2.addSequenceFeature(new SequenceFeature("variant", null, 1, 2, 0f,
251             null));
252     s2.addSequenceFeature(new SequenceFeature("variant", null, 5, 5, 0f,
253             null));
254     alf.setShowSeqFeatures(true);
255     alf.getFeatureRenderer().findAllFeatures(true);
256
257     ScoreModelI sm = new FeatureDistanceModel();
258     sm = ScoreModels.getInstance().getScoreModel(sm.getName(),
259             alf.getCurrentView().getAlignPanel());
260     alf.selectAllSequenceMenuItem_actionPerformed(null);
261
262     AlignmentView alignmentView = alf.getViewport()
263             .getAlignmentView(true);
264     MatrixI distances = sm.findDistances(alignmentView,
265             SimilarityParams.Jalview);
266     assertEquals(distances.width(), 2);
267     assertEquals(distances.height(), 2);
268     assertEquals(distances.getValue(0, 0), 0d);
269     assertEquals(distances.getValue(1, 1), 0d);
270
271     assertEquals(distances.getValue(0, 1), 1d,
272             "expected identical pairs. (check normalisation for similarity score)");
273     assertEquals(distances.getValue(1, 0), 1d);
274   }
275
276   /**
277    * Verify computed distances with varying parameter options
278    */
279   @Test(groups = "Functional")
280   public void testFindDistances_withParams()
281   {
282     AlignFrame af = setupAlignmentView();
283     AlignViewport viewport = af.getViewport();
284     AlignmentView view = viewport.getAlignmentView(false);
285
286     ScoreModelI sm = new FeatureDistanceModel();
287     sm = ScoreModels.getInstance().getScoreModel(sm.getName(),
288             af.alignPanel);
289
290     /*
291      * feature distance model always normalises by region width
292      * gap-gap is always included (but scores zero)
293      * the only variable parameter is 'includeGaps'
294      */
295
296     /*
297      * include gaps
298      * score = 3 + 3 + 0 + 2 + 3 + 2 = 13/6
299      */
300     SimilarityParamsI params = new SimilarityParams(true, true, true, true);
301     MatrixI distances = sm.findDistances(view, params);
302     assertEquals(distances.getValue(0, 0), 0d);
303     assertEquals(distances.getValue(1, 1), 0d);
304     assertEquals(distances.getValue(0, 1), 13d / 6); // should be 13d/6
305     assertEquals(distances.getValue(1, 0), 13d / 6);
306   
307     /*
308      * exclude gaps
309      * score = 3 + 3 + 0 + 0 + 0 + 0 = 6/6
310      */
311     params = new SimilarityParams(true, true, false, true);
312     distances = sm.findDistances(view, params);
313     assertEquals(distances.getValue(0, 1), 6d / 6);// should be 6d/6
314   }
315
316   /**
317    * <pre>
318    * Set up
319    *   column      1 2 3 4 5 6
320    *        seq s1 F R - K - S
321    *        seq s2 F S - - L
322    *   s1 chain    c c   c   c
323    *   s1 domain   d d   d   d
324    *   s2 chain    c c     c
325    *   s2 metal    m m     m
326    *   s2 Pfam     P P     P
327    *      scores:  3 3 0 2 3 2
328    * </pre>
329    * 
330    * @return
331    */
332   protected AlignFrame setupAlignmentView()
333   {
334     /*
335      * for now, using space for gap to match callers of
336      * AlignmentView.getSequenceStrings()
337      * may change this to '-' (with corresponding change to matrices)
338      */
339     SequenceI s1 = new Sequence("s1", "FR K S");
340     SequenceI s2 = new Sequence("s2", "FS  L");
341
342     s1.addSequenceFeature(new SequenceFeature("chain", null, 1, 4, 0f, null));
343     s1.addSequenceFeature(new SequenceFeature("domain", null, 1, 4, 0f,
344             null));
345     s2.addSequenceFeature(new SequenceFeature("chain", null, 1, 3, 0f, null));
346     s2.addSequenceFeature(new SequenceFeature("metal", null, 1, 3, 0f, null));
347     s2.addSequenceFeature(new SequenceFeature("Pfam", null, 1, 3, 0f, null));
348     AlignmentI al = new Alignment(new SequenceI[] { s1, s2 });
349     AlignFrame af = new AlignFrame(al, 300, 300);
350     af.setShowSeqFeatures(true);
351     af.getFeatureRenderer().findAllFeatures(true);
352     return af;
353   }
354
355 }