Signet Forge 0.1.0
C++20 Parquet library with AI-native extensions
DEMO
Loading...
Searching...
No Matches
vector_type.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright 2026 Johnson Ogundeji
3
8
9#pragma once
10
11// ---------------------------------------------------------------------------
12// vector_type.hpp — AI-native ML vector column type for SignetStack Signet Forge
13//
14// Provides VectorWriter / VectorReader for storing and retrieving dense
15// float embedding vectors as FIXED_LEN_BYTE_ARRAY Parquet columns, plus
16// SIMD-accelerated vector utilities (dot product, L2 distance, normalize).
17//
18// Header-only. Part of the signet::forge AI module.
19// ---------------------------------------------------------------------------
20
21#include "signet/types.hpp"
22#include "signet/error.hpp"
23#include "signet/schema.hpp"
24
25#include <cstdint>
26#include <cstring>
27#include <cmath>
28#include <vector>
29#include <string>
30
31// -- SIMD intrinsic headers (conditional) ------------------------------------
32#if defined(__AVX2__)
33 #include <immintrin.h>
34#elif defined(__SSE4_2__) || defined(__SSE2__)
35 #include <immintrin.h>
36#elif defined(__ARM_NEON) || defined(__ARM_NEON__)
37 #include <arm_neon.h>
38#endif
39
40namespace signet::forge {
41
42// ===========================================================================
43// VectorElementType — data type of individual vector elements
44// ===========================================================================
45
47enum class VectorElementType : int32_t {
48 FLOAT32 = 0,
49 FLOAT64 = 1,
50 FLOAT16 = 2
51};
52
53// ===========================================================================
54// IEEE 754 half-precision (float16) conversion utilities
55// ===========================================================================
56
59inline float f16_to_f32(uint16_t h) noexcept {
60 const uint32_t sign = static_cast<uint32_t>(h >> 15) & 0x1u;
61 const uint32_t exponent = static_cast<uint32_t>(h >> 10) & 0x1Fu;
62 const uint32_t mantissa = static_cast<uint32_t>(h) & 0x3FFu;
63
64 uint32_t f32_bits = 0;
65
66 if (exponent == 0) {
67 if (mantissa == 0) {
68 // +/- zero
69 f32_bits = sign << 31;
70 } else {
71 // Subnormal: renormalize to float32 normal
72 // f16 subnormal: value = (-1)^sign * 2^(-14) * (0.mantissa)
73 uint32_t m = mantissa;
74 int32_t e = -1;
75 while ((m & 0x400u) == 0) {
76 m <<= 1;
77 e--;
78 }
79 m &= ~0x400u; // clear leading 1
80 // f32 exponent: bias(127) + f16_exp(-14) + e
81 uint32_t f32_exp = static_cast<uint32_t>(127 - 14 + e + 1);
82 f32_bits = (sign << 31) | (f32_exp << 23) | (m << 13);
83 }
84 } else if (exponent == 0x1Fu) {
85 // Infinity or NaN
86 f32_bits = (sign << 31) | (0xFFu << 23) | (mantissa << 13);
87 } else {
88 // Normal number: rebias exponent from f16 bias (15) to f32 bias (127)
89 uint32_t f32_exp = exponent - 15u + 127u;
90 f32_bits = (sign << 31) | (f32_exp << 23) | (mantissa << 13);
91 }
92
93 float result;
94 std::memcpy(&result, &f32_bits, sizeof(result));
95 return result;
96}
97
100inline uint16_t f32_to_f16(float val) noexcept {
101 uint32_t f32_bits;
102 std::memcpy(&f32_bits, &val, sizeof(f32_bits));
103
104 const uint32_t sign = (f32_bits >> 31) & 0x1u;
105 const uint32_t exponent = (f32_bits >> 23) & 0xFFu;
106 const uint32_t mantissa = f32_bits & 0x7FFFFFu;
107
108 uint16_t h_sign = static_cast<uint16_t>(sign << 15);
109
110 if (exponent == 0xFF) {
111 // Infinity or NaN
112 if (mantissa == 0) {
113 return h_sign | 0x7C00u; // Infinity
114 } else {
115 // NaN — preserve some mantissa bits
116 return h_sign | 0x7C00u | static_cast<uint16_t>(mantissa >> 13);
117 }
118 }
119
120 // Unbias the float32 exponent
121 int32_t unbiased_exp = static_cast<int32_t>(exponent) - 127;
122
123 if (unbiased_exp > 15) {
124 // Overflow → infinity
125 return h_sign | 0x7C00u;
126 }
127
128 if (unbiased_exp < -24) {
129 // Too small → zero
130 return h_sign;
131 }
132
133 if (unbiased_exp < -14) {
134 // Subnormal in float16: shift mantissa right, adding the implicit 1
135 uint32_t full_mantissa = mantissa | 0x800000u; // add implicit leading 1
136 int32_t shift = -1 - unbiased_exp - 14 + 24; // = -unbiased_exp - 14 + 23
137 // Handle shift == 32 specially: preserve smallest subnormal (CWE-682)
138 if (shift == 32) {
139 uint16_t h_mantissa = static_cast<uint16_t>((full_mantissa >> 31) & 1u);
140 return h_sign | h_mantissa;
141 }
142 // Guard: shift > 32 or negative is UB for uint32_t
143 if (shift < 0 || shift > 32) {
144 return h_sign; // too small for float16 subnormal — rounds to zero
145 }
146 // Round to nearest even
147 uint32_t rounded = full_mantissa >> shift;
148 uint32_t remainder = full_mantissa & ((1u << shift) - 1u);
149 uint32_t midpoint = 1u << (shift - 1);
150 if (remainder > midpoint || (remainder == midpoint && (rounded & 1u))) {
151 rounded++;
152 }
153 return h_sign | static_cast<uint16_t>(rounded);
154 }
155
156 // Normal number
157 uint16_t h_exp = static_cast<uint16_t>((unbiased_exp + 15) << 10);
158 // Round mantissa (23 bits → 10 bits, dropping 13 bits)
159 uint16_t h_man = static_cast<uint16_t>(mantissa >> 13);
160 // Round to nearest even on the dropped bits
161 uint32_t remainder = mantissa & 0x1FFFu;
162 if (remainder > 0x1000u || (remainder == 0x1000u && (h_man & 1u))) {
163 h_man++;
164 if (h_man > 0x3FFu) {
165 // Mantissa overflow → increment exponent
166 h_man = 0;
167 h_exp += (1u << 10);
168 }
169 }
170 return h_sign | h_exp | h_man;
171}
172
173// ===========================================================================
174// VectorColumnSpec — describes the shape and element type of a vector column
175// ===========================================================================
176
182 uint32_t dimension = 0;
184
186 [[nodiscard]] constexpr size_t element_size() const noexcept {
187 switch (element_type) {
188 case VectorElementType::FLOAT16: return 2;
189 case VectorElementType::FLOAT32: return 4;
190 case VectorElementType::FLOAT64: return 8;
191 }
192 return 4; // unreachable, silence warnings
193 }
194
196 [[nodiscard]] constexpr size_t bytes_per_vector() const noexcept {
197 return static_cast<size_t>(dimension) * element_size();
198 }
199};
200
201// ===========================================================================
202// VectorWriter — encodes float vectors as FIXED_LEN_BYTE_ARRAY page data
203// ===========================================================================
204
221public:
225 : spec_(spec) {}
226
228 inline void add(const float* data) {
229 const size_t dim = spec_.dimension;
230 const size_t bpv = spec_.bytes_per_vector();
231
232 switch (spec_.element_type) {
234 const auto* raw = reinterpret_cast<const uint8_t*>(data);
235 buf_.insert(buf_.end(), raw, raw + bpv);
236 break;
237 }
239 size_t offset = buf_.size();
240 buf_.resize(offset + bpv);
241 // Use memcpy to avoid unaligned double writes (CWE-704)
242 for (size_t i = 0; i < dim; ++i) {
243 double d = static_cast<double>(data[i]);
244 std::memcpy(buf_.data() + offset + i * sizeof(double), &d, sizeof(d));
245 }
246 break;
247 }
249 size_t offset = buf_.size();
250 buf_.resize(offset + bpv);
251 // Use memcpy to avoid unaligned uint16_t writes (CWE-704)
252 for (size_t i = 0; i < dim; ++i) {
253 uint16_t h = f32_to_f16(data[i]);
254 std::memcpy(buf_.data() + offset + i * 2, &h, sizeof(h));
255 }
256 break;
257 }
258 }
259 ++num_vectors_;
260 }
261
264 inline bool add_batch(const float* data, size_t num_vectors) {
265 const size_t dim = spec_.dimension;
266 if (dim == 0 || num_vectors == 0) return true;
267 if (num_vectors > SIZE_MAX / dim) return false;
268 for (size_t i = 0; i < num_vectors; ++i) {
269 add(data + i * dim);
270 }
271 return true;
272 }
273
281 [[nodiscard]] inline std::vector<uint8_t> flush() {
282 std::vector<uint8_t> out = std::move(buf_);
283 buf_.clear();
284 num_vectors_ = 0;
285 return out;
286 }
287
289 [[nodiscard]] size_t num_vectors() const noexcept { return num_vectors_; }
290
292 [[nodiscard]] const VectorColumnSpec& spec() const noexcept { return spec_; }
293
300 [[nodiscard]] static inline ColumnDescriptor make_descriptor(
301 const std::string& name,
302 const VectorColumnSpec& spec) {
304 cd.name = name;
307 cd.type_length = static_cast<int32_t>(spec.bytes_per_vector());
308 return cd;
309 }
310
311private:
312 VectorColumnSpec spec_;
313 std::vector<uint8_t> buf_;
314 size_t num_vectors_ = 0;
315};
316
317// ===========================================================================
318// VectorReader — decodes FIXED_LEN_BYTE_ARRAY pages into float vectors
319// ===========================================================================
320
330public:
334 : spec_(spec) {}
335
342 [[nodiscard]] inline std::vector<std::vector<float>>
343 read_page(const uint8_t* data, size_t data_size) const {
344 const size_t bpv = spec_.bytes_per_vector();
345 const size_t dim = spec_.dimension;
346
347 if (bpv == 0) return {};
348
349 const size_t count = data_size / bpv;
350 std::vector<std::vector<float>> result;
351 result.reserve(count);
352
353 for (size_t i = 0; i < count; ++i) {
354 const uint8_t* src = data + i * bpv;
355 std::vector<float> vec(dim);
356
357 switch (spec_.element_type) {
359 std::memcpy(vec.data(), src, dim * sizeof(float));
360 break;
361 }
363 // Use memcpy to avoid unaligned double reads (CWE-704)
364 for (size_t j = 0; j < dim; ++j) {
365 double d;
366 std::memcpy(&d, src + j * sizeof(double), sizeof(d));
367 vec[j] = static_cast<float>(d);
368 }
369 break;
370 }
372 // Use memcpy to avoid unaligned uint16_t reads (CWE-704)
373 for (size_t j = 0; j < dim; ++j) {
374 uint16_t h;
375 std::memcpy(&h, src + j * 2, sizeof(h));
376 vec[j] = f16_to_f32(h);
377 }
378 break;
379 }
380 }
381 result.push_back(std::move(vec));
382 }
383 return result;
384 }
385
388 const float* data;
389 size_t num_vectors;
390 };
391
403 [[nodiscard]] inline expected<ZeroCopyResult>
404 read_page_zero_copy(const uint8_t* data, size_t data_size) const {
407 "zero-copy read requires FLOAT32 element type"};
408 }
409
410 // Check alignment (float requires 4-byte alignment)
411 if (reinterpret_cast<uintptr_t>(data) % alignof(float) != 0) {
413 "page data is not aligned for float access"};
414 }
415
416 const size_t bpv = spec_.bytes_per_vector();
417 if (bpv == 0) {
419 "vector dimension is zero"};
420 }
421 if (data_size % bpv != 0) {
423 "page size is not a multiple of bytes_per_vector"};
424 }
425
426 ZeroCopyResult result;
427 result.data = reinterpret_cast<const float*>(data);
428 result.num_vectors = data_size / bpv;
429 return result;
430 }
431
438 [[nodiscard]] inline std::vector<float>
439 read_vector(const uint8_t* page_data, size_t page_size, size_t index) const {
440 const size_t bpv = spec_.bytes_per_vector();
441 const size_t dim = spec_.dimension;
442
443 if (bpv == 0 || (index + 1) * bpv > page_size) {
444 return {};
445 }
446
447 const uint8_t* src = page_data + index * bpv;
448 std::vector<float> vec(dim);
449
450 switch (spec_.element_type) {
452 std::memcpy(vec.data(), src, dim * sizeof(float));
453 break;
454 }
456 // Use memcpy to avoid unaligned double reads (CWE-704)
457 for (size_t j = 0; j < dim; ++j) {
458 double d;
459 std::memcpy(&d, src + j * sizeof(double), sizeof(d));
460 vec[j] = static_cast<float>(d);
461 }
462 break;
463 }
465 // Use memcpy to avoid unaligned uint16_t reads (CWE-704)
466 for (size_t j = 0; j < dim; ++j) {
467 uint16_t h;
468 std::memcpy(&h, src + j * 2, sizeof(h));
469 vec[j] = f16_to_f32(h);
470 }
471 break;
472 }
473 }
474 return vec;
475 }
476
478 [[nodiscard]] const VectorColumnSpec& spec() const noexcept { return spec_; }
479
480private:
481 VectorColumnSpec spec_;
482};
483
484// ===========================================================================
485// SIMD vector utilities
486// ===========================================================================
487
497namespace simd {
498
499// ---------------------------------------------------------------------------
500// dot_product — inner product of two float vectors
501// ---------------------------------------------------------------------------
502
509inline float dot_product(const float* a, const float* b, size_t n) noexcept {
510 float sum = 0.0f;
511 size_t i = 0;
512
513#if defined(__AVX2__)
514 __m256 acc = _mm256_setzero_ps();
515 for (; i + 8 <= n; i += 8) {
516 __m256 va = _mm256_loadu_ps(a + i);
517 __m256 vb = _mm256_loadu_ps(b + i);
518 acc = _mm256_fmadd_ps(va, vb, acc);
519 }
520 // Horizontal sum of 8 floats
521 __m128 lo = _mm256_castps256_ps128(acc);
522 __m128 hi = _mm256_extractf128_ps(acc, 1);
523 __m128 s4 = _mm_add_ps(lo, hi);
524 __m128 s2 = _mm_add_ps(s4, _mm_movehl_ps(s4, s4));
525 __m128 s1 = _mm_add_ss(s2, _mm_movehdup_ps(s2));
526 sum = _mm_cvtss_f32(s1);
527
528#elif defined(__SSE4_2__) || defined(__SSE2__)
529 __m128 acc = _mm_setzero_ps();
530 for (; i + 4 <= n; i += 4) {
531 __m128 va = _mm_loadu_ps(a + i);
532 __m128 vb = _mm_loadu_ps(b + i);
533 acc = _mm_add_ps(acc, _mm_mul_ps(va, vb));
534 }
535 // Horizontal sum of 4 floats
536 __m128 shuf = _mm_movehl_ps(acc, acc);
537 __m128 sums = _mm_add_ps(acc, shuf);
538 shuf = _mm_shuffle_ps(sums, sums, _MM_SHUFFLE(0,0,0,1)); // SSE1 (no SSE3)
539 sums = _mm_add_ss(sums, shuf);
540 sum = _mm_cvtss_f32(sums);
541
542#elif defined(__ARM_NEON) || defined(__ARM_NEON__)
543 float32x4_t acc = vdupq_n_f32(0.0f);
544 for (; i + 4 <= n; i += 4) {
545 float32x4_t va = vld1q_f32(a + i);
546 float32x4_t vb = vld1q_f32(b + i);
547 acc = vmlaq_f32(acc, va, vb);
548 }
549 // Horizontal sum
550 float32x2_t pair = vadd_f32(vget_low_f32(acc), vget_high_f32(acc));
551 pair = vpadd_f32(pair, pair);
552 sum = vget_lane_f32(pair, 0);
553#endif
554
555 // Scalar tail
556 for (; i < n; ++i) {
557 sum += a[i] * b[i];
558 }
559 return sum;
560}
561
562// ---------------------------------------------------------------------------
563// l2_distance_sq — squared Euclidean distance
564// ---------------------------------------------------------------------------
565
572inline float l2_distance_sq(const float* a, const float* b, size_t n) noexcept {
573 float sum = 0.0f;
574 size_t i = 0;
575
576#if defined(__AVX2__)
577 __m256 acc = _mm256_setzero_ps();
578 for (; i + 8 <= n; i += 8) {
579 __m256 va = _mm256_loadu_ps(a + i);
580 __m256 vb = _mm256_loadu_ps(b + i);
581 __m256 diff = _mm256_sub_ps(va, vb);
582 acc = _mm256_fmadd_ps(diff, diff, acc);
583 }
584 __m128 lo = _mm256_castps256_ps128(acc);
585 __m128 hi = _mm256_extractf128_ps(acc, 1);
586 __m128 s4 = _mm_add_ps(lo, hi);
587 __m128 s2 = _mm_add_ps(s4, _mm_movehl_ps(s4, s4));
588 __m128 s1 = _mm_add_ss(s2, _mm_movehdup_ps(s2));
589 sum = _mm_cvtss_f32(s1);
590
591#elif defined(__SSE4_2__) || defined(__SSE2__)
592 __m128 acc = _mm_setzero_ps();
593 for (; i + 4 <= n; i += 4) {
594 __m128 va = _mm_loadu_ps(a + i);
595 __m128 vb = _mm_loadu_ps(b + i);
596 __m128 diff = _mm_sub_ps(va, vb);
597 acc = _mm_add_ps(acc, _mm_mul_ps(diff, diff));
598 }
599 __m128 shuf = _mm_movehl_ps(acc, acc);
600 __m128 sums = _mm_add_ps(acc, shuf);
601 shuf = _mm_shuffle_ps(sums, sums, _MM_SHUFFLE(0,0,0,1)); // SSE1 (no SSE3)
602 sums = _mm_add_ss(sums, shuf);
603 sum = _mm_cvtss_f32(sums);
604
605#elif defined(__ARM_NEON) || defined(__ARM_NEON__)
606 float32x4_t acc = vdupq_n_f32(0.0f);
607 for (; i + 4 <= n; i += 4) {
608 float32x4_t va = vld1q_f32(a + i);
609 float32x4_t vb = vld1q_f32(b + i);
610 float32x4_t diff = vsubq_f32(va, vb);
611 acc = vmlaq_f32(acc, diff, diff);
612 }
613 float32x2_t pair = vadd_f32(vget_low_f32(acc), vget_high_f32(acc));
614 pair = vpadd_f32(pair, pair);
615 sum = vget_lane_f32(pair, 0);
616#endif
617
618 for (; i < n; ++i) {
619 float d = a[i] - b[i];
620 sum += d * d;
621 }
622 return sum;
623}
624
625// ---------------------------------------------------------------------------
626// sum_of_squares — for norm computation
627// ---------------------------------------------------------------------------
628
634inline float sum_of_squares(const float* data, size_t n) noexcept {
635 float sum = 0.0f;
636 size_t i = 0;
637
638#if defined(__AVX2__)
639 __m256 acc = _mm256_setzero_ps();
640 for (; i + 8 <= n; i += 8) {
641 __m256 v = _mm256_loadu_ps(data + i);
642 acc = _mm256_fmadd_ps(v, v, acc);
643 }
644 __m128 lo = _mm256_castps256_ps128(acc);
645 __m128 hi = _mm256_extractf128_ps(acc, 1);
646 __m128 s4 = _mm_add_ps(lo, hi);
647 __m128 s2 = _mm_add_ps(s4, _mm_movehl_ps(s4, s4));
648 __m128 s1 = _mm_add_ss(s2, _mm_movehdup_ps(s2));
649 sum = _mm_cvtss_f32(s1);
650
651#elif defined(__SSE4_2__) || defined(__SSE2__)
652 __m128 acc = _mm_setzero_ps();
653 for (; i + 4 <= n; i += 4) {
654 __m128 v = _mm_loadu_ps(data + i);
655 acc = _mm_add_ps(acc, _mm_mul_ps(v, v));
656 }
657 __m128 shuf = _mm_movehl_ps(acc, acc);
658 __m128 sums = _mm_add_ps(acc, shuf);
659 shuf = _mm_shuffle_ps(sums, sums, _MM_SHUFFLE(0,0,0,1)); // SSE1 (no SSE3)
660 sums = _mm_add_ss(sums, shuf);
661 sum = _mm_cvtss_f32(sums);
662
663#elif defined(__ARM_NEON) || defined(__ARM_NEON__)
664 float32x4_t acc = vdupq_n_f32(0.0f);
665 for (; i + 4 <= n; i += 4) {
666 float32x4_t v = vld1q_f32(data + i);
667 acc = vmlaq_f32(acc, v, v);
668 }
669 float32x2_t pair = vadd_f32(vget_low_f32(acc), vget_high_f32(acc));
670 pair = vpadd_f32(pair, pair);
671 sum = vget_lane_f32(pair, 0);
672#endif
673
674 for (; i < n; ++i) {
675 sum += data[i] * data[i];
676 }
677 return sum;
678}
679
680// ---------------------------------------------------------------------------
681// l2_normalize — normalize a vector to unit length in-place
682// ---------------------------------------------------------------------------
683
690inline void l2_normalize(float* data, size_t n) noexcept {
691 const float norm_sq = sum_of_squares(data, n);
692 if (norm_sq == 0.0f) return;
693
694 const float inv_norm = 1.0f / std::sqrt(norm_sq);
695 size_t i = 0;
696
697#if defined(__AVX2__)
698 __m256 scale = _mm256_set1_ps(inv_norm);
699 for (; i + 8 <= n; i += 8) {
700 __m256 v = _mm256_loadu_ps(data + i);
701 _mm256_storeu_ps(data + i, _mm256_mul_ps(v, scale));
702 }
703
704#elif defined(__SSE4_2__) || defined(__SSE2__)
705 __m128 scale = _mm_set1_ps(inv_norm);
706 for (; i + 4 <= n; i += 4) {
707 __m128 v = _mm_loadu_ps(data + i);
708 _mm_storeu_ps(data + i, _mm_mul_ps(v, scale));
709 }
710
711#elif defined(__ARM_NEON) || defined(__ARM_NEON__)
712 float32x4_t scale = vdupq_n_f32(inv_norm);
713 for (; i + 4 <= n; i += 4) {
714 float32x4_t v = vld1q_f32(data + i);
715 vst1q_f32(data + i, vmulq_f32(v, scale));
716 }
717#endif
718
719 for (; i < n; ++i) {
720 data[i] *= inv_norm;
721 }
722}
723
724// ---------------------------------------------------------------------------
725// copy_floats — fast memcpy for float arrays
726// ---------------------------------------------------------------------------
727
736inline void copy_floats(float* dst, const float* src, size_t n) noexcept {
737 size_t i = 0;
738
739#if defined(__AVX2__)
740 for (; i + 8 <= n; i += 8) {
741 __m256 v = _mm256_loadu_ps(src + i);
742 _mm256_storeu_ps(dst + i, v);
743 }
744
745#elif defined(__SSE4_2__) || defined(__SSE2__)
746 for (; i + 4 <= n; i += 4) {
747 __m128 v = _mm_loadu_ps(src + i);
748 _mm_storeu_ps(dst + i, v);
749 }
750
751#elif defined(__ARM_NEON) || defined(__ARM_NEON__)
752 for (; i + 4 <= n; i += 4) {
753 float32x4_t v = vld1q_f32(src + i);
754 vst1q_f32(dst + i, v);
755 }
756#endif
757
758 // Scalar tail (also handles the full copy on platforms without SIMD)
759 if (i < n) {
760 std::memcpy(dst + i, src + i, (n - i) * sizeof(float));
761 }
762}
763
764} // namespace simd
765
766// ===========================================================================
767// SchemaBuilder extension — add_vector_column
768// ===========================================================================
769
789 SchemaBuilder& builder,
790 const std::string& name,
791 uint32_t dimension,
793 VectorColumnSpec spec{dimension, elem};
794 return builder.raw_column(VectorWriter::make_descriptor(name, spec));
795}
796
797} // namespace signet::forge
Fluent builder for constructing a Schema one column at a time.
Definition schema.hpp:92
SchemaBuilder & raw_column(ColumnDescriptor cd)
Add a pre-built ColumnDescriptor directly.
Definition schema.hpp:168
Reads FIXED_LEN_BYTE_ARRAY page data back into float vectors.
std::vector< float > read_vector(const uint8_t *page_data, size_t page_size, size_t index) const
Read a single vector at the given index from a page.
VectorReader(VectorColumnSpec spec)
Construct a VectorReader for the given column specification.
const VectorColumnSpec & spec() const noexcept
The column spec this reader was constructed with.
std::vector< std::vector< float > > read_page(const uint8_t *data, size_t data_size) const
Decode a PLAIN-encoded page of FIXED_LEN_BYTE_ARRAY vectors into float32 vectors.
expected< ZeroCopyResult > read_page_zero_copy(const uint8_t *data, size_t data_size) const
Attempt a zero-copy read of a FLOAT32 page.
Buffers float vectors and encodes them as FIXED_LEN_BYTE_ARRAY PLAIN data.
void add(const float *data)
Add a single vector from a float32 pointer (must point to dimension floats).
std::vector< uint8_t > flush()
Flush the buffered vectors and return the encoded page bytes.
const VectorColumnSpec & spec() const noexcept
The column spec this writer was constructed with.
VectorWriter(VectorColumnSpec spec)
Construct a VectorWriter for the given column specification.
static ColumnDescriptor make_descriptor(const std::string &name, const VectorColumnSpec &spec)
Create a ColumnDescriptor for a vector column with the given name and spec.
size_t num_vectors() const noexcept
Number of vectors currently buffered (since last flush).
bool add_batch(const float *data, size_t num_vectors)
Add a batch of vectors (num_vectors vectors, each dimension elements, row-major).
A lightweight result type that holds either a success value of type T or an Error.
Definition error.hpp:145
void copy_floats(float *dst, const float *src, size_t n) noexcept
Fast copy of n floats from src to dst.
float sum_of_squares(const float *data, size_t n) noexcept
Compute the sum of squares of a float vector.
void l2_normalize(float *data, size_t n) noexcept
L2-normalize a float vector in-place (divide each element by the L2 norm).
float dot_product(const float *a, const float *b, size_t n) noexcept
Compute the dot product (inner product) of two float vectors.
float l2_distance_sq(const float *a, const float *b, size_t n) noexcept
Compute the squared L2 (Euclidean) distance between two float vectors.
VectorElementType
Specifies the numerical precision of each element within a vector column.
@ FLOAT64
IEEE 754 double-precision (8 bytes per element)
@ FLOAT32
IEEE 754 single-precision (4 bytes per element)
@ FLOAT16
IEEE 754 half-precision (2 bytes per element)
@ FIXED_LEN_BYTE_ARRAY
Fixed-length byte array (UUID, vectors, decimals).
float f16_to_f32(uint16_t h) noexcept
Convert a 16-bit IEEE 754 half-precision value to a 32-bit float.
SchemaBuilder & add_vector_column(SchemaBuilder &builder, const std::string &name, uint32_t dimension, VectorElementType elem=VectorElementType::FLOAT32)
Add a vector column to a SchemaBuilder.
@ FLOAT32_VECTOR
ML embedding vector — FIXED_LEN_BYTE_ARRAY(dim*4).
uint16_t f32_to_f16(float val) noexcept
Convert a 32-bit float to a 16-bit IEEE 754 half-precision value.
@ UNSUPPORTED_TYPE
The file contains a Parquet physical or logical type that is not implemented.
@ SCHEMA_MISMATCH
The requested column name or type does not match the file schema.
@ INTERNAL_ERROR
An unexpected internal error that does not fit any other category.
@ CORRUPT_PAGE
A data page failed integrity checks (bad CRC, truncated, or exceeds size limits).
@ FLOAT64
IEEE 754 double-precision (8 bytes)
@ FLOAT32
IEEE 754 single-precision (4 bytes)
@ FLOAT16
IEEE 754 half-precision (2 bytes)
Schema definition types: Column<T>, SchemaBuilder, and Schema.
Descriptor for a single column in a Parquet schema.
Definition types.hpp:152
int32_t type_length
Byte length for FIXED_LEN_BYTE_ARRAY columns (-1 = N/A).
Definition types.hpp:157
LogicalType logical_type
Semantic annotation (STRING, TIMESTAMP_NS, etc.).
Definition types.hpp:155
std::string name
Column name (unique within a schema).
Definition types.hpp:153
PhysicalType physical_type
On-disk storage type.
Definition types.hpp:154
Lightweight error value carrying an ErrorCode and a human-readable message.
Definition error.hpp:101
Configuration for a vector column: dimensionality and element precision.
uint32_t dimension
Number of elements per vector.
constexpr size_t element_size() const noexcept
Returns the byte size of one element (2, 4, or 8).
VectorElementType element_type
Element precision.
constexpr size_t bytes_per_vector() const noexcept
Returns the total byte size of one vector (dimension * element_size).
Zero-copy read result: a pointer to the float data and the vector count.
const float * data
Pointer to the first float of the first vector.
size_t num_vectors
Number of complete vectors in the page.
Parquet format enumerations, type traits, and statistics structs.