package jalview.ws.datamodel.alphafold; import java.util.Iterator; import java.util.List; import java.util.Map; import jalview.datamodel.ContactListI; import jalview.datamodel.ContactListImpl; import jalview.datamodel.ContactListProviderI; import jalview.datamodel.ContactMatrixI; import jalview.datamodel.SequenceI; import jalview.util.MapUtils; public class PAEContactMatrix implements ContactMatrixI { SequenceI refSeq = null; /** * the length that refSeq is expected to be (excluding gaps, of course) */ int length; int maxrow = 0, maxcol = 0; int[] indices1, indices2; float[][] elements; float maxscore; private void setRefSeq(SequenceI _refSeq) { refSeq = _refSeq; while (refSeq.getDatasetSequence() != null) { refSeq = refSeq.getDatasetSequence(); } length = _refSeq.getEnd() - _refSeq.getStart() + 1; } @SuppressWarnings("unchecked") public PAEContactMatrix(SequenceI _refSeq, Map pae_obj) { setRefSeq(_refSeq); // convert the lists to primitive arrays and store if (!MapUtils.containsAKey(pae_obj, "predicted_aligned_error", "pae")) { parse_version_1_pAE(pae_obj); return; } else { parse_version_2_pAE(pae_obj); } } /** * construct a sequence associated PAE matrix directly from a float array * * @param _refSeq * @param matrix */ public PAEContactMatrix(SequenceI _refSeq, float[][] matrix) { setRefSeq(_refSeq); maxcol = 0; for (float[] row : matrix) { if (row.length > maxcol) { maxcol = row.length; } maxscore = row[0]; for (float f : row) { if (maxscore < f) { maxscore = f; } } } maxrow = matrix.length; elements = matrix; } /** * parse a sane JSON representation of the pAE * * @param pae_obj */ @SuppressWarnings("unchecked") private void parse_version_2_pAE(Map pae_obj) { // this is never going to be reached by the integer rounding.. or is it ? maxscore = ((Double) MapUtils.getFirst(pae_obj, "max_predicted_aligned_error", "max_pae")).floatValue(); List> scoreRows = ((List>) MapUtils .getFirst(pae_obj, "predicted_aligned_error", "pae")); elements = new float[scoreRows.size()][scoreRows.size()]; int row = 0, col = 0; for (List scoreRow : scoreRows) { Iterator scores = scoreRow.iterator(); while (scores.hasNext()) { Object d = scores.next(); if (d instanceof Double) elements[row][col++] = ((Double) d).longValue(); else elements[row][col++] = (float) ((Long) d).longValue(); } row++; col = 0; } maxcol = length; maxrow = length; } /** * v1 format got ditched 28th July 2022 see * https://alphafold.ebi.ac.uk/faq#:~:text=We%20updated%20the%20PAE%20JSON%20file%20format%20on%2028th%20July%202022 * * @param pae_obj */ @SuppressWarnings("unchecked") private void parse_version_1_pAE(Map pae_obj) { // assume indices are with respect to range defined by _refSeq on the // dataset refSeq Iterator rows = ((List) pae_obj.get("residue1")).iterator(); Iterator cols = ((List) pae_obj.get("residue2")).iterator(); Iterator scores = ((List) pae_obj.get("distance")) .iterator(); // assume square matrix elements = new float[length][length]; while (scores.hasNext()) { float escore = scores.next().floatValue(); int row = rows.next().intValue(); int col = cols.next().intValue(); if (maxrow < row) { maxrow = row; } if (maxcol < col) { maxcol = col; } elements[row - 1][col - 1] = escore; } maxscore = ((Double) MapUtils.getFirst(pae_obj, "max_predicted_aligned_error", "max_pae")).floatValue(); } @Override public ContactListI getContactList(final int _column) { if (_column < 0 || _column >= elements.length) { return null; } return new ContactListImpl(new ContactListProviderI() { @Override public int getPosition() { return _column; } @Override public int getContactHeight() { return maxcol - 1; } @Override public double getContactAt(int column) { if (column < 0 || column >= elements[_column].length) { return -1; } return elements[_column][column]; } }); } @Override public float getMin() { return 0; } @Override public float getMax() { return maxscore; } @Override public boolean hasReferenceSeq() { return (refSeq != null); } @Override public SequenceI getReferenceSeq() { return refSeq; } @Override public String getAnnotDescr() { return "Predicted Alignment Error for " + refSeq.getName(); } @Override public String getAnnotLabel() { StringBuilder label = new StringBuilder("pAE Matrix"); // if (this.getReferenceSeq() != null) // label.append(":").append(this.getReferenceSeq().getDisplayId(false)); return label.toString(); } public static final String PAEMATRIX = "PAE_MATRIX"; @Override public String getType() { return PAEMATRIX; } @Override public int getWidth() { return length; } @Override public int getHeight() { return length; } }