2424
2525import org .biojava .nbio .structure .Structure ;
2626import org .biojava .nbio .structure .cluster .*;
27+ import org .jgrapht .graph .DefaultDirectedGraph ;
28+ import org .jgrapht .traverse .TopologicalOrderIterator ;
2729import org .slf4j .Logger ;
2830import org .slf4j .LoggerFactory ;
2931
@@ -186,8 +188,7 @@ public static List<QuatSymmetryResults> calcLocalSymmetries(List<SubunitCluster>
186188 symmParams .setLocalTimeStart (System .nanoTime ());
187189
188190 Set <Set <Integer >> knownResults = new HashSet <>();
189- List <QuatSymmetryResults > outputSymmetries = new ArrayList <>();
190-
191+ //more than one subunit per cluster required for symmetry
191192 List <SubunitCluster > nontrivialClusters =
192193 clusters .stream ().
193194 filter (cluster -> (cluster .size ()>1 )).
@@ -196,26 +197,31 @@ public static List<QuatSymmetryResults> calcLocalSymmetries(List<SubunitCluster>
196197 QuatSymmetrySubunits allSubunits = new QuatSymmetrySubunits (nontrivialClusters );
197198
198199 if (allSubunits .getSubunitCount () < 2 )
199- return outputSymmetries ;
200+ return new ArrayList <>() ;
200201
201202 Graph <Integer , DefaultEdge > graph = SubunitContactGraph .calculateGraph (allSubunits .getTraces ());
202203
203204 List <Integer > allSubunitIds = new ArrayList <>(graph .vertexSet ());
204205 Collections .sort (allSubunitIds );
205206 List <Integer > allSubunitClusterIds = allSubunits .getClusterIds ();
206207
208+ // since clusters are rearranged and trimmed, we need a reference to the original data
209+ // to maintain consistent IDs of clusters and subunits across all solutions
207210 Map <Integer , List <Integer >> clusterIdToSubunitIds =
208211 allSubunitIds .stream ().
209212 collect (Collectors .
210213 groupingBy (allSubunitClusterIds ::get , Collectors .toList ()));
211214
212215 List <QuatSymmetryResults > redundantSymmetries = new ArrayList <>();
216+ // first, find symmetries for single clusters and their groups
217+ // grouping is done based on symmetries found (i.e., no exhaustive permutation search is performed)
213218 if (clusters .size ()>1 ) {
214219 List <QuatSymmetryResults > clusterSymmetries =
215220 calcLocalSymmetriesCluster (nontrivialClusters , clusterIdToSubunitIds ,symmParams , knownResults );
216221 redundantSymmetries .addAll (clusterSymmetries );
217222 }
218-
223+ //find symmetries for groups based on connectivity of subunits
224+ // disregarding initial clustering
219225 List <QuatSymmetryResults > graphSymmetries = calcLocalSymmetriesGraph (nontrivialClusters ,
220226 allSubunitClusterIds ,
221227 clusterIdToSubunitIds ,
@@ -225,19 +231,16 @@ public static List<QuatSymmetryResults> calcLocalSymmetries(List<SubunitCluster>
225231
226232 redundantSymmetries .addAll (graphSymmetries );
227233
228- redundantSymmetries =
229- redundantSymmetries .stream ().
230- sorted (Comparator .
231- comparing ((QuatSymmetryResults r ) -> r .getRotationGroup ().getOrder ()).
232- thenComparing (r -> r .getSubunitCount ()).reversed ()).
233- collect (Collectors .toList ());
234+ // find symmetries which are not superseded by any other symmetry
235+ // e.g., we have global stoichiometry of A3B3C,
236+ // the local symmetries found are C3 with stoichiometries A3, B3, A3B3;
237+ // then output only A3B3.
234238
235- for (QuatSymmetryResults redundantSymmetry :redundantSymmetries ) {
236- if (outputSymmetries .stream ().
237- noneMatch (redundantSymmetry ::isSupersededBy )) {
238- outputSymmetries .add (redundantSymmetry );
239- }
240- }
239+ List <QuatSymmetryResults > outputSymmetries =
240+ redundantSymmetries .stream ().
241+ filter (a -> redundantSymmetries .stream ().
242+ noneMatch (b -> a !=b && a .isSupersededBy (b ))).
243+ collect (Collectors .toList ());
241244
242245 double time = (System .nanoTime () - symmParams .getLocalTimeStart ()) / 1000000000 ;
243246 if (time > symmParams .getLocalTimeLimit ()) {
@@ -255,7 +258,7 @@ private static List<QuatSymmetryResults> calcLocalSymmetriesCluster(List<Subunit
255258
256259 List <QuatSymmetryResults > clusterSymmetries = new ArrayList <>();
257260
258- // find solutions for single clusters
261+ // find solutions for single clusters first
259262 for (int i =0 ;i <nontrivialClusters .size ();i ++) {
260263 SubunitCluster nontrivialCluster = nontrivialClusters .get (i );
261264 QuatSymmetryResults localResult =
@@ -265,6 +268,8 @@ private static List<QuatSymmetryResults> calcLocalSymmetriesCluster(List<Subunit
265268 localResult .setLocal (true );
266269 clusterSymmetries .add (localResult );
267270 Set <Integer > knownResult = new HashSet <>(clusterIdToSubunitIds .get (i ));
271+ // since symmetry is found,
272+ // do not try graph decomposition of this set of subunits later
268273 knownResults .add (knownResult );
269274 }
270275 }
@@ -287,7 +292,7 @@ private static List<QuatSymmetryResults> calcLocalSymmetriesCluster(List<Subunit
287292 if (clustersGroup .size () < 2 ) {
288293 continue ;
289294 }
290-
295+ //check if grouped clusters also have symmetry
291296 QuatSymmetryResults localResult = calcQuatSymmetry (clustersGroup ,symmParams );
292297
293298 if (localResult !=null && !localResult .getSymmetry ().equals ("C1" )) {
@@ -299,6 +304,8 @@ private static List<QuatSymmetryResults> calcLocalSymmetriesCluster(List<Subunit
299304 int i = nontrivialClusters .indexOf (cluster );
300305 knownResult .addAll (clusterIdToSubunitIds .get (i ));
301306 }
307+ // since symmetry is found,
308+ // do not try graph decomposition of this set of subunits later
302309 knownResults .add (knownResult );
303310 }
304311 }
@@ -316,28 +323,31 @@ private static List<QuatSymmetryResults> calcLocalSymmetriesGraph(final List<Sub
316323
317324 List <QuatSymmetryResults > localSymmetries = new ArrayList <>();
318325
326+ // do not go any deeper into recursion if over the time limit
319327 double time = (System .nanoTime () - symmParams .getLocalTimeStart ()) / 1000000000 ;
320328 if (time > symmParams .getLocalTimeLimit ()) {
321329 return localSymmetries ;
322330 }
323331
324- // calc components of a (sub-)graph
332+ // extract components of a (sub-)graph
325333 CliqueMinimalSeparatorDecomposition <Integer , DefaultEdge > cmsd =
326334 new CliqueMinimalSeparatorDecomposition <>(graph );
327335
336+ // only consider components with more than 1 vertex (subunit)
328337 Set <Set <Integer >> graphComponents =
329338 cmsd .getAtoms ().stream ().
330339 filter (component -> component .size ()>1 ).
331340 collect (Collectors .toSet ());
332341
333- //subtract known results
342+ //do not go into what has already been explored
334343 graphComponents .removeAll (knownResults );
335344
336345 for (Set <Integer > graphComponent : graphComponents ) {
337346 knownResults .add (graphComponent );
338347
339348 List <Integer > usedSubunitIds = new ArrayList <>(graphComponent );
340349 Collections .sort (usedSubunitIds );
350+ // get clusters which contain only subunits in the current component
341351 List <SubunitCluster > localClusters =
342352 trimSubunitClusters (allClusters , allSubunitClusterIds , clusterIdToSubunitIds , usedSubunitIds );
343353
@@ -346,6 +356,8 @@ private static List<QuatSymmetryResults> calcLocalSymmetriesGraph(final List<Sub
346356 }
347357
348358 //NB: usedSubunitIds might have changed when trimming clusters
359+ // if a subunit belongs to a cluster with no other subunits,
360+ // it is removed inside trimSubunitClusters
349361 Set <Integer > usedSubunitIdsSet = new HashSet <>(usedSubunitIds );
350362 if (!graphComponent .equals (usedSubunitIdsSet )) {
351363 if (knownResults .contains (usedSubunitIdsSet )) {
@@ -363,11 +375,12 @@ private static List<QuatSymmetryResults> calcLocalSymmetriesGraph(final List<Sub
363375 }
364376
365377 if (usedSubunitIds .size () < 3 ) {
378+ // cannot decompose this component any further
366379 continue ;
367380 }
368381
369382 for (Integer removeSubunitId : usedSubunitIds ) {
370-
383+ // try removing subunits one by one and decompose the sub-graph recursively
371384 Set <Integer > prunedGraphVertices = new HashSet <>(usedSubunitIds );
372385 prunedGraphVertices .remove (removeSubunitId );
373386 if (knownResults .contains (prunedGraphVertices )) {
0 commit comments