Skip to content
Snippets Groups Projects
Commit dd0ad070 authored by David Sehnal's avatar David Sehnal
Browse files

Induce IntGraph subgraph

parent 2a3f2e99
Branches
Tags
No related merge requests found
...@@ -13,4 +13,5 @@ declare module Helpers { ...@@ -13,4 +13,5 @@ declare module Helpers {
export type NumberArray = TypedArray | number[] export type NumberArray = TypedArray | number[]
export type UintArray = Uint8Array | Uint16Array | Uint32Array | number[] export type UintArray = Uint8Array | Uint16Array | Uint32Array | number[]
export type ValueOf<T> = T[keyof T] 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
...@@ -59,6 +59,10 @@ describe('sortedArray', () => { ...@@ -59,6 +59,10 @@ describe('sortedArray', () => {
compareArrays(SortedArray.deduplicate(SortedArray.ofSortedArray([1, 2, 3])), [1, 2, 3]); 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(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))) // console.log(SortedArray.findPredecessorIndexInInterval(SortedArray.ofSortedArray([0, 1, 2]), 2, Interval.ofBounds(0, 3)))
}); });
\ No newline at end of file
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
import { sortArray, hash3, hash4 } from '../../util' import { sortArray, hash3, hash4, createRangeArray } from '../../util'
import Interval from '../interval' import Interval from '../interval'
type Nums = ArrayLike<number> type Nums = ArrayLike<number>
...@@ -289,6 +289,39 @@ export function deduplicate(xs: Nums) { ...@@ -289,6 +289,39 @@ export function deduplicate(xs: Nums) {
return ret; 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 }; 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 // for small sets, just gets the whole range, for large sets does a bunch of binary searches
function getSuitableIntersectionRange(a: Nums, b: Nums) { function getSuitableIntersectionRange(a: Nums, b: Nums) {
......
...@@ -41,7 +41,9 @@ namespace SortedArray { ...@@ -41,7 +41,9 @@ namespace SortedArray {
export const findPredecessorIndexInInterval: (array: SortedArray, x: number, bounds: Interval) => number = Impl.findPredecessorIndexInInterval as any; 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 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' } interface SortedArray extends ArrayLike<number> { '@type': 'int-sorted-array' }
......
...@@ -22,4 +22,28 @@ export function iterableToArray<T>(it: IterableIterator<T>): T[] { ...@@ -22,4 +22,28 @@ export function iterableToArray<T>(it: IterableIterator<T>): T[] {
ret[ret.length] = value; ret[ret.length] = value;
} }
return ret; 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
...@@ -4,4 +4,4 @@ ...@@ -4,4 +4,4 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
export * from './graph/int/graph' export * from './graph/int-graph'
\ No newline at end of file \ No newline at end of file
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
* @author David Sehnal <david.sehnal@gmail.com> * @author David Sehnal <david.sehnal@gmail.com>
*/ */
import { IntGraph } from '../int/graph'; import { IntGraph } from '../int-graph';
describe('IntGraph', () => { describe('IntGraph', () => {
const vc = 3; const vc = 3;
...@@ -28,9 +28,16 @@ describe('IntGraph', () => { ...@@ -28,9 +28,16 @@ describe('IntGraph', () => {
}); });
it('triangle-propAndEdgeIndex', () => { it('triangle-propAndEdgeIndex', () => {
const prop = graph.prop; const prop = graph.edgeProps.prop;
expect(prop[graph.getEdgeIndex(0, 1)]).toBe(10); expect(prop[graph.getEdgeIndex(0, 1)]).toBe(10);
expect(prop[graph.getEdgeIndex(1, 2)]).toBe(11); expect(prop[graph.getEdgeIndex(1, 2)]).toBe(11);
expect(prop[graph.getEdgeIndex(2, 0)]).toBe(12); 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
import { arrayPickIndices } from 'mol-data/util';
/** /**
* Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info. * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
* *
...@@ -12,12 +14,13 @@ ...@@ -12,12 +14,13 @@
* *
* Edge properties are indexed same as in the arrays a and b. * 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 offset: ArrayLike<number>,
readonly a: ArrayLike<number>, readonly a: ArrayLike<number>,
readonly b: ArrayLike<number>, readonly b: ArrayLike<number>,
readonly vertexCount: number, readonly vertexCount: number,
readonly edgeCount: number, readonly edgeCount: number,
readonly edgeProps: Readonly<EdgeProps>
/** /**
* Get the edge index between i-th and j-th vertex. * Get the edge index between i-th and j-th vertex.
...@@ -28,11 +31,14 @@ type IntGraph<EdgeProperties extends object = { }> = { ...@@ -28,11 +31,14 @@ type IntGraph<EdgeProperties extends object = { }> = {
*/ */
getEdgeIndex(i: number, j: number): number, getEdgeIndex(i: number, j: number): number,
getVertexEdgeCount(i: number): number getVertexEdgeCount(i: number): number
} & EdgeProperties }
namespace IntGraph { namespace IntGraph {
class Impl implements IntGraph<any> { export type EdgePropsBase = { [name: string]: ArrayLike<any> }
class IntGraphImpl implements IntGraph<any> {
readonly vertexCount: number; readonly vertexCount: number;
readonly edgeProps: object;
getEdgeIndex(i: number, j: number): number { getEdgeIndex(i: number, j: number): number {
let a, b; let a, b;
...@@ -48,18 +54,14 @@ namespace IntGraph { ...@@ -48,18 +54,14 @@ namespace IntGraph {
return this.offset[i + 1] - this.offset[i]; 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; this.vertexCount = offset.length - 1;
if (props) { this.edgeProps = edgeProps || {};
for (const p of Object.keys(props)) {
(this as any)[p] = props[p];
}
}
} }
} }
export function create<EdgeProps extends object = { }>(offset: ArrayLike<number>, a: ArrayLike<number>, b: ArrayLike<number>, edgeCount: number, edgeProps?: EdgeProps): 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 Impl(offset, a, b, edgeCount, edgeProps) as IntGraph<EdgeProps>; return new IntGraphImpl(offset, a, b, edgeCount, edgeProps) as IntGraph<EdgeProps>;
} }
export class EdgeBuilder { export class EdgeBuilder {
...@@ -75,7 +77,7 @@ namespace IntGraph { ...@@ -75,7 +77,7 @@ namespace IntGraph {
a: Int32Array; a: Int32Array;
b: 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); return create(this.offsets, this.a, this.b, this.edgeCount, edgeProps);
} }
...@@ -132,6 +134,47 @@ namespace IntGraph { ...@@ -132,6 +134,47 @@ namespace IntGraph {
this.b = new Int32Array(offset); 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 } export { IntGraph }
\ No newline at end of file
// TODO
\ No newline at end of file
// TODO
\ No newline at end of file
// TODO
\ No newline at end of file
...@@ -148,7 +148,7 @@ function addRing(state: State, a: number, b: number) { ...@@ -148,7 +148,7 @@ function addRing(state: State, a: number, b: number) {
function findRings(state: State, from: number) { function findRings(state: State, from: number) {
const { bonds, startVertex, endVertex, visited, queue, pred } = state; 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; visited[from] = 1;
queue[0] = from; queue[0] = from;
let head = 0, size = 1; let head = 0, size = 1;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment