From dd0ad070c819aa8ba316b487f04696c0ef88aa91 Mon Sep 17 00:00:00 2001 From: David Sehnal <david.sehnal@gmail.com> Date: Thu, 7 Jun 2018 13:16:23 +0200 Subject: [PATCH] Induce IntGraph subgraph --- src/helpers.d.ts | 1 + src/mol-data/int/_spec/sorted-array.spec.ts | 4 ++ src/mol-data/int/impl/sorted-array.ts | 35 +++++++++- src/mol-data/int/sorted-array.ts | 4 +- src/mol-data/util/array.ts | 24 +++++++ src/mol-math/graph.ts | 2 +- src/mol-math/graph/_spec/int-graph.spec.ts | 11 ++- .../graph/{int/graph.ts => int-graph.ts} | 67 +++++++++++++++---- .../structure/structure/unit/bonds/data.ts | 1 + .../structure/unit/bonds/inter-compute.ts | 1 + .../structure/unit/bonds/inter-data.ts | 1 + .../structure/structure/unit/rings/compute.ts | 2 +- 12 files changed, 135 insertions(+), 18 deletions(-) rename src/mol-math/graph/{int/graph.ts => int-graph.ts} (61%) create mode 100644 src/mol-model/structure/structure/unit/bonds/data.ts create mode 100644 src/mol-model/structure/structure/unit/bonds/inter-compute.ts create mode 100644 src/mol-model/structure/structure/unit/bonds/inter-data.ts diff --git a/src/helpers.d.ts b/src/helpers.d.ts index 3dbdd2dc7..69dca2592 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 2cd82af32..c5ca33cf5 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 4a5476c16..09b787726 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 928e383dd..2c92aa329 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 93707e797..94311c38c 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 ce0429662..f152b30c2 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 3d75dddb0..25319d118 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 fb50ee13f..f6576942c 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 000000000..0ffdd02fc --- /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 000000000..0ffdd02fc --- /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 000000000..0ffdd02fc --- /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 a76cd6e47..3a8359412 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; -- GitLab