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