@gmod/gbz-base 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +25 -0
- package/README.md +125 -0
- package/bin/query.js +7 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +140 -0
- package/dist/db.d.ts +72 -0
- package/dist/db.js +208 -0
- package/dist/filehandle.d.ts +6 -0
- package/dist/filehandle.js +1 -0
- package/dist/gbwt/bytecode.d.ts +20 -0
- package/dist/gbwt/bytecode.js +70 -0
- package/dist/gbwt/node.d.ts +15 -0
- package/dist/gbwt/node.js +44 -0
- package/dist/gbwt/record.d.ts +18 -0
- package/dist/gbwt/record.js +133 -0
- package/dist/gbwt/sequence.d.ts +3 -0
- package/dist/gbwt/sequence.js +40 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +6 -0
- package/dist/lcs.d.ts +1 -0
- package/dist/lcs.js +214 -0
- package/dist/query.d.ts +16 -0
- package/dist/query.js +37 -0
- package/dist/sqlite/btree.d.ts +18 -0
- package/dist/sqlite/btree.js +201 -0
- package/dist/sqlite/database.d.ts +29 -0
- package/dist/sqlite/database.js +71 -0
- package/dist/sqlite/pager.d.ts +19 -0
- package/dist/sqlite/pager.js +55 -0
- package/dist/sqlite/record.d.ts +3 -0
- package/dist/sqlite/record.js +85 -0
- package/dist/subgraph.d.ts +122 -0
- package/dist/subgraph.js +760 -0
- package/package.json +53 -0
- package/tools/haplotype-index/Cargo.toml +15 -0
- package/tools/haplotype-index/src/main.rs +319 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const ENDMARKER = 0;
|
|
2
|
+
export type Orientation = 'forward' | 'reverse';
|
|
3
|
+
export declare function encodeNode(id: number, orientation: Orientation): number;
|
|
4
|
+
export declare function nodeId(handle: number): number;
|
|
5
|
+
export declare function nodeOrientation(handle: number): Orientation;
|
|
6
|
+
export declare function isReverse(handle: number): boolean;
|
|
7
|
+
export declare function flipNode(handle: number): number;
|
|
8
|
+
export type NodeSide = 'left' | 'right';
|
|
9
|
+
export declare function flipSide(side: NodeSide): NodeSide;
|
|
10
|
+
export declare function entrySide(orientation: Orientation): NodeSide;
|
|
11
|
+
export declare function exitSide(orientation: Orientation): NodeSide;
|
|
12
|
+
export declare function entryOrientation(side: NodeSide): Orientation;
|
|
13
|
+
export declare function exitOrientation(side: NodeSide): Orientation;
|
|
14
|
+
export declare function edgeIsCanonical(from: number, to: number): boolean;
|
|
15
|
+
export declare function pathIsCanonical(path: number[]): boolean;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const ENDMARKER = 0;
|
|
2
|
+
export function encodeNode(id, orientation) {
|
|
3
|
+
return 2 * id + (orientation === 'reverse' ? 1 : 0);
|
|
4
|
+
}
|
|
5
|
+
export function nodeId(handle) {
|
|
6
|
+
return Math.floor(handle / 2);
|
|
7
|
+
}
|
|
8
|
+
export function nodeOrientation(handle) {
|
|
9
|
+
return handle % 2 === 0 ? 'forward' : 'reverse';
|
|
10
|
+
}
|
|
11
|
+
export function isReverse(handle) {
|
|
12
|
+
return handle % 2 === 1;
|
|
13
|
+
}
|
|
14
|
+
export function flipNode(handle) {
|
|
15
|
+
return handle % 2 === 0 ? handle + 1 : handle - 1;
|
|
16
|
+
}
|
|
17
|
+
export function flipSide(side) {
|
|
18
|
+
return side === 'left' ? 'right' : 'left';
|
|
19
|
+
}
|
|
20
|
+
export function entrySide(orientation) {
|
|
21
|
+
return orientation === 'forward' ? 'left' : 'right';
|
|
22
|
+
}
|
|
23
|
+
export function exitSide(orientation) {
|
|
24
|
+
return orientation === 'forward' ? 'right' : 'left';
|
|
25
|
+
}
|
|
26
|
+
export function entryOrientation(side) {
|
|
27
|
+
return side === 'left' ? 'forward' : 'reverse';
|
|
28
|
+
}
|
|
29
|
+
export function exitOrientation(side) {
|
|
30
|
+
return side === 'right' ? 'forward' : 'reverse';
|
|
31
|
+
}
|
|
32
|
+
export function edgeIsCanonical(from, to) {
|
|
33
|
+
const fromId = nodeId(from);
|
|
34
|
+
const toId = nodeId(to);
|
|
35
|
+
return isReverse(from) ? toId > fromId || (toId === fromId && !isReverse(to)) : toId >= fromId;
|
|
36
|
+
}
|
|
37
|
+
export function pathIsCanonical(path) {
|
|
38
|
+
const first = path[0];
|
|
39
|
+
const last = path[path.length - 1];
|
|
40
|
+
if (first === undefined || last === undefined) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
return isReverse(first) === isReverse(last) ? !isReverse(first) : edgeIsCanonical(first, last);
|
|
44
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { RunReader } from './bytecode.ts';
|
|
2
|
+
export interface Pos {
|
|
3
|
+
node: number;
|
|
4
|
+
offset: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function decompressEdges(bytes: Uint8Array): Pos[];
|
|
7
|
+
export declare class GbwtRecord {
|
|
8
|
+
readonly edges: Pos[];
|
|
9
|
+
readonly bwt: Uint8Array;
|
|
10
|
+
constructor(edges: Pos[], bwt: Uint8Array);
|
|
11
|
+
runs(): RunReader;
|
|
12
|
+
private edge;
|
|
13
|
+
lf(i: number): Pos | undefined;
|
|
14
|
+
private edgeTo;
|
|
15
|
+
offsetTo(pos: Pos): number | undefined;
|
|
16
|
+
predecessorAt(i: number): number | undefined;
|
|
17
|
+
decompress(): Pos[];
|
|
18
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { ByteCodeReader, RunReader } from "./bytecode.js";
|
|
2
|
+
import { ENDMARKER, flipNode, nodeId } from "./node.js";
|
|
3
|
+
export function decompressEdges(bytes) {
|
|
4
|
+
const reader = new ByteCodeReader(bytes);
|
|
5
|
+
const sigma = reader.int();
|
|
6
|
+
if (sigma === undefined || sigma === 0) {
|
|
7
|
+
return [];
|
|
8
|
+
}
|
|
9
|
+
const edges = [];
|
|
10
|
+
let prev = 0;
|
|
11
|
+
for (let i = 0; i < sigma; i++) {
|
|
12
|
+
const delta = reader.int();
|
|
13
|
+
const offset = reader.int();
|
|
14
|
+
if (delta === undefined || offset === undefined) {
|
|
15
|
+
throw new Error('GBWT edge list ends early');
|
|
16
|
+
}
|
|
17
|
+
prev += delta;
|
|
18
|
+
edges.push({ node: prev, offset });
|
|
19
|
+
}
|
|
20
|
+
return edges;
|
|
21
|
+
}
|
|
22
|
+
export class GbwtRecord {
|
|
23
|
+
edges;
|
|
24
|
+
bwt;
|
|
25
|
+
constructor(edges, bwt) {
|
|
26
|
+
this.edges = edges;
|
|
27
|
+
this.bwt = bwt;
|
|
28
|
+
}
|
|
29
|
+
runs() {
|
|
30
|
+
return new RunReader(this.bwt, this.edges.length);
|
|
31
|
+
}
|
|
32
|
+
edge(rank) {
|
|
33
|
+
const edge = this.edges[rank];
|
|
34
|
+
if (edge === undefined) {
|
|
35
|
+
throw new Error(`GBWT run refers to edge rank ${rank} of ${this.edges.length}`);
|
|
36
|
+
}
|
|
37
|
+
return edge;
|
|
38
|
+
}
|
|
39
|
+
lf(i) {
|
|
40
|
+
const offsets = this.edges.map(e => e.offset);
|
|
41
|
+
let offset = 0;
|
|
42
|
+
for (const run of this.runs()) {
|
|
43
|
+
const edge = this.edge(run.value);
|
|
44
|
+
const soFar = offsets[run.value];
|
|
45
|
+
if (offset + run.len > i) {
|
|
46
|
+
return edge.node === ENDMARKER ? undefined : { node: edge.node, offset: soFar + (i - offset) };
|
|
47
|
+
}
|
|
48
|
+
offsets[run.value] = soFar + run.len;
|
|
49
|
+
offset += run.len;
|
|
50
|
+
}
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
edgeTo(node) {
|
|
54
|
+
let low = 0;
|
|
55
|
+
let high = this.edges.length;
|
|
56
|
+
while (low < high) {
|
|
57
|
+
const mid = (low + high) >> 1;
|
|
58
|
+
const edge = this.edges[mid];
|
|
59
|
+
if (edge.node === node) {
|
|
60
|
+
return mid;
|
|
61
|
+
}
|
|
62
|
+
if (edge.node < node) {
|
|
63
|
+
low = mid + 1;
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
high = mid;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
offsetTo(pos) {
|
|
72
|
+
if (pos.node === ENDMARKER) {
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
const rank = this.edgeTo(pos.node);
|
|
76
|
+
if (rank === undefined) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
let succRank = this.edges[rank].offset;
|
|
80
|
+
if (succRank > pos.offset) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
let offset = 0;
|
|
84
|
+
for (const run of this.runs()) {
|
|
85
|
+
offset += run.len;
|
|
86
|
+
if (run.value !== rank) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
succRank += run.len;
|
|
90
|
+
if (succRank > pos.offset) {
|
|
91
|
+
return offset - (succRank - pos.offset);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
predecessorAt(i) {
|
|
97
|
+
const counts = this.edges.map(e => ({ node: e.node === ENDMARKER ? ENDMARKER : flipNode(e.node), count: 0 }));
|
|
98
|
+
for (const run of this.runs()) {
|
|
99
|
+
const entry = counts[run.value];
|
|
100
|
+
if (entry) {
|
|
101
|
+
entry.count += run.len;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
for (let rank = 1; rank < counts.length; rank++) {
|
|
105
|
+
const prev = counts[rank - 1];
|
|
106
|
+
const curr = counts[rank];
|
|
107
|
+
if (nodeId(prev.node) === nodeId(curr.node)) {
|
|
108
|
+
counts[rank - 1] = curr;
|
|
109
|
+
counts[rank] = prev;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
let offset = 0;
|
|
113
|
+
for (const entry of counts) {
|
|
114
|
+
offset += entry.count;
|
|
115
|
+
if (offset > i) {
|
|
116
|
+
return entry.node === ENDMARKER ? undefined : entry.node;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
decompress() {
|
|
122
|
+
const offsets = this.edges.map(e => e.offset);
|
|
123
|
+
const result = [];
|
|
124
|
+
for (const run of this.runs()) {
|
|
125
|
+
const edge = this.edge(run.value);
|
|
126
|
+
for (let k = 0; k < run.len; k++) {
|
|
127
|
+
result.push({ node: edge.node, offset: offsets[run.value] });
|
|
128
|
+
offsets[run.value] = offsets[run.value] + 1;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return result;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const DECODE = ['', 'A', 'C', 'G', 'T', 'N'];
|
|
2
|
+
export function encodedSequenceLength(encoded) {
|
|
3
|
+
const last = encoded[encoded.length - 1];
|
|
4
|
+
if (last === undefined) {
|
|
5
|
+
return 0;
|
|
6
|
+
}
|
|
7
|
+
let value = last;
|
|
8
|
+
let inLast = 0;
|
|
9
|
+
for (let i = 0; i < 3; i++) {
|
|
10
|
+
if (value % 6 === 0) {
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
value = Math.floor(value / 6);
|
|
14
|
+
inLast += 1;
|
|
15
|
+
}
|
|
16
|
+
return 3 * (encoded.length - 1) + inLast;
|
|
17
|
+
}
|
|
18
|
+
export function decodeSequence(encoded) {
|
|
19
|
+
let result = '';
|
|
20
|
+
for (const byte of encoded) {
|
|
21
|
+
let value = byte;
|
|
22
|
+
for (let i = 0; i < 3; i++) {
|
|
23
|
+
const base = DECODE[value % 6];
|
|
24
|
+
if (base === '') {
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
value = Math.floor(value / 6);
|
|
28
|
+
result += base;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
const COMPLEMENT = { A: 'T', C: 'G', G: 'C', T: 'A', N: 'N' };
|
|
34
|
+
export function reverseComplement(sequence) {
|
|
35
|
+
let result = '';
|
|
36
|
+
for (let i = sequence.length - 1; i >= 0; i--) {
|
|
37
|
+
result += COMPLEMENT[sequence[i]] ?? 'N';
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { GBZBase, GbzRecord, SchemaVersionError, formatPathName, GENERIC_SAMPLE, SCHEMA_VERSION } from './db.ts';
|
|
2
|
+
export type { GbzPath, HaplotypeSample, PathName } from './db.ts';
|
|
3
|
+
export type { ByteSource } from './filehandle.ts';
|
|
4
|
+
export type { Pos } from './gbwt/record.ts';
|
|
5
|
+
export { Subgraph } from './subgraph.ts';
|
|
6
|
+
export type { HaplotypeAlignment, HaplotypeOutput, PathIdentity, PathPosition, ReferencePath, SubgraphJson, SubgraphPath, ToJsonOptions, } from './subgraph.ts';
|
|
7
|
+
export { subgraphAtOffset, subgraphInInterval, subgraphAroundNodes } from './query.ts';
|
|
8
|
+
export type { PathQuery, QueryOptions } from './query.ts';
|
|
9
|
+
export { SqliteDatabase } from './sqlite/database.ts';
|
|
10
|
+
export { weightedLcs } from './lcs.ts';
|
|
11
|
+
export * as nodes from './gbwt/node.ts';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { GBZBase, GbzRecord, SchemaVersionError, formatPathName, GENERIC_SAMPLE, SCHEMA_VERSION } from "./db.js";
|
|
2
|
+
export { Subgraph } from "./subgraph.js";
|
|
3
|
+
export { subgraphAtOffset, subgraphInInterval, subgraphAroundNodes } from "./query.js";
|
|
4
|
+
export { SqliteDatabase } from "./sqlite/database.js";
|
|
5
|
+
export { weightedLcs } from "./lcs.js";
|
|
6
|
+
export * as nodes from "./gbwt/node.js";
|
package/dist/lcs.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function weightedLcs(a: number[], b: number[], weight: (x: number) => number): [pairs: [number, number][], weight: number];
|
package/dist/lcs.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
function prefixSums(sequence, weight) {
|
|
2
|
+
const sums = [0];
|
|
3
|
+
for (let i = 0; i < sequence.length; i++) {
|
|
4
|
+
sums.push(sums[i] + weight(sequence[i]));
|
|
5
|
+
}
|
|
6
|
+
return sums;
|
|
7
|
+
}
|
|
8
|
+
class MinHeap {
|
|
9
|
+
items = [];
|
|
10
|
+
push(value) {
|
|
11
|
+
const items = this.items;
|
|
12
|
+
items.push(value);
|
|
13
|
+
let i = items.length - 1;
|
|
14
|
+
while (i > 0) {
|
|
15
|
+
const parent = (i - 1) >> 1;
|
|
16
|
+
if (items[parent] <= value) {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
items[i] = items[parent];
|
|
20
|
+
i = parent;
|
|
21
|
+
}
|
|
22
|
+
items[i] = value;
|
|
23
|
+
}
|
|
24
|
+
peek() {
|
|
25
|
+
return this.items[0];
|
|
26
|
+
}
|
|
27
|
+
pop() {
|
|
28
|
+
const items = this.items;
|
|
29
|
+
const top = items[0];
|
|
30
|
+
const last = items.pop();
|
|
31
|
+
if (items.length > 0 && last !== undefined) {
|
|
32
|
+
let i = 0;
|
|
33
|
+
for (;;) {
|
|
34
|
+
const left = 2 * i + 1;
|
|
35
|
+
const right = left + 1;
|
|
36
|
+
let smallest = i;
|
|
37
|
+
let value = last;
|
|
38
|
+
if (left < items.length && items[left] < value) {
|
|
39
|
+
smallest = left;
|
|
40
|
+
value = items[left];
|
|
41
|
+
}
|
|
42
|
+
if (right < items.length && items[right] < value) {
|
|
43
|
+
smallest = right;
|
|
44
|
+
}
|
|
45
|
+
if (smallest === i) {
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
items[i] = items[smallest];
|
|
49
|
+
i = smallest;
|
|
50
|
+
}
|
|
51
|
+
items[i] = last;
|
|
52
|
+
}
|
|
53
|
+
return top;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
class Matrix {
|
|
57
|
+
a;
|
|
58
|
+
b;
|
|
59
|
+
aSums;
|
|
60
|
+
bSums;
|
|
61
|
+
points = new Map();
|
|
62
|
+
pendingEdits = new MinHeap();
|
|
63
|
+
constructor(a, b, weight) {
|
|
64
|
+
this.a = a;
|
|
65
|
+
this.b = b;
|
|
66
|
+
this.aSums = prefixSums(a, weight);
|
|
67
|
+
this.bSums = prefixSums(b, weight);
|
|
68
|
+
this.set(0, 0, { weight: 0, a: 0, b: 0, matches: 0 });
|
|
69
|
+
}
|
|
70
|
+
set(edits, diagonal, point) {
|
|
71
|
+
let row = this.points.get(edits);
|
|
72
|
+
if (!row) {
|
|
73
|
+
row = new Map();
|
|
74
|
+
this.points.set(edits, row);
|
|
75
|
+
this.pendingEdits.push(edits);
|
|
76
|
+
}
|
|
77
|
+
row.set(diagonal, point);
|
|
78
|
+
}
|
|
79
|
+
get(edits, diagonal) {
|
|
80
|
+
return this.points.get(edits)?.get(diagonal);
|
|
81
|
+
}
|
|
82
|
+
tryInsert(edits, diagonal, point) {
|
|
83
|
+
const existing = this.get(edits, diagonal);
|
|
84
|
+
if (!existing || point.weight > existing.weight) {
|
|
85
|
+
this.set(edits, diagonal, point);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
aWeight(offset) {
|
|
89
|
+
return this.aSums[offset + 1] - this.aSums[offset];
|
|
90
|
+
}
|
|
91
|
+
bWeight(offset) {
|
|
92
|
+
return this.bSums[offset + 1] - this.bSums[offset];
|
|
93
|
+
}
|
|
94
|
+
extend(edits) {
|
|
95
|
+
const row = this.points.get(edits);
|
|
96
|
+
if (!row) {
|
|
97
|
+
return undefined;
|
|
98
|
+
}
|
|
99
|
+
const diagonals = [...row.keys()].sort((x, y) => x - y);
|
|
100
|
+
for (const diagonal of diagonals) {
|
|
101
|
+
const found = row.get(diagonal);
|
|
102
|
+
if (!found) {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
const point = { ...found };
|
|
106
|
+
while (point.a < this.a.length && point.b < this.b.length && this.a[point.a] === this.b[point.b]) {
|
|
107
|
+
point.weight += 2 * this.aWeight(point.a);
|
|
108
|
+
point.a += 1;
|
|
109
|
+
point.b += 1;
|
|
110
|
+
point.matches += 1;
|
|
111
|
+
}
|
|
112
|
+
if (point.matches > 0) {
|
|
113
|
+
row.set(diagonal, point);
|
|
114
|
+
}
|
|
115
|
+
if (point.a === this.a.length && point.b === this.b.length) {
|
|
116
|
+
return point;
|
|
117
|
+
}
|
|
118
|
+
if (point.a < this.a.length) {
|
|
119
|
+
const w = this.aWeight(point.a);
|
|
120
|
+
this.tryInsert(edits + w, diagonal + w, { weight: point.weight, a: point.a + 1, b: point.b, matches: 0 });
|
|
121
|
+
}
|
|
122
|
+
if (point.b < this.b.length) {
|
|
123
|
+
const w = this.bWeight(point.b);
|
|
124
|
+
this.tryInsert(edits + w, diagonal - w, { weight: point.weight, a: point.a, b: point.b + 1, matches: 0 });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
nextEdits(edits) {
|
|
130
|
+
while (this.pendingEdits.peek() !== undefined && this.pendingEdits.peek() <= edits) {
|
|
131
|
+
this.pendingEdits.pop();
|
|
132
|
+
}
|
|
133
|
+
return this.pendingEdits.peek();
|
|
134
|
+
}
|
|
135
|
+
predecessor(a, b, edits) {
|
|
136
|
+
const diagonal = this.aSums[a] - this.bSums[b];
|
|
137
|
+
const prev = a > 0 && this.aWeight(a - 1) <= edits ? this.get(edits - this.aWeight(a - 1), diagonal - this.aWeight(a - 1)) : undefined;
|
|
138
|
+
const next = b > 0 && this.bWeight(b - 1) <= edits ? this.get(edits - this.bWeight(b - 1), diagonal + this.bWeight(b - 1)) : undefined;
|
|
139
|
+
if (prev && next) {
|
|
140
|
+
return prev.weight > next.weight ? [prev, edits - this.aWeight(a - 1)] : [next, edits - this.bWeight(b - 1)];
|
|
141
|
+
}
|
|
142
|
+
if (prev) {
|
|
143
|
+
return [prev, edits - this.aWeight(a - 1)];
|
|
144
|
+
}
|
|
145
|
+
if (next) {
|
|
146
|
+
return [next, edits - this.bWeight(b - 1)];
|
|
147
|
+
}
|
|
148
|
+
return undefined;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export function weightedLcs(a, b, weight) {
|
|
152
|
+
let prefix = 0;
|
|
153
|
+
while (prefix < a.length && prefix < b.length && a[prefix] === b[prefix]) {
|
|
154
|
+
prefix += 1;
|
|
155
|
+
}
|
|
156
|
+
let suffix = 0;
|
|
157
|
+
while (suffix < a.length - prefix && suffix < b.length - prefix && a[a.length - 1 - suffix] === b[b.length - 1 - suffix]) {
|
|
158
|
+
suffix += 1;
|
|
159
|
+
}
|
|
160
|
+
const pairs = [];
|
|
161
|
+
let total = 0;
|
|
162
|
+
for (let i = 0; i < prefix; i++) {
|
|
163
|
+
pairs.push([i, i]);
|
|
164
|
+
total += weight(a[i]);
|
|
165
|
+
}
|
|
166
|
+
const [middle, middleWeight] = weightedLcsCore(a.slice(prefix, a.length - suffix), b.slice(prefix, b.length - suffix), weight);
|
|
167
|
+
for (const [i, j] of middle) {
|
|
168
|
+
pairs.push([i + prefix, j + prefix]);
|
|
169
|
+
}
|
|
170
|
+
total += middleWeight;
|
|
171
|
+
for (let i = suffix; i > 0; i--) {
|
|
172
|
+
pairs.push([a.length - i, b.length - i]);
|
|
173
|
+
total += weight(a[a.length - i]);
|
|
174
|
+
}
|
|
175
|
+
return [pairs, total];
|
|
176
|
+
}
|
|
177
|
+
function weightedLcsCore(a, b, weight) {
|
|
178
|
+
if (a.length === 0 || b.length === 0) {
|
|
179
|
+
return [[], 0];
|
|
180
|
+
}
|
|
181
|
+
const matrix = new Matrix(a, b, weight);
|
|
182
|
+
let edits = 0;
|
|
183
|
+
let point = { weight: 0, a: 0, b: 0, matches: 0 };
|
|
184
|
+
for (;;) {
|
|
185
|
+
const end = matrix.extend(edits);
|
|
186
|
+
if (end) {
|
|
187
|
+
point = end;
|
|
188
|
+
break;
|
|
189
|
+
}
|
|
190
|
+
const next = matrix.nextEdits(edits);
|
|
191
|
+
if (next === undefined) {
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
edits = next;
|
|
195
|
+
}
|
|
196
|
+
const result = [];
|
|
197
|
+
const finalWeight = point.weight / 2;
|
|
198
|
+
point = { ...point };
|
|
199
|
+
for (;;) {
|
|
200
|
+
for (let i = 0; i < point.matches; i++) {
|
|
201
|
+
point.a -= 1;
|
|
202
|
+
point.b -= 1;
|
|
203
|
+
result.push([point.a, point.b]);
|
|
204
|
+
}
|
|
205
|
+
const pred = matrix.predecessor(point.a, point.b, edits);
|
|
206
|
+
if (!pred) {
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
point = { ...pred[0] };
|
|
210
|
+
edits = pred[1];
|
|
211
|
+
}
|
|
212
|
+
result.reverse();
|
|
213
|
+
return [result, finalWeight];
|
|
214
|
+
}
|
package/dist/query.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { GBZBase } from './db.ts';
|
|
2
|
+
import { Subgraph } from './subgraph.ts';
|
|
3
|
+
import type { HaplotypeOutput } from './subgraph.ts';
|
|
4
|
+
export interface QueryOptions {
|
|
5
|
+
context?: number;
|
|
6
|
+
haplotypes?: HaplotypeOutput;
|
|
7
|
+
limit?: number;
|
|
8
|
+
}
|
|
9
|
+
export interface PathQuery {
|
|
10
|
+
sample?: string;
|
|
11
|
+
contig: string;
|
|
12
|
+
haplotype?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function subgraphAtOffset(db: GBZBase, query: PathQuery, offset: number, opts?: QueryOptions): Promise<Subgraph>;
|
|
15
|
+
export declare function subgraphInInterval(db: GBZBase, query: PathQuery, start: number, end: number, opts?: QueryOptions): Promise<Subgraph>;
|
|
16
|
+
export declare function subgraphAroundNodes(db: GBZBase, nodes: number[], opts?: QueryOptions): Promise<Subgraph>;
|
package/dist/query.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { GENERIC_SAMPLE } from "./db.js";
|
|
2
|
+
import { Subgraph } from "./subgraph.js";
|
|
3
|
+
function pathName(query, fragment) {
|
|
4
|
+
return {
|
|
5
|
+
sample: query.sample ?? GENERIC_SAMPLE,
|
|
6
|
+
contig: query.contig,
|
|
7
|
+
haplotype: query.haplotype ?? 0,
|
|
8
|
+
fragment,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export async function subgraphAtOffset(db, query, offset, opts = {}) {
|
|
12
|
+
const subgraph = new Subgraph(db);
|
|
13
|
+
subgraph.limit = opts.limit;
|
|
14
|
+
const reference = await subgraph.pathPosition(pathName(query, offset));
|
|
15
|
+
await subgraph.aroundPosition(reference.position.handle, reference.position.nodeOffset, opts.context ?? 100);
|
|
16
|
+
subgraph.extractPaths(reference, opts.haplotypes ?? 'all');
|
|
17
|
+
return subgraph;
|
|
18
|
+
}
|
|
19
|
+
export async function subgraphInInterval(db, query, start, end, opts = {}) {
|
|
20
|
+
const subgraph = new Subgraph(db);
|
|
21
|
+
subgraph.limit = opts.limit;
|
|
22
|
+
const reference = await subgraph.pathPosition(pathName(query, start));
|
|
23
|
+
await subgraph.aroundInterval(reference.position, end - start, opts.context ?? 100);
|
|
24
|
+
subgraph.extractPaths(reference, opts.haplotypes ?? 'all');
|
|
25
|
+
return subgraph;
|
|
26
|
+
}
|
|
27
|
+
export async function subgraphAroundNodes(db, nodes, opts = {}) {
|
|
28
|
+
const haplotypes = opts.haplotypes ?? 'all';
|
|
29
|
+
if (haplotypes === 'reference-only') {
|
|
30
|
+
throw new Error('Cannot output a reference path in a node-based query');
|
|
31
|
+
}
|
|
32
|
+
const subgraph = new Subgraph(db);
|
|
33
|
+
subgraph.limit = opts.limit;
|
|
34
|
+
await subgraph.aroundNodes(nodes, opts.context ?? 100);
|
|
35
|
+
subgraph.extractPaths(undefined, haplotypes);
|
|
36
|
+
return subgraph;
|
|
37
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Pager } from './pager.ts';
|
|
2
|
+
import type { SqlValue } from './record.ts';
|
|
3
|
+
export declare class BTree {
|
|
4
|
+
private pager;
|
|
5
|
+
private readonly usable;
|
|
6
|
+
constructor(pager: Pager, reservedBytes: number);
|
|
7
|
+
private localPayloadSize;
|
|
8
|
+
private payload;
|
|
9
|
+
private pageAt;
|
|
10
|
+
tableRowid(root: number, rowid: number): Promise<SqlValue[] | undefined>;
|
|
11
|
+
tableScan(root: number): AsyncGenerator<{
|
|
12
|
+
rowid: number;
|
|
13
|
+
values: SqlValue[];
|
|
14
|
+
}>;
|
|
15
|
+
private indexCell;
|
|
16
|
+
indexScanFrom(root: number, low: number[]): AsyncGenerator<SqlValue[]>;
|
|
17
|
+
indexSeekLE(root: number, key: number[]): Promise<SqlValue[] | undefined>;
|
|
18
|
+
}
|