|
| 1 | +import { binarySpreadSample } from './constants.js' |
| 2 | + |
1 | 3 | /** |
2 | 4 | * @import { DistanceMetric, HypVectorMetadata } from './types.js' |
3 | 5 | * @import { FileMetaData, KeyValue } from 'hyparquet' |
@@ -114,6 +116,38 @@ export function packBinary(v, dim = v.length) { |
114 | 116 | return bytes |
115 | 117 | } |
116 | 118 |
|
| 119 | +/** |
| 120 | + * Expected Hamming distance between two random rows of packed binary codes, |
| 121 | + * from per-bit one-frequencies: sum over bits of 2 * p * (1 - p). Identical |
| 122 | + * codes measure 0; independent fair bits measure dimension / 2. Rows are |
| 123 | + * sampled with an even stride when there are more than sampleCap, which is |
| 124 | + * plenty to estimate per-bit frequencies at any corpus size. |
| 125 | + * |
| 126 | + * @param {Uint8Array[]} codes packed binary codes (see packBinary) |
| 127 | + * @param {number} dimension vector dimension |
| 128 | + * @param {number} [sampleCap] max rows to sample |
| 129 | + * @returns {number} expected Hamming distance in bits |
| 130 | + */ |
| 131 | +export function expectedHamming(codes, dimension, sampleCap = binarySpreadSample) { |
| 132 | + if (!codes.length) return 0 |
| 133 | + const stride = Math.max(1, Math.ceil(codes.length / sampleCap)) |
| 134 | + const ones = new Uint32Array(dimension) |
| 135 | + let sampled = 0 |
| 136 | + for (let row = 0; row < codes.length; row += stride) { |
| 137 | + const code = codes[row] |
| 138 | + for (let i = 0; i < dimension; i += 1) { |
| 139 | + if (code[i >> 3] >> (i & 7) & 1) ones[i] += 1 |
| 140 | + } |
| 141 | + sampled += 1 |
| 142 | + } |
| 143 | + let expected = 0 |
| 144 | + for (let i = 0; i < dimension; i += 1) { |
| 145 | + const p = ones[i] / sampled |
| 146 | + expected += 2 * p * (1 - p) |
| 147 | + } |
| 148 | + return expected |
| 149 | +} |
| 150 | + |
117 | 151 | /** |
118 | 152 | * Unpack a Uint8Array of raw little-endian float32 bytes into a Float32Array. |
119 | 153 | * Always returns a fresh aligned copy, since the source buffer may be |
|
0 commit comments