JAL-4134 allow tree groups to be stored/recovered on contact matrix for groupwise...
authorJames Procter <j.procter@dundee.ac.uk>
Mon, 27 Feb 2023 17:18:44 +0000 (17:18 +0000)
committerJames Procter <j.procter@dundee.ac.uk>
Mon, 27 Feb 2023 17:25:43 +0000 (17:25 +0000)
src/jalview/api/AlignViewportI.java
src/jalview/datamodel/Alignment.java
src/jalview/datamodel/ContactMatrixI.java
src/jalview/gui/AnnotationPanel.java
src/jalview/viewmodel/AlignmentViewport.java
src/jalview/ws/datamodel/alphafold/PAEContactMatrix.java
src/jalview/ws/dbsources/EBIAlfaFold.java
test/jalview/analysis/AverageDistanceEngineTest.java

index b09538e..03efec5 100644 (file)
@@ -28,6 +28,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.AlignmentView;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.ContactListI;
+import jalview.datamodel.ContactMatrixI;
 import jalview.datamodel.ProfilesI;
 import jalview.datamodel.SearchResultsI;
 import jalview.datamodel.SequenceCollectionI;
@@ -555,4 +556,6 @@ public interface AlignViewportI extends ViewStyleI
    * @return
    */
   Iterator<int[]> getViewAsVisibleContigs(boolean selectedRegionOnly);
+
+  ContactMatrixI getContactMatrix(AlignmentAnnotation alignmentAnnotation);
 }
index 7f97f33..321eee3 100755 (executable)
@@ -2047,9 +2047,23 @@ public class Alignment implements AlignmentI, AutoCloseable
   }
 
   @Override
-  public ContactMatrixI getContactMatrixFor(AlignmentAnnotation ann)
+  public ContactMatrixI getContactMatrixFor(AlignmentAnnotation _aa)
   {
-    return cmholder.getContactMatrixFor(ann);
+    ContactMatrixI cm = cmholder.getContactMatrixFor(_aa);
+    if (cm==null && _aa.groupRef!=null)
+    {
+      cm = _aa.groupRef.getContactMatrixFor(_aa);
+    }
+    if (cm==null && _aa.sequenceRef!=null)
+    {
+      cm = _aa.sequenceRef.getContactMatrixFor(_aa);
+      if (cm==null)
+      {
+        // TODO fix up this logic and unify with getContactListFor
+        cm = _aa.sequenceRef.getDatasetSequence().getContactMatrixFor(_aa);
+      }
+    }
+    return cm;
   }
 
   @Override
index 2367414..4e2076d 100644 (file)
@@ -1,5 +1,7 @@
 package jalview.datamodel;
 
+import java.util.BitSet;
+
 public interface ContactMatrixI
 {
 
@@ -25,5 +27,14 @@ public interface ContactMatrixI
 
   int getWidth();
   int getHeight();
+  
+  default boolean hasGroups() {
+    return false;
+  }
+  default BitSet getGroupsFor(int column) {
+    BitSet colbitset  = new BitSet();
+    colbitset.set(column);
+    return colbitset;
+  }
 
 }
index b68ff01..3aacb07 100755 (executable)
@@ -41,6 +41,7 @@ import java.awt.event.MouseWheelListener;
 import java.awt.image.BufferedImage;
 import java.beans.PropertyChangeEvent;
 import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.List;
 
@@ -56,6 +57,7 @@ import jalview.datamodel.AlignmentI;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.ContactListI;
+import jalview.datamodel.ContactMatrixI;
 import jalview.datamodel.ContactRange;
 import jalview.datamodel.GraphLine;
 import jalview.datamodel.HiddenColumns;
@@ -603,6 +605,26 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
         {
           GraphLine thr = aa[graphStretch].getThreshold();
           int currentX = getColumnForXPos(evt.getX());
+          ContactMatrixI matrix = av.getContactMatrix(aa[graphStretch]);
+          if (matrix!=null)
+          {
+            if (matrix.hasGroups())
+            {
+            SequenceI rseq = aa[graphStretch].sequenceRef;
+            BitSet grp = matrix.getGroupsFor(currentX);
+            ColumnSelection cs = av.getColumnSelection();
+            HiddenColumns hc = av.getAlignment().getHiddenColumns();
+            for (int p=grp.nextSetBit(0); p>=0; p = grp.nextSetBit(p+1))
+            {
+              int offp = (rseq!=null) ? rseq.findIndex(rseq.getStart()-1+p) : p;
+              
+              if (!av.hasHiddenColumns() || hc.isVisible(offp))
+              { 
+                av.getColumnSelection().addElement(offp);
+              }
+            }
+          } else 
+          {
           ContactListI forCurrentX = av.getContactList(aa[graphStretch],
                   currentX);
           if (forCurrentX != null)
@@ -662,9 +684,10 @@ public class AnnotationPanel extends JPanel implements AwtRenderPanelI,
                 }
               }
             }
+            }
           }
         }
-      }
+      }}
     }
     else
     {
index a42a2a4..36b0851 100644 (file)
@@ -51,6 +51,7 @@ import jalview.datamodel.AlignmentView;
 import jalview.datamodel.Annotation;
 import jalview.datamodel.ColumnSelection;
 import jalview.datamodel.ContactListI;
+import jalview.datamodel.ContactMatrixI;
 import jalview.datamodel.HiddenColumns;
 import jalview.datamodel.HiddenSequences;
 import jalview.datamodel.ProfilesI;
@@ -2948,6 +2949,14 @@ public abstract class AlignmentViewport
     return alignment.getContactListFor(_aa, column);
   }
 
