diff --git a/src/helpers.d.ts b/src/helpers.d.ts
index 3dbdd2dc79bbbf6f3878bba7ea672785785c6490..69dca25924d4e2fc6dd927672674506e1ad935ff 100644
--- a/src/helpers.d.ts
+++ b/src/helpers.d.ts
@@ -13,4 +13,5 @@ declare module Helpers {
     export type NumberArray = TypedArray | number[]
     export type UintArray = Uint8Array | Uint16Array | Uint32Array | number[]
     export type ValueOf<T> = T[keyof T]
+    export type ArrayCtor<T> = { new(size: number): { [i: number]: T, length: number } }
 }
\ No newline at end of file
diff --git a/src/mol-data/int/_spec/sorted-array.spec.ts b/src/mol-data/int/_spec/sorted-array.spec.ts
index 2cd82af32ce9046bb8c2b7a3a71779ea68084071..c5ca33cf506892bc90d9da503a241d0fc0f2bfe4 100644
--- a/src/mol-data/int/_spec/sorted-array.spec.ts
+++ b/src/mol-data/int/_spec/sorted-array.spec.ts
@@ -59,6 +59,10 @@ describe('sortedArray', () => {
         compareArrays(SortedArray.deduplicate(SortedArray.ofSortedArray([1, 2, 3])), [1, 2, 3]);
     });
 
+    it('indicesOf', () => {
+        compareArrays(SortedArray.indicesOf(SortedArray.ofSortedArray([10, 11, 12]), SortedArray.ofSortedArray([10, 12, 14])), [0, 2]);
+    })
+
     // console.log(Interval.findPredecessorIndexInInterval(Interval.ofBounds(0, 3), 2, Interval.ofBounds(0, 3)))
     // console.log(SortedArray.findPredecessorIndexInInterval(SortedArray.ofSortedArray([0, 1, 2]), 2, Interval.ofBounds(0, 3)))
 });
\ No newline at end of file
diff --git a/src/mol-data/int/impl/sorted-array.ts b/src/mol-data/int/impl/sorted-array.ts
index 4a5476c16c46de1194639194442ba6b904339a81..09b787726d0f33f11fe4422525c18c56948e2ce2 100644
--- a/src/mol-data/int/impl/sorted-array.ts
+++ b/src/mol-data/int/impl/sorted-array.ts
@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { sortArray, hash3, hash4 } from '../../util'
+import { sortArray, hash3, hash4, createRangeArray } from '../../util'
 import Interval from '../interval'
 
 type Nums = ArrayLike<number>
@@ -289,6 +289,39 @@ export function deduplicate(xs: Nums) {
     return ret;
 }
 
