| 42 | /* ── Encode ─────────────────────────────────────────────────────────── */ |
| 43 | |
| 44 | void cbm_rsq_encode(const float *v, cbm_rsq_code_t *out) { |
| 45 | rsq_init_diag(); |
| 46 | |
| 47 | float rot[CBM_RSQ_DIM]; |
| 48 | for (int d = 0; d < CBM_RSQ_IN_DIM; d++) { |
| 49 | rot[d] = v[d] * g_rsq_diag[d]; |
| 50 | } |
| 51 | for (int d = CBM_RSQ_IN_DIM; d < CBM_RSQ_DIM; d++) { |
| 52 | rot[d] = 0.0F; |
| 53 | } |
| 54 | rsq_fwht(rot); |
| 55 | /* Normalize the transform so the rotation is orthonormal (H/√D): keeps |
| 56 | * the coordinates in a data-independent range and makes the estimated IP |
| 57 | * directly comparable to the pre-rotation IP. */ |
| 58 | const float inv_sqrt_d = 1.0F / 32.0F; /* 1/√1024 */ |
| 59 | float lo = rot[0] * inv_sqrt_d; |
| 60 | float hi = lo; |
| 61 | for (int d = 0; d < CBM_RSQ_DIM; d++) { |
| 62 | rot[d] *= inv_sqrt_d; |
| 63 | if (rot[d] < lo) { |
| 64 | lo = rot[d]; |
| 65 | } |
| 66 | if (rot[d] > hi) { |
| 67 | hi = rot[d]; |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | /* Per-vector scalar quantization over [lo, hi] at CBM_RSQ_BITS. */ |
| 72 | float range = hi - lo; |
| 73 | float step = range > 0.0F ? range / (float)CBM_RSQ_LEVELS : 1.0F; |
| 74 | out->offset = lo; |
| 75 | out->scale = step; |
| 76 | |
| 77 | int32_t sum = 0; |
| 78 | memset(out->codes, 0, sizeof(out->codes)); |
| 79 | for (int d = 0; d < CBM_RSQ_DIM; d++) { |
| 80 | float q = (rot[d] - lo) / step; |
| 81 | int32_t c = (int32_t)(q + 0.5F); |
| 82 | if (c < 0) { |
| 83 | c = 0; |
| 84 | } |
| 85 | if (c > CBM_RSQ_LEVELS) { |
| 86 | c = CBM_RSQ_LEVELS; |
| 87 | } |
| 88 | sum += c; |
| 89 | if (d & 1) { |
| 90 | out->codes[d >> 1] |= (uint8_t)(c << 4); |
| 91 | } else { |
| 92 | out->codes[d >> 1] |= (uint8_t)c; |
| 93 | } |
| 94 | } |
| 95 | out->code_sum = sum; |
| 96 | } |
| 97 | |
| 98 | /* ── Estimated inner product from two codes ─────────────────────────── */ |
| 99 |
no test coverage detected