@gmod/hic 1.3.2 → 2.1.0

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/README.md CHANGED
@@ -5,11 +5,9 @@
5
5
 
6
6
  Read `.hic` contact matrix files, in Node.js or the browser.
7
7
 
8
- A fork of [hic-straw](https://github.com/aidenlab/hic-straw), rewritten in
9
- TypeScript for a genome browser: many region pairs per fetch, arbitrary
10
- viewport windows, and contacts handed to a GPU renderer. See
11
- [docs/optimizations.md](docs/optimizations.md) and
12
- [docs/correctness.md](docs/correctness.md) for what changed.
8
+ `@gmod/hic` is a fork of [hic-straw](https://github.com/aidenlab/hic-straw),
9
+ rewritten in TypeScript for a genome browser that fetches many region pairs at
10
+ once over arbitrary viewport windows.
13
11
 
14
12
  ## Install
15
13
 
@@ -29,7 +27,7 @@ await hic.getNormalizationOptions() // ['NONE', 'VC', 'VC_SQRT', 'KR', 'SCALE']
29
27
 
30
28
  const region = { chr: '1', start: 0, end: 20_000_000 }
31
29
  const { records, appliedNormalization, transposed } =
32
- await hic.getContactRecords('KR', region, region, 'BP', 2_500_000)
30
+ await hic.getContactRecords('KR', region, region, 2_500_000)
33
31
 
34
32
  // records is struct-of-arrays: three parallel typed arrays
35
33
  records.bin1 // Int32Array of bin indices along region1
@@ -38,16 +36,11 @@ records.counts // Float32Array of contact counts
38
36
  ```
39
37
 
40
38
  `getNormalizationOptions()` and `getContactRecords()` take an optional trailing
41
- `{ onProgress }`, called as `(current, total)`:
42
-
43
- - The unit is the block for a contact fetch.
44
- - The unit is the expected-value chunk for the normalization-index walk, the
45
- slow part of opening a pre-v9 file.
46
-
47
- See [docs/api.md](docs/api.md#progress).
39
+ `{ onProgress }`, called as `(current, total)`; see
40
+ [docs/api.md](docs/api.md#progress).
48
41
 
49
42
  ```js
50
- await hic.getContactRecords('KR', region, region, 'BP', 2_500_000, {
43
+ await hic.getContactRecords('KR', region, region, 2_500_000, {
51
44
  onProgress: (current, total) => showBar(current / total),
52
45
  })
53
46
  ```
@@ -67,17 +60,11 @@ const hic = new HicFile({
67
60
  })
68
61
  ```
69
62
 
70
- **Over HTTP, put a byte-range cache underneath. It is worth more than every
71
- other optimization in this package combined.**
72
-
73
- - A contact-matrix query reads many small blocks scattered through the file, and
74
- a browser runs about six requests per origin at a time, so the number of
75
- requests sets the load time.
76
- - [`@gmod/range-cache-filehandle`](https://github.com/GMOD/range-cache-filehandle)
77
- is a drop-in for `RemoteFile` that reads 256 KiB chunks, so neighboring blocks
78
- share a request.
79
- - Whole chr1 at 5 kb of a real 69 GB ENCODE file, in headless Chrome: **24.0 s
80
- and 225 requests becomes 1.8 s and 45**, with byte-identical output.
63
+ **Over HTTP, put a byte-range cache underneath.** A query reads many small
64
+ scattered blocks, so the request count sets the load time.
65
+ [`@gmod/range-cache-filehandle`](https://github.com/GMOD/range-cache-filehandle)
66
+ cut a whole-chr1 fetch from a 69 GB ENCODE file from 24.0 s to 1.8 s
67
+ ([docs/optimizations.md](docs/optimizations.md#round-trips-not-cpu-dominate-a-remote-fetch)).
81
68
 
82
69
  ```js
83
70
  import { RemoteFileWithRangeCache } from '@gmod/range-cache-filehandle'
@@ -105,10 +92,12 @@ See [docs/api.md](docs/api.md) for the full API reference.
105
92
  - A `.hic` stores only the `bin1 <= bin2` half of the matrix, so a query whose x
106
93
  window sits right of its y window gets swapped. `transposed` marks the swap,
107
94
  and `bin1` then runs along `region2`.
108
- - `appliedNormalization` names the normalization the file applied, which may
95
+ - `appliedNormalization` names the normalization the reader applied, which may
109
96
  differ from the one you asked for. The file stores vectors per (type,
110
97
  chromosome, unit, binsize), so it can offer KR at 5 kb and nothing at 2.5 Mb.
111
- - `BP` is the only unit this fork supports; it drops the FRAG code paths.
98
+ - Bins are base pairs only; this fork drops the FRAG (restriction-fragment)
99
+ code paths.
100
+ - Chromosome names match the file's own exactly, with no `chr` aliasing.
112
101
  - Each `HicFile` caches up to 128 MB of decompressed contacts. Pass
113
102
  `blockCacheMaxBytes` to change that ceiling.
114
103
  - Record order is unspecified — the result concatenates whole blocks, cached
@@ -135,10 +124,6 @@ be linked from [jbrowse.org](http://jbrowse.org).
135
124
  If you use `.hic` files, please also cite the Juicer/Juicebox papers from the
136
125
  [Aiden Lab](https://github.com/aidenlab), whose hic-straw this is derived from.
137
126
 
138
- ## Contributing
139
-
140
- See [CONTRIBUTING.md](CONTRIBUTING.md) for development and release workflow.
141
-
142
127
  ## License
143
128
 
144
- MIT, as is upstream hic-straw. See [LICENSE](LICENSE).
129
+ MIT, as is upstream hic-straw; see [LICENSE](LICENSE).
@@ -35,33 +35,16 @@ class BufferedFile {
35
35
  const sliceStart = start - bufferStart;
36
36
  result = buf.slice(sliceStart, sliceStart + length);
37
37
  }
38
- else if (buf && start < bufferStart && end > bufferStart) {
39
- // Overlap left (unexpected in straw); we don't adjust the buffer
40
- const l1 = bufferStart - start;
41
- const a1 = await this.file.read(position, l1, opts);
42
- const l2 = length - l1;
43
- if (l2 > 0) {
44
- result = concatBuffers(a1, buf.slice(0, l2));
45
- }
46
- else {
47
- result = a1;
48
- }
49
- }
50
- else if (buf && start < bufferEnd && end > bufferEnd) {
51
- // Overlap right
38
+ else if (buf && start >= bufferStart && start < bufferEnd) {
39
+ // Overlap right. The expected-values walk only moves forward, so a
40
+ // request starting before the buffer takes the fresh read below.
52
41
  const l1 = bufferEnd - start;
53
42
  const a1 = buf.slice(this.bufferLength - l1, this.bufferLength);
54
- const l2 = length - l1;
55
- if (l2 > 0) {
56
- const next = await this.file.read(bufferEnd, this.size, opts);
57
- this.buffer = next;
58
- this.bufferStart = bufferEnd;
59
- this.bufferLength = next.byteLength;
60
- result = concatBuffers(a1, next.slice(0, l2));
61
- }
62
- else {
63
- result = a1;
64
- }
43
+ const next = await this.file.read(bufferEnd, this.size, opts);
44
+ this.buffer = next;
45
+ this.bufferStart = bufferEnd;
46
+ this.bufferLength = next.byteLength;
47
+ result = concatBuffers(a1, next.slice(0, length - l1));
65
48
  }
66
49
  else {
67
50
  // No overlap with buffer
@@ -1 +1 @@
1
- {"version":3,"file":"bufferedFile.js","sourceRoot":"","sources":["../src/bufferedFile.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bufferedFile.js","sourceRoot":"","sources":["../src/bufferedFile.ts"],"names":[],"mappings":"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"}
package/dist/hicFile.d.ts CHANGED
@@ -2,7 +2,7 @@ import Matrix from './matrix.ts';
2
2
  import NormalizationVector from './normalizationVector.ts';
3
3
  import type { ContactRecords } from './contactRecords.ts';
4
4
  import type MatrixZoomData from './matrixZoomData.ts';
5
- import type { BaseOpts, BlockIndexEntry, HicMetadata, HicRegion, ProgressOpts, Reader } from './types.ts';
5
+ import type { BaseOpts, BlockIndexEntry, ExpectedValues, HicMetadata, HicRegion, ProgressOpts, Reader } from './types.ts';
6
6
  import type { GenericFilehandle } from 'generic-filehandle2';
7
7
  /**
8
8
  * Prefix of the error thrown when a region pair has no matrix at the requested
@@ -27,8 +27,7 @@ export interface HicConfig {
27
27
  /**
28
28
  * Ceiling on the decompressed contacts the block cache holds, in bytes.
29
29
  * Defaults to 128 MB. Lower it where the budget is tight and re-reading is
30
- * cheaper than the memory; raising it buys nothing once a fetch's working set
31
- * fits, since the entry cap bounds the cache too.
30
+ * cheaper than the memory.
32
31
  */
33
32
  blockCacheMaxBytes?: number;
34
33
  }
@@ -37,10 +36,11 @@ interface Block {
37
36
  records: ContactRecords;
38
37
  }
39
38
  export declare class HicFile {
40
- private config;
39
+ private nvi;
41
40
  private file;
42
41
  private headerCache;
43
42
  private normVectorIndexCache;
43
+ private expectedSectionCache;
44
44
  private normVectorCache;
45
45
  private matrixCache;
46
46
  private blockCache;
@@ -49,10 +49,10 @@ export declare class HicFile {
49
49
  private version;
50
50
  private genomeId;
51
51
  private footerPosition;
52
+ private expectedValuesPosition;
52
53
  normExpectedValueVectorsPosition: number | undefined;
53
54
  private chromosomes;
54
55
  private chromosomeIndexMap;
55
- private chrAliasTable;
56
56
  private bpResolutions;
57
57
  private masterIndex;
58
58
  private meta;
@@ -63,7 +63,7 @@ export declare class HicFile {
63
63
  private readFooter;
64
64
  getMatrix(chrIdx1: number, chrIdx2: number, opts?: BaseOpts): Promise<Matrix | undefined>;
65
65
  private readMatrix;
66
- getContactRecords(normalization: string, region1: HicRegion, region2: HicRegion, units: string, binsize: number, opts?: ProgressOpts): Promise<{
66
+ getContactRecords(normalization: string, region1: HicRegion, region2: HicRegion, binsize: number, opts?: ProgressOpts): Promise<{
67
67
  records: ContactRecords;
68
68
  appliedNormalization: string;
69
69
  transposed: boolean;
@@ -81,7 +81,7 @@ export declare class HicFile {
81
81
  * of the session, so it is worth one memcpy not to pin an oversized buffer.
82
82
  */
83
83
  private parseBlockRecords;
84
- getNormalizationVector(type: string, chr: string, unit: string, binSize: number, opts?: BaseOpts): Promise<NormalizationVector | undefined>;
84
+ getNormalizationVector(type: string, chr: string, binSize: number, opts?: BaseOpts): Promise<NormalizationVector | undefined>;
85
85
  private readNormalizationVector;
86
86
  getNormVectorIndex(opts?: ProgressOpts): Promise<Record<string, BlockIndexEntry> | undefined>;
87
87
  private getParsedNormVectorIndex;
@@ -89,8 +89,21 @@ export declare class HicFile {
89
89
  getNormalizationOptions(opts?: ProgressOpts): Promise<string[]>;
90
90
  private readNormVectorIndex;
91
91
  private readNormExpectedValuesAndNormVectorIndex;
92
- private skipExpectedValues;
92
+ /**
93
+ * The expected count for a region pair at `binsize`, the denominator of
94
+ * observed/expected, as straw computes it. `normalization` must be the one
95
+ * the pair's records carry (`getContactRecords`' `appliedNormalization`),
96
+ * since each normalization has its own vector. Undefined where the file has
97
+ * no expected value for it: no vector at this binsize, or a version 5 file
98
+ * asked for a normalized one.
99
+ *
100
+ * Within a chromosome the vector is read for only the distances the pair
101
+ * spans. Between chromosomes straw divides by the map's average contact, the
102
+ * raw `sumCounts` over its bins, whatever the normalization.
103
+ */
104
+ getExpectedValues(normalization: string, region1: HicRegion, region2: HicRegion, binsize: number, opts?: ProgressOpts): Promise<ExpectedValues | undefined>;
105
+ private getExpectedSection;
106
+ private walkExpectedSection;
93
107
  private parseNormVectorEntry;
94
- getFileChrName(chrAlias: string): string;
95
108
  }
96
109
  export {};