+  @Override
+  public ContactMatrixI getContactMatrix(
+          AlignmentAnnotation alignmentAnnotation)
+  {
+    return alignment.getContactMatrixFor(alignmentAnnotation);
+  }
+
+
   /**
    * get the consensus sequence as displayed under the PID consensus annotation
    * row.
index 48071bd..30c77d2 100644 (file)
@@ -1,9 +1,14 @@
 package jalview.ws.datamodel.alphafold;
 
+import java.util.ArrayList;
+import java.util.BitSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import jalview.analysis.AverageDistanceEngine;
+import jalview.bin.Console;
+import jalview.datamodel.BinaryNode;
 import jalview.datamodel.ContactListI;
 import jalview.datamodel.ContactListImpl;
 import jalview.datamodel.ContactListProviderI;
@@ -239,4 +244,60 @@ public class PAEContactMatrix implements ContactMatrixI
   {
     return length;
   }
+  List<BitSet> groups=null;
+  @Override
+  public boolean hasGroups()
+  {
+    return groups!=null;
+  }
+  String newick=null;
+  public String getNewickString()
+  {
+    return newick;
+  }
+  public void makeGroups(float thresh,boolean abs)
+  {
+    AverageDistanceEngine clusterer = new AverageDistanceEngine(null, null, this);
+    double height = clusterer.findHeight(clusterer.getTopNode());
+    newick = new jalview.io.NewickFile(clusterer.getTopNode(),false,true).print();
+
+    Console.trace("Newick string\n"+newick);
+
+    List<BinaryNode> nodegroups;
+    if (abs ? height > thresh : 0 < thresh && thresh < 1)
+    {
+      float cut = abs ? (float) (thresh / height) : thresh;
+      Console.debug("Threshold "+cut+" for height="+height);
+
+      nodegroups = clusterer.groupNodes(cut);
+    }
+    else
+    {
+      nodegroups = new ArrayList<BinaryNode>();
+      nodegroups.add(clusterer.getTopNode());
+    }
+
+    groups = new ArrayList<>();
+    for (BinaryNode root:nodegroups)
+    {
+      BitSet gpset=new BitSet();
+      for (BinaryNode leaf:clusterer.findLeaves(root))
+      {
+        gpset.set((Integer)leaf.element());
+      }
+      groups.add(gpset);
+    }
+  }
+  
+  @Override
+  public BitSet getGroupsFor(int column)
+  {
+    for (BitSet gp:groups) {
+      if (gp.get(column))
+      {
+        return gp;
+      }
+    }
+    return ContactMatrixI.super.getGroupsFor(column);
+  }
 }
index 72fd8d9..dd71ec3 100644 (file)
@@ -410,6 +410,7 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy
     }
     ContactMatrixI matrix = new PAEContactMatrix(sequence,
             (Map<String, Object>) paeDict);
+    ((PAEContactMatrix) matrix).makeGroups(5f, true);
 
     AlignmentAnnotation cmannot = sequence.addContactList(matrix);
     pdbAlignment.addAnnotation(cmannot);
@@ -468,7 +469,7 @@ public class EBIAlfaFold extends EbiFileRetrievedProxy
 
     ContactMatrixI matrix = new PAEContactMatrix(sm.getSequence(),
             (Map<String, Object>) pae_obj);
-
+    ((PAEContactMatrix) matrix).makeGroups(5f, true);
     AlignmentAnnotation cmannot = sm.getSequence().addContactList(matrix);
     // sm.getSequence().addAlignmentAnnotation(cmannot);
     sm.transfer(cmannot);
index 7c068cb..6d9ab50 100644 (file)
@@ -4,6 +4,7 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
@@ -58,14 +59,28 @@ public class AverageDistanceEngineTest
       ContactMatrixI matrix = new PAEContactMatrix(target,
               (Map<String, Object>) pae_obj.get(0));
       AlignmentAnnotation aa = target.addContactList(matrix);
+      System.out.println("Matrix has max="+matrix.getMax()+" and min="+matrix.getMin());
       long start = System.currentTimeMillis();
       AverageDistanceEngine clusterer = new AverageDistanceEngine(af.getViewport(), null, matrix);
       System.out.println("built a tree in "+(System.currentTimeMillis()-start)*0.001+" seconds.");
       StringBuffer sb = new StringBuffer(); 
       System.out.println("Newick string\n"+      new jalview.io.NewickFile(clusterer.getTopNode(),true,true).print());
       
-      clusterer.findHeight(clusterer.getTopNode());
-      List<BinaryNode> groups = clusterer.groupNodes(0.8f);
+      double height = clusterer.findHeight(clusterer.getTopNode());
+      // compute height fraction to cut 
+      // PAE matrixes are absolute measure in angstrom, so 
+      // cluster all regions within threshold (e.g. 2A) - if height above threshold. Otherwise all nodes are in one cluster
+      double thr=.2;
+      List<BinaryNode> groups;
+      if (height>thr)
+      {
+        float cut = (float) (thr/height);
+        System.out.println("Threshold "+cut+" for height="+height);
+        groups = clusterer.groupNodes(cut);
+      } else{
+        groups=new ArrayList<BinaryNode>();
+        groups.add(clusterer.getTopNode());
+      }
       int n=1;
       for (BinaryNode root:groups)
       {