+export function indicesOf(a: Nums, b: Nums): Nums {
+    if (a === b) return ofSortedArray(createRangeArray(0, a.length - 1));
+
+    const { startI: sI, startJ: sJ, endI, endJ } = getSuitableIntersectionRange(a, b);
+    let i = sI, j = sJ;
+    let commonCount = 0;
+    while (i < endI && j < endJ) {
+        const x = a[i], y = b[j];
+        if (x < y) { i++; }
+        else if (x > y) { j++; }
+        else { i++; j++; commonCount++; }
+    }
+
+    const lenA = a.length;
+    // no common elements
+    if (!commonCount) return Empty;
+    // A is subset of B ==> A
+    if (commonCount === lenA) return ofSortedArray(createRangeArray(0, a.length - 1));
+
+    const indices = new Int32Array(commonCount);
+    let offset = 0;
+    i = sI;
+    j = sJ;
+    while (i < endI && j < endJ) {
+        const x = a[i], y = b[j];
+        if (x < y) { i++; }
+        else if (x > y) { j++; }
+        else { indices[offset++] = i; i++; j++; }
+    }
+
+    return ofSortedArray(indices);
+}
+
 const _maxIntRangeRet = { startI: 0, startJ: 0, endI: 0, endJ: 0 };
 // for small sets, just gets the whole range, for large sets does a bunch of binary searches
 function getSuitableIntersectionRange(a: Nums, b: Nums) {
diff --git a/src/mol-data/int/sorted-array.ts b/src/mol-data/int/sorted-array.ts
index 928e383ddf1a083f9a652428131074e4d5a8c735..2c92aa329d3dc16ebd1e1bdfd977b11426d9db09 100644
--- a/src/mol-data/int/sorted-array.ts
+++ b/src/mol-data/int/sorted-array.ts
@@ -41,7 +41,9 @@ namespace SortedArray {
     export const findPredecessorIndexInInterval: (array: SortedArray, x: number, bounds: Interval) => number = Impl.findPredecessorIndexInInterval as any;
     export const findRange: (array: SortedArray, min: number, max: number) => Interval = Impl.findRange as any;
 
-    export const deduplicate: (arrat: SortedArray) => SortedArray = Impl.deduplicate as any;
+    export const deduplicate: (array: SortedArray) => SortedArray = Impl.deduplicate as any;
+    /** Returns indices of xs in the array. E.g. indicesOf([10, 11, 12], [10, 12]) ==> [0, 2] */
+    export const indicesOf: (array: SortedArray, xs: SortedArray) => SortedArray = Impl.indicesOf as any;
 }
 
 interface SortedArray extends ArrayLike<number> { '@type': 'int-sorted-array' }
diff --git a/src/mol-data/util/array.ts b/src/mol-data/util/array.ts
index 93707e7978d657144d2b487a4c665c5f8eaf0198..94311c38c2ad057768411f1c65c9762244c341e5 100644
--- a/src/mol-data/util/array.ts
+++ b/src/mol-data/util/array.ts
@@ -22,4 +22,28 @@ export function iterableToArray<T>(it: IterableIterator<T>): T[] {
         ret[ret.length] = value;
     }
     return ret;
+}
+
+/** Fills the array so that array[0] = start and array[array.length - 1] = end */
+export function createRangeArray(start: number, end: number, ctor?: Helpers.ArrayCtor<number>) {
+    const len = end - start + 1;
+    const array = ctor ? new ctor(len) : new Int32Array(len);
+    for (let i = 0; i < len; i++) {
+        array[i] = i + start;
+    }
+    return array;
+}
+
+export function arrayPickIndices<T>(array: ArrayLike<T>, indices: ArrayLike<number>) {
+    const ret = new (arrayGetCtor(array))(indices.length);
+    for (let i = 0, _i = indices.length; i < _i; i++) {
+        ret[i] = array[indices[i]];
+    }
+    return ret;
+}
+
+export function arrayGetCtor<T>(data: ArrayLike<T>): Helpers.ArrayCtor<T> {
+    const ret = (data as any).constructor;
+    if (!ret) throw new Error('data does not define a constructor and it should');
+    return ret;
 }
\ No newline at end of file
diff --git a/src/mol-math/graph.ts b/src/mol-math/graph.ts
index ce0429662fe04ce07a6a24a56e8a25c188bb4cf1..f152b30c290df2178546c272a5008bb6f9ce65ae 100644
--- a/src/mol-math/graph.ts
+++ b/src/mol-math/graph.ts
@@ -4,4 +4,4 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-export * from './graph/int/graph'
\ No newline at end of file
+export * from './graph/int-graph'
\ No newline at end of file
diff --git a/src/mol-math/graph/_spec/int-graph.spec.ts b/src/mol-math/graph/_spec/int-graph.spec.ts
index 3d75dddb0296d749b60261249cfd666ddb1d8a35..25319d11850f360bc3420b0dfc1ff6c9ecdff965 100644
--- a/src/mol-math/graph/_spec/int-graph.spec.ts
+++ b/src/mol-math/graph/_spec/int-graph.spec.ts
@@ -4,7 +4,7 @@
  * @author David Sehnal <david.sehnal@gmail.com>
  */
 
-import { IntGraph } from '../int/graph';
+import { IntGraph } from '../int-graph';
 
 describe('IntGraph', () => {
     const vc = 3;
@@ -28,9 +28,16 @@ describe('IntGraph', () => {
     });
 
     it('triangle-propAndEdgeIndex', () => {
-        const prop = graph.prop;
+        const prop = graph.edgeProps.prop;
         expect(prop[graph.getEdgeIndex(0, 1)]).toBe(10);
         expect(prop[graph.getEdgeIndex(1, 2)]).toBe(11);
         expect(prop[graph.getEdgeIndex(2, 0)]).toBe(12);
     });
+
+    it('induce', () => {
+        const induced = IntGraph.induceByVertices(graph, [1, 2]);
+        expect(induced.vertexCount).toBe(2);
+        expect(induced.edgeCount).toBe(1);
+        expect(induced.edgeProps.prop[induced.getEdgeIndex(0, 1)]).toBe(11);
+    })
 });
