Browse Source

IntAdjacencyGraph.areConnected => areVertexSetsConnected + optimized

David Sehnal 6 years ago
parent
commit
167833385e

+ 36 - 15
src/mol-math/graph/int-adjacency-graph.ts

@@ -6,7 +6,7 @@
  */
 
 import { arrayPickIndices, cantorPairing } from 'mol-data/util';
-import { LinkedIndex } from 'mol-data/int';
+import { LinkedIndex, SortedArray } from 'mol-data/int';
 
 /**
  * Represent a graph using vertex adjacency list.
@@ -281,22 +281,43 @@ export namespace IntAdjacencyGraph {
 
         return { componentCount: vCount, componentIndex };
     }
+
+    /**
+     * Check if any vertex in `verticesA` is connected to any vertex in `verticesB`
+     * via at most `maxDistance` edges.
+     *
+     * Returns true if A and B are intersecting.
+     */
+    export function areVertexSetsConnected(graph: IntAdjacencyGraph, verticesA: SortedArray<number>, verticesB: SortedArray<number>, maxDistance: number): boolean {
+        // check if A and B are intersecting, this handles maxDepth = 0
+        if (SortedArray.areIntersecting(verticesA, verticesB)) return true;
+        if (maxDistance < 1) return false;
+
+        const visited = new Set<number>();
+        for (let i = 0, il = verticesA.length; i < il; ++i) {
+            visited.add(verticesA[i]);
+        }
+
+        return areVertexSetsConnectedImpl(graph, verticesA, verticesB, maxDistance, visited);
+    }
 }
 
