@@ -61,14 +61,21 @@ function generateSyntheticVectors(n, dims, seed) {
6161}
6262
6363/**
64- * Compute percentile from sorted array.
64+ * Compute percentile from sorted array using linear interpolation.
65+ * For small sample sizes (< 100), P99 will interpolate between the
66+ * highest values rather than always returning the max.
6567 * @param {number[] } sorted - sorted array of values
6668 * @param {number } p - percentile (0-100)
6769 * @returns {number }
6870 */
6971function percentile ( sorted , p ) {
70- const idx = Math . ceil ( ( p / 100 ) * sorted . length ) - 1 ;
71- return sorted [ Math . max ( 0 , idx ) ] ;
72+ if ( sorted . length === 0 ) return 0 ;
73+ if ( sorted . length === 1 ) return sorted [ 0 ] ;
74+ const rank = ( p / 100 ) * ( sorted . length - 1 ) ;
75+ const lo = Math . floor ( rank ) ;
76+ const hi = Math . ceil ( rank ) ;
77+ const frac = rank - lo ;
78+ return sorted [ lo ] + frac * ( sorted [ hi ] - sorted [ lo ] ) ;
7279}
7380
7481/**
@@ -89,6 +96,9 @@ function percentile(sorted, p) {
8996 * @returns {Promise<object> } timing results
9097 */
9198async function benchADC ( nCodes , nQueries , iterations , opts = { } ) {
99+ if ( iterations < 1 ) throw new Error ( 'iterations must be >= 1' ) ;
100+ if ( nCodes < 1 ) throw new Error ( 'nCodes must be >= 1' ) ;
101+ if ( nQueries < 1 ) throw new Error ( 'nQueries must be >= 1' ) ;
92102 const dims = opts . dims || 768 ;
93103 const m = opts . m || 8 ;
94104 const ksub = opts . ksub || 256 ;
@@ -118,7 +128,7 @@ async function benchADC(nCodes, nQueries, iterations, opts = {}) {
118128 const allCodes = new Uint8Array ( nCodes * m ) ;
119129 for ( let i = 0 ; i < nCodes ; i ++ ) {
120130 const vec = data . subarray ( i * dims , ( i + 1 ) * dims ) ;
121- const code = codebook . encodePq ( new Float32Array ( vec ) ) ;
131+ const code = codebook . encodePq ( vec ) ;
122132 allCodes . set ( code , i * m ) ;
123133 }
124134 const encMs = performance . now ( ) - encStart ;
@@ -181,6 +191,8 @@ async function benchADC(nCodes, nQueries, iterations, opts = {}) {
181191 * @returns {Promise<object> } timing results
182192 */
183193async function benchTraining ( nVectors , iterations , opts = { } ) {
194+ if ( iterations < 1 ) throw new Error ( 'iterations must be >= 1' ) ;
195+ if ( nVectors < 1 ) throw new Error ( 'nVectors must be >= 1' ) ;
184196 const dims = opts . dims || 768 ;
185197 const m = opts . m || 8 ;
186198 const ksub = opts . ksub || 256 ;
@@ -256,7 +268,7 @@ function computeGroundTruth(vectors, n, dims, queries, k) {
256268 dists [ i ] = sum ;
257269 }
258270
259- // Find top-k (partial sort via selection )
271+ // Find top-k via full sort (acceptable: ground truth cost excluded from timing )
260272 const indices = Array . from ( { length : n } , ( _ , i ) => i ) ;
261273 indices . sort ( ( a , b ) => dists [ a ] - dists [ b ] ) ;
262274 results . push ( indices . slice ( 0 , k ) ) ;
0 commit comments