\ No newline at end of file
diff --git a/src/mol-math/graph/int/graph.ts b/src/mol-math/graph/int-graph.ts
similarity index 61%
rename from src/mol-math/graph/int/graph.ts
rename to src/mol-math/graph/int-graph.ts
index fb50ee13f123ed9deec41270e3e81b5923878f56..f6576942cee2afd79ec9315a9e55ade52a301780 100644
--- a/src/mol-math/graph/int/graph.ts
+++ b/src/mol-math/graph/int-graph.ts
@@ -1,3 +1,5 @@
+import { arrayPickIndices } from 'mol-data/util';
+
 /**
  * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
  *
@@ -12,12 +14,13 @@
  *
  * Edge properties are indexed same as in the arrays a and b.
  */
-type IntGraph<EdgeProperties extends object = { }> = {
+interface IntGraph<EdgeProps extends IntGraph.EdgePropsBase = {}> {
     readonly offset: ArrayLike<number>,
     readonly a: ArrayLike<number>,
     readonly b: ArrayLike<number>,
     readonly vertexCount: number,
     readonly edgeCount: number,
+    readonly edgeProps: Readonly<EdgeProps>
 
     /**
      * Get the edge index between i-th and j-th vertex.
@@ -28,11 +31,14 @@ type IntGraph<EdgeProperties extends object = { }> = {
      */
     getEdgeIndex(i: number, j: number): number,
     getVertexEdgeCount(i: number): number
-} & EdgeProperties
+}
 
 namespace IntGraph {
-    class Impl implements IntGraph<any> {
+    export type EdgePropsBase = { [name: string]: ArrayLike<any> }
+
+    class IntGraphImpl implements IntGraph<any> {
         readonly vertexCount: number;
+        readonly edgeProps: object;
 
         getEdgeIndex(i: number, j: number): number {
             let a, b;
@@ -48,18 +54,14 @@ namespace IntGraph {
             return this.offset[i + 1] - this.offset[i];
         }
 
-        constructor(public offset: ArrayLike<number>, public a: ArrayLike<number>, public b: ArrayLike<number>, public edgeCount: number, props?: any) {
+        constructor(public offset: ArrayLike<number>, public a: ArrayLike<number>, public b: ArrayLike<number>, public edgeCount: number, edgeProps?: any) {
             this.vertexCount = offset.length - 1;
-            if (props) {
-                for (const p of Object.keys(props)) {
-                    (this as any)[p] = props[p];
-                }
-            }
+            this.edgeProps = edgeProps || {};
         }
     }
 
-    export function create<EdgeProps extends object = { }>(offset: ArrayLike<number>, a: ArrayLike<number>, b: ArrayLike<number>, edgeCount: number, edgeProps?: EdgeProps): IntGraph<EdgeProps> {
-        return new Impl(offset, a, b, edgeCount, edgeProps) as IntGraph<EdgeProps>;
+    export function create<EdgeProps extends IntGraph.EdgePropsBase = {}>(offset: ArrayLike<number>, a: ArrayLike<number>, b: ArrayLike<number>, edgeCount: number, edgeProps?: EdgeProps): IntGraph<EdgeProps> {
+        return new IntGraphImpl(offset, a, b, edgeCount, edgeProps) as IntGraph<EdgeProps>;
     }
 
     export class EdgeBuilder {
@@ -75,7 +77,7 @@ namespace IntGraph {
         a: Int32Array;
         b: Int32Array;
 
-        createGraph<EdgeProps extends object = { }>(edgeProps?: EdgeProps) {
+        createGraph<EdgeProps extends IntGraph.EdgePropsBase = {}>(edgeProps?: EdgeProps) {
             return create(this.offsets, this.a, this.b, this.edgeCount, edgeProps);
         }
 
@@ -132,6 +134,47 @@ namespace IntGraph {
             this.b = new Int32Array(offset);
         }
     }
+
+    export function induceByVertices<P extends IntGraph.EdgePropsBase>(graph: IntGraph<P>, vertexIndices: ArrayLike<number>): IntGraph<P> {
+        const { b, offset, vertexCount, edgeProps } = graph;
+        const vertexMap = new Int32Array(vertexCount);
+        for (let i = 0, _i = vertexIndices.length; i < _i; i++) vertexMap[vertexIndices[i]] = i + 1;
+
+        let newEdgeCount = 0;
+        for (let i = 0; i < vertexCount; i++) {
+            if (vertexMap[i] === 0) continue;
+            for (let j = offset[i], _j = offset[i + 1]; j < _j; j++) {
+                if (b[j] > i && vertexMap[b[j]] !== 0) newEdgeCount++;
+            }
+        }
+
+        const newOffsets = new Int32Array(vertexIndices.length + 1);
+        const edgeIndices = new Int32Array(2 * newEdgeCount);
+        const newA = new Int32Array(2 * newEdgeCount);
+        const newB = new Int32Array(2 * newEdgeCount);
+        let eo = 0, vo = 0;
+        for (let i = 0; i < vertexCount; i++) {
+            if (vertexMap[i] === 0) continue;
+            const aa = vertexMap[i] - 1;
+            for (let j = offset[i], _j = offset[i + 1]; j < _j; j++) {
+                const bb = vertexMap[b[j]];
+                if (bb === 0) continue;
+
+                newA[eo] = aa;
+                newB[eo] = bb - 1;
+                edgeIndices[eo] = j;
+                eo++;
+            }
+            newOffsets[++vo] = eo;
+        }
+
+        const newEdgeProps: P = {} as any;
+        for (const key of Object.keys(edgeProps)) {
+            newEdgeProps[key] = arrayPickIndices(edgeProps[key], edgeIndices);
+        }
+
+        return create(newOffsets, newA, newB, newEdgeCount, newEdgeProps);
+    }
 }
 
 export { IntGraph }
\ No newline at end of file
diff --git a/src/mol-model/structure/structure/unit/bonds/data.ts b/src/mol-model/structure/structure/unit/bonds/data.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0ffdd02fcbce683e436c0030ffe0517135c6ceda
--- /dev/null
+++ b/src/mol-model/structure/structure/unit/bonds/data.ts
@@ -0,0 +1 @@
+// TODO
\ No newline at end of file
diff --git a/src/mol-model/structure/structure/unit/bonds/inter-compute.ts b/src/mol-model/structure/structure/unit/bonds/inter-compute.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0ffdd02fcbce683e436c0030ffe0517135c6ceda
--- /dev/null
+++ b/src/mol-model/structure/structure/unit/bonds/inter-compute.ts
@@ -0,0 +1 @@
+// TODO
\ No newline at end of file
diff --git a/src/mol-model/structure/structure/unit/bonds/inter-data.ts b/src/mol-model/structure/structure/unit/bonds/inter-data.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0ffdd02fcbce683e436c0030ffe0517135c6ceda
--- /dev/null
+++ b/src/mol-model/structure/structure/unit/bonds/inter-data.ts
@@ -0,0 +1 @@
+// TODO
\ No newline at end of file
diff --git a/src/mol-model/structure/structure/unit/rings/compute.ts b/src/mol-model/structure/structure/unit/rings/compute.ts
index a76cd6e47c29b67c4d57d2a7565f91a6cd3f2b76..3a83594121a8150912f1a6daabba7278767008e2 100644
--- a/src/mol-model/structure/structure/unit/rings/compute.ts
+++ b/src/mol-model/structure/structure/unit/rings/compute.ts
@@ -148,7 +148,7 @@ function addRing(state: State, a: number, b: number) {
 
 function findRings(state: State, from: number) {
     const { bonds, startVertex, endVertex, visited, queue, pred } = state;
-    const { b: neighbor, flags: bondFlags, offset } = bonds;
+    const { b: neighbor, edgeProps: { flags: bondFlags }, offset } = bonds;
     visited[from] = 1;
     queue[0] = from;
     let head = 0, size = 1;