JAL-3858 persist PAE matrix (only so far) as serialised floats in element on dataset...
[jalview.git] / src / jalview / ws / datamodel / alphafold / PAEContactMatrix.java
1 package jalview.ws.datamodel.alphafold;
2
3 import java.util.Iterator;
4 import java.util.List;
5 import java.util.Map;
6
7 import jalview.datamodel.ContactListI;
8 import jalview.datamodel.ContactListImpl;
9 import jalview.datamodel.ContactListProviderI;
10 import jalview.datamodel.ContactMatrixI;
11 import jalview.datamodel.SequenceI;
12 import jalview.util.MapUtils;
13
14 public class PAEContactMatrix implements ContactMatrixI
15 {
16
17   SequenceI refSeq = null;
18
19   /**
20    * the length that refSeq is expected to be (excluding gaps, of course)
21    */
22   int length;
23
24   int maxrow = 0, maxcol = 0;
25
26   int[] indices1, indices2;
27
28   float[][] elements;
29
30   float maxscore;
31
32   private void setRefSeq(SequenceI _refSeq)
33   {
34     refSeq = _refSeq;
35     while (refSeq.getDatasetSequence() != null)
36     {
37       refSeq = refSeq.getDatasetSequence();
38     }
39     length = _refSeq.getEnd() - _refSeq.getStart() + 1;
40   }
41   @SuppressWarnings("unchecked")
42   public PAEContactMatrix(SequenceI _refSeq, Map<String, Object> pae_obj)
43   {
44     setRefSeq(_refSeq);
45     // convert the lists to primitive arrays and store
46     
47     if (!MapUtils.containsAKey(pae_obj, "predicted_aligned_error", "pae"))
48     {
49       parse_version_1_pAE(pae_obj);
50       return;
51     }
52     else
53     {
54       parse_version_2_pAE(pae_obj);
55     }
56   }
57   /**
58    * construct a sequence associated PAE matrix directly from a float array
59    * @param _refSeq
60    * @param matrix
61    */
62   public PAEContactMatrix(SequenceI _refSeq, float[][] matrix)
63   {
64     setRefSeq(_refSeq);
65     maxcol=0;
66     for (float[] row:matrix)
67     {
68       if (row.length>maxcol)
69       {
70         maxcol=row.length;
71       }
72       maxscore=row[0];
73       for (float f:row)
74       {
75         if (maxscore<f) {
76           maxscore=f;
77         }
78       }
79     }
80     maxrow=matrix.length;
81     elements = matrix;
82     
83   }
84
85   /**
86    * parse a sane JSON representation of the pAE
87    * 
88    * @param pae_obj
89    */
90   @SuppressWarnings("unchecked")
91   private void parse_version_2_pAE(Map<String, Object> pae_obj)
92   {
93     elements = new float[length][length];
94     // this is never going to be reached by the integer rounding.. or is it ?
95     maxscore = ((Double) MapUtils.getFirst(pae_obj,
96             "max_predicted_aligned_error", "max_pae")).floatValue();
97     Iterator<List<Long>> scoreRows = ((List<List<Long>>) MapUtils
98             .getFirst(pae_obj, "predicted_aligned_error", "pae"))
99             .iterator();
100     int row = 0, col = 0;
101     while (scoreRows.hasNext())
102     {
103       Iterator<Long> scores = scoreRows.next().iterator();
104       while (scores.hasNext())
105       {
106         elements[row][col++] = scores.next();
107       }
108       row++;
109       col = 0;
110     }
111     maxcol = length;
112     maxrow = length;
113   }
114
115   /**
116    * v1 format got ditched 28th July 2022 see
117    * https://alphafold.ebi.ac.uk/faq#:~:text=We%20updated%20the%20PAE%20JSON%20file%20format%20on%2028th%20July%202022
118    * 
119    * @param pae_obj
120    */
121   @SuppressWarnings("unchecked")
122   private void parse_version_1_pAE(Map<String, Object> pae_obj)
123   {
124     // assume indices are with respect to range defined by _refSeq on the
125     // dataset refSeq
126     Iterator<Long> rows = ((List<Long>) pae_obj.get("residue1")).iterator();
127     Iterator<Long> cols = ((List<Long>) pae_obj.get("residue2")).iterator();
128     Iterator<Double> scores = ((List<Double>) pae_obj.get("distance"))
129             .iterator();
130
131     elements = new float[length][length];
132     while (scores.hasNext())
133     {
134       float escore = scores.next().floatValue();
135       int row = rows.next().intValue();
136       int col = cols.next().intValue();
137       if (maxrow < row)
138       {
139         maxrow = row;
140       }
141       if (maxcol < col)
142       {
143         maxcol = col;
144       }
145       elements[row - 1][col - 1] = escore;
146     }
147
148     maxscore = ((Double) MapUtils.getFirst(pae_obj,
149             "max_predicted_aligned_error", "max_pae")).floatValue();
150   }
151
152   @Override
153   public ContactListI getContactList(final int _column)
154   {
155     if (_column < 0 || _column >= elements.length)
156     {
157       return null;
158     }
159
160     return new ContactListImpl(new ContactListProviderI()
161     {
162       @Override
163       public int getPosition()
164       {
165         return _column;
166       }
167
168       @Override
169       public int getContactHeight()
170       {
171         return maxcol-1;
172       }
173
174       @Override
175       public double getContactAt(int column)
176       {
177         if (column < 0 || column >= elements[_column].length)
178         {
179           return -1;
180         }
181         return elements[_column][column];
182       }
183     });
184   }
185
186   @Override
187   public float getMin()
188   {
189     return 0;
190   }
191
192   @Override
193   public float getMax()
194   {
195     return maxscore;
196   }
197
198   @Override
199   public boolean hasReferenceSeq()
200   {
201     return (refSeq != null);
202   }
203
204   @Override
205   public SequenceI getReferenceSeq()
206   {
207     return refSeq;
208   }
209
210   @Override
211   public String getAnnotDescr()
212   {
213     return "Predicted Alignment Error for " + refSeq.getName();
214   }
215
216   @Override
217   public String getAnnotLabel()
218   {
219     return "pAE Matrix";
220   }
221
222   public static final String PAEMATRIX="PAE_MATRIX";
223   @Override
224   public String getType()
225   {
226     return PAEMATRIX;
227   }
228   @Override
229   public int getWidth()
230   {
231     return length;
232   }
233   @Override
234   public int getHeight()
235   {
236     return length;
237   }
238 }