From 9e40070f757fc662aba39ba6314be4937276ead4 Mon Sep 17 00:00:00 2001 From: James Procter Date: Fri, 12 May 2023 16:28:31 +0100 Subject: [PATCH] JAL-2349 allow PAE or other contact matrices to hold a coordinate mapping allowing them to be transferred from one sequence to another - basic working but region selects are broken --- src/jalview/analysis/AverageDistanceEngine.java | 4 + src/jalview/api/AlignViewportI.java | 5 + src/jalview/datamodel/Alignment.java | 15 +- src/jalview/datamodel/ContactListI.java | 7 + src/jalview/datamodel/ContactListImpl.java | 3 +- src/jalview/datamodel/ContactListProviderI.java | 13 ++ src/jalview/datamodel/ContactMapHolder.java | 13 +- src/jalview/datamodel/ContactMatrix.java | 14 -- src/jalview/datamodel/ContactMatrixI.java | 7 +- .../datamodel/SeqDistanceContactMatrix.java | 33 ++--- src/jalview/renderer/ContactGeometry.java | 59 +++++++- src/jalview/renderer/ContactMapRenderer.java | 34 ++--- .../ws/datamodel/MappableContactMatrixI.java | 31 ++++ .../ws/datamodel/alphafold/PAEContactMatrix.java | 96 ++++++++----- src/mc_view/PDBChain.java | 5 +- test/jalview/datamodel/PAEContactMatrixTest.java | 151 ++++++++++++++++++++ 16 files changed, 379 insertions(+), 111 deletions(-) create mode 100644 src/jalview/ws/datamodel/MappableContactMatrixI.java create mode 100644 test/jalview/datamodel/PAEContactMatrixTest.java diff --git a/src/jalview/analysis/AverageDistanceEngine.java b/src/jalview/analysis/AverageDistanceEngine.java index e6a763b..c52c7be 100644 --- a/src/jalview/analysis/AverageDistanceEngine.java +++ b/src/jalview/analysis/AverageDistanceEngine.java @@ -105,6 +105,10 @@ public class AverageDistanceEngine extends TreeEngine { distances.setValue(i, i, 0); ContactListI jth = cm.getContactList(j); + if (jth==null) + { + break; + } double prd = 0; for (int indx = 0; indx < cm.getHeight(); indx++) { diff --git a/src/jalview/api/AlignViewportI.java b/src/jalview/api/AlignViewportI.java index 03efec5..e3478ea 100644 --- a/src/jalview/api/AlignViewportI.java +++ b/src/jalview/api/AlignViewportI.java @@ -477,6 +477,11 @@ public interface AlignViewportI extends ViewStyleI */ SearchResultsI getSearchResults(); + /** + * Retrieve a ContactListI corresponding to column in an annotation row in an alignment. + * @param _aa - annotation with associated matrix data + * @param column - column in alignment where _aa is associated + */ ContactListI getContactList(AlignmentAnnotation _aa, int column); /** diff --git a/src/jalview/datamodel/Alignment.java b/src/jalview/datamodel/Alignment.java index 517a6dd..1ec702c 100755 --- a/src/jalview/datamodel/Alignment.java +++ b/src/jalview/datamodel/Alignment.java @@ -2076,14 +2076,19 @@ public class Alignment implements AlignmentI, AutoCloseable } if (cl == null && _aa.sequenceRef != null) { - int spos = _aa.sequenceRef.findPosition(column); - if (spos >= _aa.sequenceRef.getStart() - && spos <= 1 + _aa.sequenceRef.getEnd()) + if (_aa.annotations[column] != null) { - cl = _aa.sequenceRef.getContactListFor(_aa, spos-_aa.sequenceRef.getStart()); + // sequence associated + cl = _aa.sequenceRef.getContactListFor(_aa, column); if (cl == null && _aa.sequenceRef.getDatasetSequence() != null) { - _aa.sequenceRef.getDatasetSequence().getContactListFor(_aa, spos-_aa.sequenceRef.getStart()); + int spos = _aa.sequenceRef.findPosition(column); + if (spos >= _aa.sequenceRef.getStart() + && spos <= 1 + _aa.sequenceRef.getEnd()) + { + cl = _aa.sequenceRef.getDatasetSequence().getContactListFor(_aa, + spos-_aa.sequenceRef.getStart()); + } } } } diff --git a/src/jalview/datamodel/ContactListI.java b/src/jalview/datamodel/ContactListI.java index ac06adb..d0544b8 100644 --- a/src/jalview/datamodel/ContactListI.java +++ b/src/jalview/datamodel/ContactListI.java @@ -1,5 +1,9 @@ package jalview.datamodel; +import java.awt.Color; + +import jalview.renderer.ContactGeometry.contactInterval; + public interface ContactListI extends ContactListProviderI { @@ -12,4 +16,7 @@ public interface ContactListI extends ContactListProviderI */ ContactRange getRangeFor(int from_column, int to_column); + default Color getColourForGroup() { + return null; + } } diff --git a/src/jalview/datamodel/ContactListImpl.java b/src/jalview/datamodel/ContactListImpl.java index 8e806e4..6a37864 100644 --- a/src/jalview/datamodel/ContactListImpl.java +++ b/src/jalview/datamodel/ContactListImpl.java @@ -1,5 +1,7 @@ package jalview.datamodel; +import jalview.renderer.ContactGeometry.contactInterval; + /** * helper class to compute min/max/mean for a range on a contact list * @@ -94,5 +96,4 @@ public class ContactListImpl implements ContactListI } return cr; } - } diff --git a/src/jalview/datamodel/ContactListProviderI.java b/src/jalview/datamodel/ContactListProviderI.java index f027e01..a4e0b79 100644 --- a/src/jalview/datamodel/ContactListProviderI.java +++ b/src/jalview/datamodel/ContactListProviderI.java @@ -1,5 +1,7 @@ package jalview.datamodel; +import jalview.renderer.ContactGeometry.contactInterval; + public interface ContactListProviderI { @@ -26,4 +28,15 @@ public interface ContactListProviderI */ double getContactAt(int column); + + /** + * Return positions in local reference corresponding to cStart and cEnd in matrix data + * @param cStart + * @param cEnd + * @return int[] { start, end (inclusive) for each contiguous segment} + */ + default int[] getMappedPositionsFor(int cStart, int cEnd) { + return new int[] { cStart, cEnd}; + } + } diff --git a/src/jalview/datamodel/ContactMapHolder.java b/src/jalview/datamodel/ContactMapHolder.java index 296feaf..d80a719 100644 --- a/src/jalview/datamodel/ContactMapHolder.java +++ b/src/jalview/datamodel/ContactMapHolder.java @@ -5,6 +5,8 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; +import jalview.ws.datamodel.MappableContactMatrixI; + public class ContactMapHolder implements ContactMapHolderI { @@ -28,6 +30,13 @@ public class ContactMapHolder implements ContactMapHolderI { return null; } + if (cm instanceof MappableContactMatrixI) + { + if (_aa.sequenceRef!=null) + { + return ((MappableContactMatrixI)cm).getMappableContactList(_aa.sequenceRef, column); + } + } // TODO: could resolve sequence position to column position here // TODO: what about for complexes - where contactMatrix may involve two or // more sequences @@ -49,9 +58,9 @@ public class ContactMapHolder implements ContactMapHolderI contactmaps.put(aa.annotationId, cm); // TODO: contact matrices could be intra or inter - more than one refseq // possible! - if (cm.hasReferenceSeq()) + if (cm instanceof MappableContactMatrixI) { - aa.setSequenceRef(cm.getReferenceSeq()); + aa.setSequenceRef(((MappableContactMatrixI) cm).getReferenceSeq()); } return aa; } diff --git a/src/jalview/datamodel/ContactMatrix.java b/src/jalview/datamodel/ContactMatrix.java index 1b1889e..65fd01c 100644 --- a/src/jalview/datamodel/ContactMatrix.java +++ b/src/jalview/datamodel/ContactMatrix.java @@ -150,20 +150,6 @@ public abstract class ContactMatrix implements ContactMatrixI } @Override - public boolean hasReferenceSeq() - { - // TODO Auto-generated method stub - return false; - } - - @Override - public SequenceI getReferenceSeq() - { - // TODO Auto-generated method stub - return null; - } - - @Override public String getAnnotLabel() { return "Contact Matrix"; diff --git a/src/jalview/datamodel/ContactMatrixI.java b/src/jalview/datamodel/ContactMatrixI.java index 1c169ef..ba2ee48 100644 --- a/src/jalview/datamodel/ContactMatrixI.java +++ b/src/jalview/datamodel/ContactMatrixI.java @@ -14,10 +14,6 @@ public interface ContactMatrixI float getMax(); - boolean hasReferenceSeq(); - - SequenceI getReferenceSeq(); - String getAnnotDescr(); String getAnnotLabel(); @@ -72,5 +68,6 @@ public interface ContactMatrixI void setColorForGroup(BitSet bs, Color color); - default Color getColourForGroup(BitSet bs) { return Color.white;}; + default Color getColourForGroup(BitSet bs) { return Color.white;} + } diff --git a/src/jalview/datamodel/SeqDistanceContactMatrix.java b/src/jalview/datamodel/SeqDistanceContactMatrix.java index c3f3670..f6377b1 100644 --- a/src/jalview/datamodel/SeqDistanceContactMatrix.java +++ b/src/jalview/datamodel/SeqDistanceContactMatrix.java @@ -5,13 +5,16 @@ import java.util.BitSet; import java.util.HashMap; import java.util.List; +import jalview.util.MapList; +import jalview.ws.datamodel.alphafold.MappableContactMatrix; + /** * Dummy contact matrix based on sequence distance * * @author jprocter * */ -public class SeqDistanceContactMatrix implements ContactMatrixI +public class SeqDistanceContactMatrix extends MappableContactMatrix implements ContactMatrixI { private static final String SEQUENCE_DISTANCE = "SEQUENCE_DISTANCE"; private int width = 0; @@ -79,21 +82,6 @@ public class SeqDistanceContactMatrix implements ContactMatrixI } }); } - - @Override - public boolean hasReferenceSeq() - { - // TODO Auto-generated method stub - return false; - } - - @Override - public SequenceI getReferenceSeq() - { - // TODO Auto-generated method stub - return null; - } - @Override public String getAnnotDescr() { @@ -159,4 +147,17 @@ public class SeqDistanceContactMatrix implements ContactMatrixI { colorMap.put(bs,color); } + @Override + protected double getElementAt(int _column, int i) + { + return Math.abs(_column-i); + } + + @Override + protected SeqDistanceContactMatrix newMappableContactMatrix( + SequenceI newRefSeq, MapList newFromMapList) + { + + return new SeqDistanceContactMatrix(width); + } } diff --git a/src/jalview/renderer/ContactGeometry.java b/src/jalview/renderer/ContactGeometry.java index 4aef1d8..2eb325c 100644 --- a/src/jalview/renderer/ContactGeometry.java +++ b/src/jalview/renderer/ContactGeometry.java @@ -2,10 +2,23 @@ package jalview.renderer; import java.util.Iterator; +import jalview.datamodel.ColumnSelection; import jalview.datamodel.ContactListI; - +import jalview.datamodel.HiddenColumns; +import jalview.renderer.ContactGeometry.contactInterval; + +/** + * encapsulate logic for mapping between positions in a ContactList and their + * rendered representation in a given number of pixels. + * + * @author jprocter + * + */ public class ContactGeometry { + + final ContactListI contacts; + final int pixels_step; final double contacts_per_pixel; @@ -14,8 +27,9 @@ public class ContactGeometry final int graphHeight; - public ContactGeometry(ContactListI contacts, int graphHeight) + public ContactGeometry(final ContactListI contacts, int graphHeight) { + this.contacts=contacts; this.graphHeight = graphHeight; contact_height = contacts.getContactHeight(); // fractional number of contacts covering each pixel @@ -54,6 +68,47 @@ public class ContactGeometry public final int pStart; public final int pEnd; + + } + /** + * + * @param columnSelection + * @param ci + * @param visibleOnly - when true, only test intersection of visible columns given matrix range + * @return true if the range on the matrix specified by ci intersects with selected columns in the ContactListI's reference frame. + */ + + boolean intersects(contactInterval ci,ColumnSelection columnSelection, HiddenColumns hiddenColumns, boolean visibleOnly) { + boolean rowsel = false; + final int[] mappedRange = contacts.getMappedPositionsFor(ci.cStart, ci.cEnd); + if (mappedRange==null) + { + return false; + } + boolean containsHidden=false; + if (visibleOnly && hiddenColumns!=null && hiddenColumns.hasHiddenColumns()) + { + // TODO: turn into function on hiddenColumns and create test !! + Iterator viscont = hiddenColumns + .getVisContigsIterator(mappedRange[0], mappedRange[1], false); + containsHidden = !viscont.hasNext(); + if (!containsHidden) + { + for (int[] interval=viscont.next();viscont.hasNext(); + rowsel |= columnSelection.intersects(interval[0],interval[1])) + ; + } + } + else + { + // if containsHidden is true mappedRange is not visible + if (containsHidden) + { + rowsel = columnSelection.intersects(mappedRange[0], mappedRange[1]); + } + } + return rowsel; + } /** diff --git a/src/jalview/renderer/ContactMapRenderer.java b/src/jalview/renderer/ContactMapRenderer.java index e54f471..00a6169 100644 --- a/src/jalview/renderer/ContactMapRenderer.java +++ b/src/jalview/renderer/ContactMapRenderer.java @@ -146,19 +146,25 @@ public abstract class ContactMapRenderer implements AnnotationRowRendererI x++; continue; } - Color gpcol = (cm==null) ? Color.white: cm.getColourForGroup(cm.getGroupsFor(column)); + // ContactListI from viewport can map column -> group + Color gpcol = (cm==null) ? Color.white: contacts.getColourForGroup(); // cm.getColourForGroup(cm.getGroupsFor(column)); + // feature still in development - highlight or omit regions hidden in // the alignment - currently marks them as red rows boolean maskHiddenCols = false; - // TODO: pass visible column mask to the ContactGeometry object so it maps + // TODO: optionally pass visible column mask to the ContactGeometry object so it maps // only visible contacts to geometry // Bean holding mapping from contact list to pixels + // TODO: allow bracketing/limiting of range on contacts to render (like visible column mask but more flexible?) + + // COntactListI provides mapping for column -> cm-groupmapping final ContactGeometry cgeom = new ContactGeometry(contacts, _aa.graphHeight); for (int ht = y2, eht = y2 - _aa.graphHeight; ht >= eht; ht -= cgeom.pixels_step) { + ContactGeometry.contactInterval ci = cgeom.mapFor(y2 - ht, y2 - ht + cgeom.pixels_step); // cstart = (int) Math.floor(((double) y2 - ht) * contacts_per_pixel); @@ -169,29 +175,7 @@ public abstract class ContactMapRenderer implements AnnotationRowRendererI boolean rowsel = false, containsHidden = false; if (columnSelection != null) { - if (_aa.sequenceRef == null) - { - rowsel = columnSelection.intersects(ci.cStart, ci.cEnd); - } - else - { - // TODO check we have correctly mapped cstart to local sequence - // numbering - int s = _aa.sequenceRef.findIndex(ci.cStart); - int e = _aa.sequenceRef.findIndex(ci.cEnd); - if (maskHiddenCols && hasHiddenColumns) - { - // TODO: turn into function and create test !! - Iterator viscont = hiddenColumns - .getVisContigsIterator(s, e, false); - containsHidden = !viscont.hasNext(); - } - if (s > 0 && s < _aa.sequenceRef.getLength()) - { - rowsel = columnSelection.intersects(s, e); - } - - } + rowsel = cgeom.intersects(ci, columnSelection, hiddenColumns, maskHiddenCols); } // TODO: show selected region if (colsel || rowsel) diff --git a/src/jalview/ws/datamodel/MappableContactMatrixI.java b/src/jalview/ws/datamodel/MappableContactMatrixI.java new file mode 100644 index 0000000..4abaa5d --- /dev/null +++ b/src/jalview/ws/datamodel/MappableContactMatrixI.java @@ -0,0 +1,31 @@ +package jalview.ws.datamodel; + +import jalview.datamodel.ContactListI; +import jalview.datamodel.ContactMatrixI; +import jalview.datamodel.Mapping; +import jalview.datamodel.SequenceI; + +public interface MappableContactMatrixI extends ContactMatrixI +{ + + boolean hasReferenceSeq(); + + SequenceI getReferenceSeq(); + /** + * remaps the matrix to a new reference sequence + * @param dsq + * @param sqmpping - mapping from current reference to new reference - 1:1 only + * @return new ContactMatrixI instance with updated mapping + */ + MappableContactMatrixI liftOver(SequenceI dsq, Mapping sqmpping); + + /** + * like ContactMatrixI.getContactList(int column) but + * @param localFrame - sequence or other object that this contact matrix is associated with + * @param column - position in localFrame + * @return ContactListI that returns contacts w.r.t. localFrame + */ + + ContactListI getMappableContactList(SequenceI localFrame, int column); + +} diff --git a/src/jalview/ws/datamodel/alphafold/PAEContactMatrix.java b/src/jalview/ws/datamodel/alphafold/PAEContactMatrix.java index 22dd7fb..9d7891d 100644 --- a/src/jalview/ws/datamodel/alphafold/PAEContactMatrix.java +++ b/src/jalview/ws/datamodel/alphafold/PAEContactMatrix.java @@ -11,51 +11,36 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import org.json.simple.JSONObject; import jalview.analysis.AverageDistanceEngine; import jalview.bin.Console; +import jalview.datamodel.Annotation; import jalview.datamodel.BinaryNode; import jalview.datamodel.ContactListI; import jalview.datamodel.ContactListImpl; import jalview.datamodel.ContactListProviderI; import jalview.datamodel.ContactMatrixI; +import jalview.datamodel.Mapping; import jalview.datamodel.SequenceDummy; import jalview.datamodel.SequenceI; import jalview.io.DataSourceType; import jalview.io.FileFormatException; import jalview.io.FileParse; +import jalview.util.MapList; import jalview.util.MapUtils; import jalview.ws.dbsources.EBIAlfaFold; -public class PAEContactMatrix implements ContactMatrixI +public class PAEContactMatrix extends MappableContactMatrix 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) throws FileFormatException @@ -105,6 +90,19 @@ public class PAEContactMatrix implements ContactMatrixI } /** + * new matrix with specific mapping to a reference sequence + * @param newRefSeq + * @param newFromMapList + * @param elements2 + */ + public PAEContactMatrix(SequenceI newRefSeq, + MapList newFromMapList, float[][] elements2) + { + this(newRefSeq,elements2); + toSeq = newFromMapList; + } + + /** * parse a sane JSON representation of the pAE * * @param pae_obj @@ -209,8 +207,22 @@ public class PAEContactMatrix implements ContactMatrixI } @Override - public ContactListI getContactList(final int _column) + public ContactListI getContactList(final int column) { + final int _column; + if (toSeq != null) + { + int[] word = toSeq.locateInTo(column, column); + if (word == null) + { + return null; + } + _column = word[0]; + } + else + { + _column = column; + } if (_column < 0 || _column >= elements.length) { return null; @@ -231,18 +243,24 @@ public class PAEContactMatrix implements ContactMatrixI } @Override - public double getContactAt(int column) + public double getContactAt(int mcolumn) { - if (column < 0 || column >= elements[_column].length) + int[] column=(toSeq==null) ? new int[] {mcolumn} : toSeq.locateInTo(mcolumn,mcolumn); + if (column==null || column[0] < 0 || column[0] >= elements[_column].length) { return -1; } - return elements[_column][column]; + return elements[_column][column[0]]; } }); } @Override + protected double getElementAt(int _column, int i) + { + return elements[_column][i]; + } + @Override public float getMin() { return 0; @@ -255,18 +273,6 @@ public class PAEContactMatrix implements ContactMatrixI } @Override - public boolean hasReferenceSeq() - { - return (refSeq != null); - } - - @Override - public SequenceI getReferenceSeq() - { - return refSeq; - } - - @Override public String getAnnotDescr() { return "Predicted Alignment Error"+((refSeq==null) ? "" : (" for " + refSeq.getName())); @@ -367,10 +373,14 @@ public class PAEContactMatrix implements ContactMatrixI @Override public BitSet getGroupsFor(int column) { - for (BitSet gp:groups) { - if (gp.get(column)) + if (groups != null) + { + for (BitSet gp : groups) { - return gp; + if (gp.get(column)) + { + return gp; + } } } return ContactMatrixI.super.getGroupsFor(column); @@ -443,5 +453,13 @@ public class PAEContactMatrix implements ContactMatrixI { throw new FileFormatException("No data in PAE matrix read from '"+fileName+"'"); } + } + + @Override + protected PAEContactMatrix newMappableContactMatrix( + SequenceI newRefSeq, MapList newFromMapList) + { + return new PAEContactMatrix(newRefSeq, newFromMapList, + elements); } } diff --git a/src/mc_view/PDBChain.java b/src/mc_view/PDBChain.java index 6d02630..c56a947 100755 --- a/src/mc_view/PDBChain.java +++ b/src/mc_view/PDBChain.java @@ -39,6 +39,7 @@ import jalview.schemes.ResidueProperties; import jalview.structure.StructureImportSettings; import jalview.structure.StructureMapping; import jalview.util.Comparison; +import jalview.ws.datamodel.MappableContactMatrixI; public class PDBChain { @@ -700,9 +701,9 @@ public class PDBChain ana = new AlignmentAnnotation(ana); ana.liftOver(dsq, sqmpping); dsq.addAlignmentAnnotation(ana); - if (cm != null) + if (cm != null && cm instanceof MappableContactMatrixI) { - dsq.addContactListFor(ana, cm); + dsq.addContactListFor(ana, ((MappableContactMatrixI) cm).liftOver(dsq,sqmpping)); } } else diff --git a/test/jalview/datamodel/PAEContactMatrixTest.java b/test/jalview/datamodel/PAEContactMatrixTest.java new file mode 100644 index 0000000..7426cba --- /dev/null +++ b/test/jalview/datamodel/PAEContactMatrixTest.java @@ -0,0 +1,151 @@ +package jalview.datamodel; + +import static org.testng.Assert.*; + +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import jalview.gui.JvOptionPane; +import jalview.util.MapList; +import jalview.ws.datamodel.MappableContactMatrixI; +import jalview.ws.datamodel.alphafold.PAEContactMatrix; + +public class PAEContactMatrixTest +{ + @BeforeClass(alwaysRun = true) + public void setUpJvOptionPane() + { + JvOptionPane.setInteractiveMode(false); + JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); + } + + static float[][] PAEdata = { + {1.0f,2.0f,3.0f,4.0f,5.0f}, + {2.0f,1.0f,2.0f,3.0f,4.0f}, + {3.0f,2.0f,1.0f,2.0f,3.0f}, + {4.0f,3.0f,2.0f,1.0f,2.0f}, + {5.0f,4.0f,3.0f,2.0f,1.0f}}; + + /** + * test associations for a PAE matrix + */ + @Test(groups = { "Functional" }) + public void testSeqAssociatedPAEMatrix() + { + Sequence seq = new Sequence("Seq","ASDQE"); + AlignmentAnnotation aa = seq.addContactList(new PAEContactMatrix(seq, PAEdata)); + assertNotNull(seq.getContactListFor(aa, 0)); + assertEquals(seq.getContactListFor(aa, 0).getContactAt(0),1.0); + assertNotNull(seq.getContactListFor(aa, 1)); + assertEquals(seq.getContactListFor(aa, 1).getContactAt(1),1.0); + assertNotNull(seq.getContactListFor(aa, 2)); + assertEquals(seq.getContactListFor(aa, 2).getContactAt(2),1.0); + assertNotNull(seq.getContactListFor(aa, 3)); + assertEquals(seq.getContactListFor(aa, 3).getContactAt(3),1.0); + assertNotNull(seq.getContactListFor(aa, 4)); + assertEquals(seq.getContactListFor(aa, 4).getContactAt(4),1.0); + + assertNotNull(seq.getContactListFor(aa, seq.getEnd()-1)); + assertNull(seq.getContactListFor(aa, seq.getEnd())); + + ContactListI cm = seq.getContactListFor(aa, seq.getStart()); + assertEquals(cm.getContactAt(seq.getStart()),1d); + verifyPAEmatrix(seq, aa, 0,0,4); + + // Now associated with sequence not starting at 1 + seq = new Sequence("Seq/5-9","ASDQE"); + ContactMatrixI paematrix = new PAEContactMatrix(seq, PAEdata); + aa = seq.addContactList(paematrix); + cm = seq.getContactListFor(aa, 0); + assertEquals(cm.getContactAt(0),1d); + verifyPAEmatrix(seq, aa, 0, 0, 4); + + // remap - test the MappableContactMatrix.liftOver method + SequenceI newseq = new Sequence("Seq","ASDQEASDQEASDQE"); + Mapping sqmap = new Mapping(seq, new MapList(new int[] {5,8,10,10},new int[] { 5,9}, 1, 1)); + assertTrue(paematrix instanceof MappableContactMatrixI); + + MappableContactMatrixI remapped = ((MappableContactMatrixI)paematrix).liftOver(newseq, sqmap); + assertTrue(remapped instanceof PAEContactMatrix); + + AlignmentAnnotation newaa = newseq.addContactList(remapped); + assertNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(1))); + assertNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(4))); + assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(5))); + assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(6))); + assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(7))); + assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(8))); + // no mapping for position 9 + assertNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(9))); + // last column + assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(10))); + + // remap2 - test with original matrix map from 1-5 remapped to 5-9 + + seq = new Sequence("Seq/1-5","ASDQE"); + paematrix = new PAEContactMatrix(seq, PAEdata); + assertTrue(paematrix instanceof MappableContactMatrixI); + aa = seq.addContactList(paematrix); + + newseq = new Sequence("Seq","ASDQEASDQEASDQE"); + sqmap = new Mapping(seq, new MapList(new int[] {5,9},new int[] { 1,5}, 1, 1)); + + remapped = ((MappableContactMatrixI)paematrix).liftOver(newseq, sqmap); + assertTrue(remapped instanceof PAEContactMatrix); + + + newaa = newseq.addContactList(remapped); + verify_mapping(newseq, newaa); + + // remap3 - remap2 but mapping sense in liftover is reversed + + seq = new Sequence("Seq/1-5","ASDQE"); + paematrix = new PAEContactMatrix(seq, PAEdata); + assertTrue(paematrix instanceof MappableContactMatrixI); + aa = seq.addContactList(paematrix); + + newseq = new Sequence("Seq","ASDQEASDQEASDQE"); + sqmap = new Mapping(newseq, new MapList(new int[] { 1,5},new int[] {5,9},1, 1)); + + remapped = ((MappableContactMatrixI)paematrix).liftOver(newseq, sqmap); + assertTrue(remapped instanceof PAEContactMatrix); + + + newaa = newseq.addContactList(remapped); + verify_mapping(newseq, newaa); + + } + /** + * checks that the PAE matrix is located at positions 1-9 in newseq, and columns are not truncated. + * @param newseq + * @param newaa + */ + private void verify_mapping(SequenceI newseq, AlignmentAnnotation newaa) + { + assertNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(1))); + assertNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(4))); + assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(5))); + assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(6))); + assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(7))); + assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(8))); + assertNotNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(9))); + // last column should be null this time + assertNull(newseq.getContactListFor(newaa, -1+newseq.findIndex(10))); + + verifyPAEmatrix(newseq, newaa, 4, 4, 8); + + + } + + private void verifyPAEmatrix(SequenceI seq, AlignmentAnnotation aa, int topl, int rowl, int rowr) + { + for (int f=rowl;f<=rowr;f++) { + ContactListI clist = seq.getContactListFor(aa, f); + assertNotNull(clist,"No ContactListI for position "+(f)); + assertEquals(clist.getContactAt(0), (double) f-topl+1,"for column "+f+" relative to "+topl); + assertEquals(clist.getContactAt(f-topl),1d, "for column and row "+f+" relative to "+topl); + } + } + +} -- 1.7.10.2