-/**
- * Check if any vertex in `verticesA` is connected to any vertex in `verticesB`
- * via `depth` hops or intermediate vertices
- */
-export function areConnected(verticesA: ReadonlyArray<number>, verticesB: ReadonlyArray<number>, graph: IntAdjacencyGraph, depth: number): boolean {
-    const { b, offset } = graph
-    const linkedVectices: number[] = []
-    for (let i = 0, il = verticesA.length; i < il; ++i) {
-        const vi = verticesA[i]
-        for (let j = offset[vi], jl = offset[vi + 1]; j < jl; ++j) {
-            const li = b[j]
-            if (verticesB.includes(li)) return true
-            if (!verticesA.includes(li)) linkedVectices.push(li)
+function areVertexSetsConnectedImpl(graph: IntAdjacencyGraph, frontier: ArrayLike<number>, target: SortedArray<number>, distance: number, visited: Set<number>): boolean {
+    const { b: neighbor, offset } = graph;
+    const newFrontier: number[] = [];
+
+    for (let i = 0, il = frontier.length; i < il; ++i) {
+        const src = frontier[i];
+
+        for (let j = offset[src], jl = offset[src + 1]; j < jl; ++j) {
+            const other = neighbor[j];
+            if (visited.has(other)) continue;
+            if (SortedArray.has(target, other)) return true;
+
+            visited.add(other);
+            newFrontier[newFrontier.length] = other;
         }
     }
-    return depth > 0 ? areConnected(linkedVectices, verticesB, graph, depth - 1) : false
+
+    return distance > 1 ? areVertexSetsConnectedImpl(graph, newFrontier, target, distance - 1, visited) : false;
 }

+ 8 - 8
src/mol-model/structure/structure/carbohydrates/compute.ts

@@ -6,7 +6,7 @@
 
 import { Segmentation } from 'mol-data/int';
 import { combinations } from 'mol-data/util/combination';
-import { areConnected } from 'mol-math/graph';
+import { IntAdjacencyGraph } from 'mol-math/graph';
 import { Vec3 } from 'mol-math/linear-algebra';
 import PrincipalAxes from 'mol-math/linear-algebra/matrix/principal-axes';
 import { fillSerial } from 'mol-util/array';
@@ -54,7 +54,7 @@ function getSugarRingIndices(unit: Unit.Atomic) {
 }
 
 const C = ElementSymbol('C')
-function getDirection(direction: Vec3, unit: Unit.Atomic, indices: ReadonlyArray<StructureElement.UnitIndex>, center: Vec3) {
+function getDirection(direction: Vec3, unit: Unit.Atomic, indices: ArrayLike<StructureElement.UnitIndex>, center: Vec3) {
     let indexC1 = -1, indexC1X = -1, indexC = -1
     const { elements } = unit
     const { position } = unit.conformation
@@ -73,8 +73,8 @@ function getDirection(direction: Vec3, unit: Unit.Atomic, indices: ReadonlyArray
     }
     const index = indexC1 !== -1 ? indexC1
         : indexC1X !== -1 ? indexC1X
-        : indexC !== -1 ? indexC
-        : elements[indices[0]]
+            : indexC !== -1 ? indexC
+                : elements[indices[0]]
     Vec3.normalize(direction, Vec3.sub(direction, center, position(index, direction)))
     return direction
 }
@@ -163,19 +163,19 @@ export function computeCarbohydrates(structure: Structure): Carbohydrates {
                     ringElements.push(elementIndex)
                     elementsWithRingMap.set(elementKey(residueIndex, unit.id), elementIndex)
                     elements.push({
-                        geometry: { center, normal, direction, },
+                        geometry: { center, normal, direction },
                         hasRing: true,
                         unit, residueIndex, component: saccharideComp
                     })
                 }
 
                 // add carbohydrate links induced by intra-residue bonds
-                const ringCombinations = combinations(fillSerial(new Array(sugarRings.length)), 2)
+                const ringCombinations = combinations(fillSerial(new Array(sugarRings.length) as number[]), 2)
                 for (let j = 0, jl = ringCombinations.length; j < jl; ++j) {
                     const rc = ringCombinations[j];
                     const r0 = rings.all[sugarRings[rc[0]]], r1 = rings.all[sugarRings[rc[1]]];
-                    if (areConnected(r0, r1, unit.links, 2)) {
-                        // fix both directions as it is unlcear where the C1 atom is
+                    if (IntAdjacencyGraph.areVertexSetsConnected(unit.links, r0, r1, 2)) {
+                        // fix both directions as it is unclear where the C1 atom is
                         fixLinkDirection(ringElements[rc[0]], ringElements[rc[1]])
                         fixLinkDirection(ringElements[rc[1]], ringElements[rc[0]])
                         links.push({

+ 2 - 1
src/mol-model/structure/structure/unit/rings.ts

@@ -7,8 +7,9 @@
 import { computeRings, getFingerprint, createIndex } from './rings/compute'
 import Unit from '../unit';
 import StructureElement from '../element';
+import { SortedArray } from 'mol-data/int';
 
-type UnitRing = ReadonlyArray<StructureElement.UnitIndex>
+type UnitRing = SortedArray<StructureElement.UnitIndex>
 
 interface UnitRings {
     /** Each ring is specified as an array of indices in Unit.elements. */

+ 6 - 4
src/mol-model/structure/structure/unit/rings/compute.ts

@@ -4,12 +4,13 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { Segmentation } from 'mol-data/int';
+import { Segmentation, SortedArray } from 'mol-data/int';
 import { IntAdjacencyGraph } from 'mol-math/graph';
 import { LinkType } from '../../../model/types';
 import { StructureElement } from '../../../structure';
 import Unit from '../../unit';
 import { IntraUnitLinks } from '../links/data';
+import { sortArray } from 'mol-data/util';
 
 export function computeRings(unit: Unit.Atomic) {
     const size = largestResidue(unit);
@@ -42,7 +43,7 @@ interface State {
 
     currentColor: number,
 
-    rings: StructureElement.UnitIndex[][],
+    rings: SortedArray<StructureElement.UnitIndex>[],
     bonds: IntraUnitLinks,
     unit: Unit.Atomic
 }
@@ -146,7 +147,8 @@ function addRing(state: State, a: number, b: number) {
     for (let t = 0; t < leftOffset; t++) ring[ringOffset++] = state.startVertex + left[t];
     for (let t = rightOffset - 1; t >= 0; t--) ring[ringOffset++] = state.startVertex + right[t];
 
-    state.rings.push(ring as any as StructureElement.UnitIndex[]);
+    sortArray(ring);
+    state.rings.push(SortedArray.ofSortedArray(ring));
 }
 
 function findRings(state: State, from: number) {
@@ -247,7 +249,7 @@ function buildFinderprint(elements: string[], offset: number) {
 type RingIndex = import('../rings').UnitRings.Index
 type RingComponentIndex = import('../rings').UnitRings.ComponentIndex
 
-export function createIndex(rings: StructureElement.UnitIndex[][]) {
+export function createIndex(rings: SortedArray<StructureElement.UnitIndex>[]) {
     const elementRingIndices: Map<StructureElement.UnitIndex, RingIndex[]> = new Map();
 
     // for each ring atom, assign all rings that it is present in