From 6217a51fa554f13d84c93b76fa6af1cc96a2dc85 Mon Sep 17 00:00:00 2001 From: Alexander Rose <alex.rose@rcsb.org> Date: Mon, 29 Jul 2019 11:32:02 -0700 Subject: [PATCH] optimized `toScriptExpression` --- src/mol-model/structure/structure/element.ts | 103 ++++++++++++++----- 1 file changed, 75 insertions(+), 28 deletions(-) diff --git a/src/mol-model/structure/structure/element.ts b/src/mol-model/structure/structure/element.ts index 200f8b4c0..4b85cdc0f 100644 --- a/src/mol-model/structure/structure/element.ts +++ b/src/mol-model/structure/structure/element.ts @@ -16,7 +16,7 @@ import Structure from './structure'; import Unit from './unit'; import { Boundary } from './util/boundary'; import { StructureProperties } from '../structure'; -import { sortArray } from '../../../mol-data/util'; +import { sortArray, hashFnv32a, hash2 } from '../../../mol-data/util'; import Expression from '../../../mol-script/language/expression'; interface StructureElement<U = Unit> { @@ -358,21 +358,81 @@ namespace StructureElement { } } - const byOpName: Expression[] = []; + const opData: OpData[] = []; const keys = sourceIndexMap.keys(); while (true) { const k = keys.next(); if (k.done) break; const e = sourceIndexMap.get(k.value)!; - byOpName.push(getOpNameQuery(k.value, e.xs.array, models.length > 1, e.modelLabel, e.modelIndex)); + opData.push(getOpData(k.value, e.xs.array, models.length > 1, e.modelLabel, e.modelIndex)); } + const opGroups = new Map<string, OpData>(); + for (let i = 0, il = opData.length; i < il; ++i) { + const d = opData[i] + const hash = hash2(hashFnv32a(d.atom.ranges), hashFnv32a(d.atom.set)) + const key = `${hash}|${d.entity ? (d.entity.modelLabel + d.entity.modelIndex) : ''}` + if (opGroups.has(key)) { + opGroups.get(key)!.chain.opName.push(...d.chain.opName) + } else { + opGroups.set(key, d) + } + } + + const opQueries: Expression[] = []; + opGroups.forEach(d => { + const { ranges, set } = d.atom + const { opName } = d.chain + + const opProp = MS.struct.atomProperty.core.operatorName() + const siProp = MS.struct.atomProperty.core.sourceIndex(); + const tests: Expression[] = []; + + // TODO: add set.ofRanges constructor to MolQL??? + if (set.length > 0) { + tests[tests.length] = MS.core.set.has([MS.set.apply(null, set), siProp]); + } + for (let rI = 0, _rI = ranges.length / 2; rI < _rI; rI++) { + tests[tests.length] = MS.core.rel.inRange([siProp, ranges[2 * rI], ranges[2 * rI + 1]]); + } + + if (d.entity) { + const { modelLabel, modelIndex } = d.entity + opQueries.push(MS.struct.generator.atomGroups({ + 'atom-test': tests.length > 1 ? MS.core.logic.or(tests) : tests[0], + 'chain-test': opName.length > 1 + ? MS.core.set.has([MS.set.apply(null, opName), opProp]) + : MS.core.rel.eq([opProp, opName[0]]), + 'entity-test': MS.core.logic.and([ + MS.core.rel.eq([MS.struct.atomProperty.core.modelLabel(), modelLabel]), + MS.core.rel.eq([MS.struct.atomProperty.core.modelIndex(), modelIndex]), + ]) + })) + } else { + opQueries.push(MS.struct.generator.atomGroups({ + 'atom-test': tests.length > 1 ? MS.core.logic.or(tests) : tests[0], + 'chain-test': opName.length > 1 + ? MS.core.set.has([MS.set.apply(null, opName), opProp]) + : MS.core.rel.eq([opProp, opName[0]]) + })) + } + }) + return MS.struct.modifier.union([ - byOpName.length === 1 ? byOpName[0] : MS.struct.combinator.merge(byOpName) + opQueries.length === 1 + ? opQueries[0] + // Need to union before merge for fast performance + : MS.struct.combinator.merge(opQueries.map(q => MS.struct.modifier.union([ q ]))) ]); } - function getOpNameQuery(opName: string, xs: number[], multimodel: boolean, modelLabel: string, modelIndex: number) { + type OpData = { + atom: { set: number[], ranges: number[] }, + chain: { opName: string[] }, + entity?: { modelLabel: string, modelIndex: number } + } + + function getOpData(opName: string, xs: number[], multimodel: boolean, modelLabel: string, modelIndex: number): OpData { sortArray(xs); const ranges: number[] = []; @@ -395,30 +455,17 @@ namespace StructureElement { } } - const siProp = MS.struct.atomProperty.core.sourceIndex(); - const tests: Expression[] = []; - - // TODO: add set.ofRanges constructor to MolQL??? - if (set.length > 0) { - tests[tests.length] = MS.core.set.has([MS.set.apply(null, set), siProp]); - } - for (let rI = 0, _rI = ranges.length / 2; rI < _rI; rI++) { - tests[tests.length] = MS.core.rel.inRange([siProp, ranges[2 * rI], ranges[2 * rI + 1]]); - } - return multimodel - ? MS.struct.generator.atomGroups({ - 'atom-test': tests.length > 1 ? MS.core.logic.or(tests) : tests[0], - 'chain-test': MS.core.rel.eq([MS.struct.atomProperty.core.operatorName(), opName]), - 'entity-test': MS.core.logic.and([ - MS.core.rel.eq([MS.struct.atomProperty.core.modelLabel(), modelLabel]), - MS.core.rel.eq([MS.struct.atomProperty.core.modelIndex(), modelIndex]), - ]) - }) - : MS.struct.generator.atomGroups({ - 'atom-test': tests.length > 1 ? MS.core.logic.or(tests) : tests[0], - 'chain-test': MS.core.rel.eq([MS.struct.atomProperty.core.operatorName(), opName]) - }); + ? { + atom: { set, ranges }, + chain: { opName: [ opName ] }, + entity: { modelLabel, modelIndex } + } + : { + atom: { set, ranges }, + chain: { opName: [ opName ] }, + } + } } } -- GitLab