Merge branch 'develop' into features/r2_11_2_alphafold/JAL-629
[jalview.git] / src / jalview / datamodel / ContactMatrix.java
1 package jalview.datamodel;
2
3 import java.awt.Color;
4 import java.util.ArrayList;
5 import java.util.BitSet;
6 import java.util.HashMap;
7 import java.util.List;
8 import java.util.StringTokenizer;
9
10 import jalview.bin.Console;
11
12 public abstract class ContactMatrix implements ContactMatrixI
13 {
14   /**
15    * are contacts reflexive ?
16    */
17   boolean symmetric = true;
18
19   public ContactMatrix(boolean symmetric)
20   {
21     this.symmetric = symmetric;
22   }
23
24   List<List<Float>> contacts = null;
25
26   int width = 0, numcontacts = 0;
27
28   float min = 0f, max = 0f;
29
30   public void addContact(int left, int right, float strength)
31   {
32     if (left < 0 || right < 0)
33     {
34       throw new Error(new RuntimeException(
35               "Cannot have negative indices for contact left=" + left
36                       + " right=" + right + " strength=" + strength));
37     }
38     if (symmetric)
39     {
40       if (left > right)
41       {
42         // swap
43         int r = right;
44         right = left;
45         left = r;
46       }
47     }
48     if (contacts == null)
49     {
50       // TODO: use sparse list for efficiency ?
51       contacts = new ArrayList<List<Float>>();
52     }
53     List<Float> clist = contacts.get(left);
54     if (clist == null)
55     {
56       clist = new ArrayList<Float>();
57       contacts.set(left, clist);
58     }
59     Float last = clist.set(right, strength);
60     // TODO: if last is non null, may need to recompute range
61     checkBounds(strength);
62     if (last == null)
63     {
64       numcontacts++;
65     }
66   }
67
68   private void checkBounds(float strength)
69   {
70     if (min > strength)
71     {
72       min = strength;
73     }
74     if (max < strength)
75     {
76       max = strength;
77     }
78   }
79
80   @Override
81   public ContactListI getContactList(final int column)
82   {
83     if (column < 0 || column >= width)
84     {
85       return null;
86     }
87
88     return new ContactListImpl(new ContactListProviderI()
89     {
90       int p = column;
91
92       @Override
93       public int getPosition()
94       {
95         return p;
96       }
97
98       @Override
99       public int getContactHeight()
100       {
101         return width;
102
103       }
104
105       @Override
106       public double getContactAt(int column)
107       {
108         List<Float> clist;
109         Float cl = null;
110         if (symmetric)
111         {
112           if (p < column)
113           {
114             clist = contacts.get(p);
115             cl = clist.get(column);
116           }
117           else
118           {
119             clist = contacts.get(column);
120             cl = clist.get(p);
121           }
122         }
123         else
124         {
125           clist = contacts.get(p);
126           cl = clist.get(column);
127         }
128         if (cl == null)
129         {
130           // return 0 not NaN ?
131           return Double.NaN;
132         }
133         return cl.doubleValue();
134       }
135     });
136   }
137
138   @Override
139   public float getMin()
140   {
141     return min;
142   }
143
144   @Override
145   public float getMax()
146   {
147     return max;
148   }
149
150   @Override
151   public boolean hasReferenceSeq()
152   {
153     // TODO Auto-generated method stub
154     return false;
155   }
156
157   @Override
158   public SequenceI getReferenceSeq()
159   {
160     // TODO Auto-generated method stub
161     return null;
162   }
163
164   @Override
165   public String getAnnotLabel()
166   {
167     return "Contact Matrix";
168   }
169
170   @Override
171   public String getAnnotDescr()
172   {
173     return "Contact Matrix";
174   }
175
176   List<BitSet> groups = null;
177
178   @Override
179   public void updateGroups(List<BitSet> colGroups)
180   {
181     groups = colGroups;
182     colorMap = new HashMap<>();
183   }
184
185   @Override
186   public boolean hasGroups()
187   {
188     return groups != null && groups.size() > 0;
189   }
190
191   @Override
192   public List<BitSet> getGroups()
193   {
194     return groups;
195   }
196
197   @Override
198   public BitSet getGroupsFor(int column)
199   {
200     for (BitSet gp : groups)
201     {
202       if (gp.get(column))
203       {
204         return gp;
205       }
206     }
207     return ContactMatrixI.super.getGroupsFor(column);
208   }
209
210   HashMap<BitSet, Color> colorMap = new HashMap<>();
211
212   @Override
213   public Color getColourForGroup(BitSet bs)
214   {
215     if (bs == null)
216     {
217       return Color.white;
218     }
219     Color groupCol = colorMap.get(bs);
220     if (groupCol == null)
221     {
222       return Color.white;
223     }
224     return groupCol;
225   }
226
227   @Override
228   public void setColorForGroup(BitSet bs, Color color)
229   {
230     colorMap.put(bs, color);
231   }
232
233   public static String contactToFloatString(ContactMatrixI cm)
234   {
235     StringBuilder sb = new StringBuilder();
236     for (int c = 0; c < cm.getWidth(); c++)
237     {
238       ContactListI cl = cm.getContactList(c);
239       if (cl != null)
240       {
241         for (int h = 0; h <= cl.getContactHeight(); h++)
242         {
243           if (sb.length() > 0)
244           {
245             sb.append('\t');
246           }
247           sb.append(cl.getContactAt(h));
248         }
249       }
250     }
251     return sb.toString();
252   }
253
254   public static float[][] fromFloatStringToContacts(String values, int cols,
255           int rows)
256   {
257     float[][] vals = new float[cols][rows];
258     StringTokenizer tabsep = new StringTokenizer(values, "" + '\t');
259     int c = 0, r = 0;
260
261     while (tabsep.hasMoreTokens())
262     {
263       double elem = Double.valueOf(tabsep.nextToken());
264       vals[c][r++] = (float) elem;
265       if (r >= vals[c].length)
266       {
267         r = 0;
268         c++;
269       }
270       if (c >= vals.length)
271       {
272
273         break;
274       }
275     }
276     if (tabsep.hasMoreElements())
277     {
278       Console.warn(
279               "Ignoring additional elements for Float string to contact matrix parsing.");
280     }
281
282     return vals;
283   }
284 }