Skip to content

Commit 9f09e95

Browse files
author
Dmytro Guzenko
committed
comments and fine-tuning the local symmetry calculations
1 parent f68a180 commit 9f09e95

File tree

3 files changed

+69
-23
lines changed

3 files changed

+69
-23
lines changed

biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/QuatSymmetryDetector.java

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
import org.biojava.nbio.structure.Structure;
2626
import org.biojava.nbio.structure.cluster.*;
27+
import org.jgrapht.graph.DefaultDirectedGraph;
28+
import org.jgrapht.traverse.TopologicalOrderIterator;
2729
import org.slf4j.Logger;
2830
import 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)) {

biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/QuatSymmetryResults.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,42 @@ public QuatSymmetryResults(List<SubunitCluster> clusters,
107107
this.method = method;
108108
}
109109

110+
/**
111+
* Determine if this symmetry result is a subset of the other Symmetry result.
112+
* Checks the following conditions:
113+
* - 'Other' includes all subunits of 'this'.
114+
* - 'Other' has the same or higher order than 'this'.
115+
*
116+
* Special treatment for the helical symmetry:
117+
* - 'Other' includes all subunits of 'this'.
118+
* - 'this' may be Cn, as well as H
119+
*
120+
* Note that isSupersededBy establishes a partial order, i.e. for some
121+
* symmetries A and B, neither A.isSupersededBy(B) nor B.isSupersededBy(A)
122+
* may be true.
123+
*
124+
* @param other
125+
* QuatSymmetryResults
126+
*
127+
* @return true if other supersedes this, false otherwise
128+
*/
110129

111130
public boolean isSupersededBy(QuatSymmetryResults other) {
112-
if (this.rotationGroup.getOrder() <= other.rotationGroup.getOrder() && other.subunits.containsAll(this.subunits)) {
131+
if(other.getSymmetry().startsWith("H")) {
132+
if(this.getSymmetry().startsWith("C") || this.getSymmetry().startsWith("H")) {
133+
if (other.subunits.containsAll(this.subunits)) {
134+
return true;
135+
}
136+
}
137+
return false;
138+
}
139+
140+
if (this.getSymmetry().startsWith("H")) {
141+
return false;
142+
}
143+
144+
if (this.rotationGroup.getOrder() <= other.rotationGroup.getOrder() &&
145+
other.subunits.containsAll(this.subunits)) {
113146
return true;
114147
}
115148
return false;

biojava-structure/src/main/java/org/biojava/nbio/structure/symmetry/core/SubunitContactGraph.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public class SubunitContactGraph {
4646
.getLogger(SubunitContactGraph.class);
4747

4848
private static final double DISTANCE_CUTOFF = 8;
49-
private static final int MIN_CONTACTS = 10;
49+
private static final int MIN_CONTACTS = 5;
5050

5151
private SubunitContactGraph() {
5252
}

0 commit comments

Comments
 (0)