turbovec API 完全指南:TurboQuantIndex、IdMapIndex、TQ+ 校准与文件格式详解
turbovec API 完全指南TurboQuantIndex、IdMapIndex、TQ 校准与文件格式详解【免费下载链接】turbovecA vector index built on TurboQuant, written in Rust with Python bindings项目地址: https://gitcode.com/GitHub_Trending/tu/turbovec导读本文是 turbovec基于 TurboQuant 的 Rust 向量索引附带 Python 绑定的完整 API 参考指南对应仓库中的 docs/api.md。turbovec 提供两种索引类型位置索引TurboQuantIndex与稳定 ID 包装IdMapIndex、TQ 逐坐标校准生命周期、.tv/.tvim/ v7 增量容器三类文件格式以及与之配套的内存序列化与持久化语义。读完本文后你将掌握两类索引的构造与操作细节、过滤mask / allowlist的正确使用方式、校准对召回率的真实影响边界、文件格式的字节级布局与版本兼容规则以及如何在 Python 与 Rust 两种 API 之间对照使用。一、索引类型总览turbovec 对外暴露两种索引类型和每类索引各一种序列化格式类型说明删除语义TurboQuantIndex位置索引向量由插入槽位0..n标识O(1)swap_remove删除IdMapIndex基于TurboQuantIndex的稳定外部u64ID 包装O(1) 按 ID 删除remove(id)配套能力包括 TQ 逐坐标校准生命周期、.tv与.tvim文件格式以及 增量保存sync()。所有示例默认使用 Python APIRust API 与之高度对应个别例外会在文中标注精确签名请查阅各类型的 rustdoc。从 Python 绑定看两个类型直接从原生模块导出turbovec-python/python/turbovec/init.py模块还暴露BATCH_CHUNK_SIZE 4096默认批量分片大小与惰性解析的__version__。Rust 侧类型定义位于 turbovec/src/lib.rs 与 turbovec/src/id_map.rs。二、TurboQuantIndex位置索引每个向量由其插入槽位0..n标识体积小、速度快但swap_remove会使外部对槽位的引用失效。需要稳定 ID 时请使用IdMapIndex。from turbovec import TurboQuantIndex idx TurboQuantIndex(dim1536, bit_width4) idx.add(vectors) # np.ndarray of shape (n, dim), float32 scores, indices idx.search(queries, k10) idx.swap_remove(5) # O(1)原最后一个向量移入槽位 5 idx.write(index.tv) # .tv 格式 loaded TurboQuantIndex.load(index.tv)dim是可选的。省略时索引会从第一批向量自动推断维度idx TurboQuantIndex(bit_width4) # dim 在首次 add 时推断 idx.add(vectors) # 将 dim 锁定为 vectors.shape[1]在首次add之前idx.dim为Nonelen(idx)为0search()返回空结果。添加零行批次是空操作——dim仍会对照批次校验但惰性索引保持惰性其序列化字节不变。在 Rust API 上dim_opt()等价于idx.dim并返回Optionusizedim()已废弃——它返回usize惰性索引下为0做算术不安全所以凡是可能看到惰性索引的路径都应使用dim_opt()。方法一览方法说明TurboQuantIndex(dimNone, bit_width4)bit_width ∈ {2, 3, 4}。dim必须是 8 的正倍数且≤ 16384MAX_DIM。dim可选省略时从首次add推断。add(vectors)vectors是形状(n, dim)的连续 float32 数组。惰性索引首次调用锁定dim后续调用必须匹配。维度不匹配、零宽0 列批次、任一坐标非有限NaN/Inf或\|value\| ≥ 1e16时抛出ValueError。L2 范数 ≤1e-10的向量没有可表示的朝向会以 scale 0 存储对所有查询得 0 分。Rust API 上惰性索引首次 add 必须用add_2d(vectors, dim)——扁平add([f32])要求已提交 dim否则 panicPython 数组自带形状此限制仅适用于 Rust。search(queries, k, *, maskNone)返回(scores, indices)形状均为(nq, effective_k)。indices 是int64槽位。mask是可选的bool数组长度len(idx)给定后只有mask[i] True的槽位参与。effective_k min(k, mask.sum())。查询坐标非有限或\|value\| ≥ 1e16时抛出ValueError。返回的 id 对查询乘以任意正常数保持不变只要缩放后坐标仍在 float32 正常范围内最小正常数1.18e-38。查询缩放到坐标进入 subnormal 时相对精度丢失、排序可能变化——实测dim256时查询量级约~1e-36、dim768约~1e-35远低于任何真实 embedding。分数是内积随查询缩放。swap_remove(idx)O(1)。将最后一个向量移入idx返回被移向量的原位置便于更新外部引用。prepare()可选。提前构建旋转矩阵、Lloyd-Max 质心与 SIMD 分块布局避免首次search支付一次性成本。对未见首次 add 的惰性索引为空操作。sync(path)增量保存只写入上次 sync 以来变化的内容到同一路径。见增量保存。load(path)可读两种格式。write(path, *, durableTrue)/load(path).tv格式。durableFalse跳过原子重命名前的 fsync——更快但断电可能丢文件。durableTrue保存时若重命名后的目录 fsync 失败保存仍成功文件已提交且可见并抛出RuntimeWarning提示重命名可能无法承受断电。Rust API 上这不是标志位请使用write_with_durability(path, io::Durability::Fast \| Durable)。to_bytes()/from_bytes(data)内存.tv序列化。见内存序列化。pickle/copy.copy/copy.deepcopy两类索引均通过__reduce__支持见内存序列化。索引还支持弱引用可缓存在weakref.WeakValueDictionary中。len(idx)/idx.dim/idx.bit_width内省。idx.dim在提交后返回int惰性索引在首次 add 前返回None。idx.calibration_stateTQ 校准状态uncalibrated或calibrated。见TQ 校准。swap_remove语义swap_remove(i)命名对齐 Rust 的Vec::swap_remove最后一个元素移入槽位i向量截断一位。它不是平移——i之后的槽位不会整体下移。顺序不保持未删除向量的槽位索引可能指向与之前不同的向量。需要跨删除保持外部引用稳定时使用IdMapIndex。搜索掩码也是外部引用——参见掩码会被任何变更失效。从原始部件低层构造Rust已在内存中持有索引载荷例如从数据库页读出、而非从.tv文件读出的 Rust 嵌入者可用TurboQuantIndex::from_parts直接从解码后的字段构造索引跳过文件往返let index TurboQuantIndex::from_parts( dim_opt, // OptionusizeSome(dim) 已提交或 None 表示惰性 bit_width, // 2、3 或 4 n_vectors, packed_codes, // Vecu8 scales, // Vecf32 tqplus_shift, // Vecf32长度 dim或空 恒等 tqplus_scale, // Vecf32长度 dim或空 恒等 )?;它是原始部件构造的唯一校验入口每个结构不变量只检查一次任何违反都返回具名FromPartsErrorbit_width 范围、dim 为 8 的正倍数且≤ 16384、packed_codes/scales/ TQ 长度及溢出检查的大小计算、惰性状态约束、与文件加载器相同的值级校验——逐向量 scale 有限非负、TQ shift 有限、TQ scale 有限为正而非 panic 或越界读取。被from_parts接受的索引必然能通过自身的write→load往返。配套访问器packed_codes()、scales()、tqplus_shift()、tqplus_scale()、bit_width()、dim_opt()与len()返回它消费的字段索引可以经过你自己的存储格式往返。逐坐标的encode/pack/search/codebook内核是 crate 内部实现——from_parts才是受支持的底层 API。仅 RustPython 绑定使用write/load。从源码看from_parts的校验顺序与文件加载器镜像turbovec/src/lib.rs先检查bit_width防范1 bit_width造成的无界分配再检查 TQ 两数组长度一致随后在Some(dim)分支校验d 0 || d % 8 ! 0、d MAX_DIM并用 checked 乘法计算期望的packed_codes长度防止usize溢出。这些都与加载路径完全一致。三、IdMapIndex稳定 ID 索引围绕TurboQuantIndex的稳定 ID 包装哈希表支撑的u64 id ↔ slot映射O(1)remove(id)。删除向量时槽位索引仍会移动但 ID 不变。import numpy as np from turbovec import IdMapIndex idx IdMapIndex(dim1536, bit_width4) idx.add_with_ids(vectors, np.array([1001, 1002, 1003], dtypenp.uint64)) scores, ids idx.search(queries, k10) # ids 是 uint64 外部 id idx.remove(1002) # O(1) 按 id 删除 assert 1003 in idx # __contains__ 语法糖 idx.write(index.tvim) # .tvim 格式 loaded IdMapIndex.load(index.tvim)与TurboQuantIndex一样dim可选从首次add_with_ids调用推断idx IdMapIndex(bit_width4) # dim 在首次 add 时推断 idx.add_with_ids(vectors, ids) # 将 dim 锁定为 vectors.shape[1]方法一览方法说明IdMapIndex(dimNone, bit_width4)bit_width ∈ {2, 3, 4}dim必须是 8 的正倍数且≤ 16384。dim可选省略时从首次add_with_ids推断。add_with_ids(vectors, ids)ids是长度vectors.shape[0]的uint64数组。惰性索引首次调用锁定dim。维度不匹配、重复 id、len(ids) ! vectors.shape[0]、零宽批次、非有限 /\|value\| ≥ 1e16坐标时抛出ValueError。Rust API 上惰性索引首次 add 必须用add_with_ids_2d(vectors, dim, ids)——扁平add_with_ids要求已提交 dim否则 panic仅 RustPython 数组自带形状。remove(id) - boolid 存在且被删除返回True否则False。O(1)。search(queries, k, *, allowlistNone)返回(scores, ids)——ids是uint64外部 id。allowlist是可选的uint64id 数组给定后结果限制在这些 id 内effective_k min(k, allowlist 中去重后的 id 数)allowlist 会去重重复 id 不会扩大结果。空 allowlist 或非有限 /\|value\| ≥ 1e16查询坐标时抛出ValueError未知 id 抛出KeyError。Rust API 上search_with_allowlist返回Result(Vecf32, Vecu64), SearchError上述每种情况都以Err报告AllowlistEmpty、UnknownId(id)以及查询形状对QueryBufferNotMultipleOfDim/InvalidQueryValue。无 allowlist 的search直接返回元组且是 panic 形式——两种查询形状条件下以相同消息 re-panic。想要行数与effectivek而非裸元组的 Rust 调用者使用try_search/try_search_with_allowlist返回ResultIdSearchResults, SearchError——即SearchResults的 id 空间对应物带scores、ids、nq、k及scores_for_query/ids_for_query行访问器。contains(id)/id in idx成员判断。sync(path)增量保存只写入上次 sync 以来变化的内容到同一路径。见增量保存。load(path)可读两种格式。write(path, *, durableTrue)/load(path).tvim格式。durableFalse跳过原子重命名前的 fsync——更快但断电可能丢文件。durableTrue保存时若重命名后的目录 fsync 失败保存仍成功并抛出RuntimeWarning。Rust API 使用write_with_durability(path, io::Durability::Fast \| Durable)。to_bytes()/from_bytes(data)内存.tvim序列化。见内存序列化。pickle/copy.copy/copy.deepcopy同TurboQuantIndex。len(idx)/idx.dim/idx.bit_width/idx.calibration_state同TurboQuantIndex。prepare()同TurboQuantIndex.prepare()并额外预热惰性的id - slot映射使加载后首次search(..., allowlist)、contains()或remove()不必再付一次性 O(n) 构建成本。何时选用哪种TurboQuantIndex——你从不删除或可以接受位置 ID。IdMapIndex——你需要稳定的外部 ID例如调用方维护的字符串 ID → 向量映射。所有框架集成LangChain、LlamaIndex、Haystack内部都为此使用IdMapIndex。对应实现与测试可参考 turbovec/src/id_map.rs 及 turbovec/tests/filtering.rs后者验证掩码/allowlist 过滤的正确性与内核一致性。四、TQ 校准TQ 从向量样本的经验分位数拟合每坐标(shift, scale)对所有存储向量都在这一个已校准坐标系下编码。它在平均数据上约带来 2.5 点 R10 提升在最各向异性的实测数据上最高约 8.7。校准只来自一个明确调用idx.calibrate(sample)Rust 为calibrate/calibrate_2d。索引绝不会自行拟合——从未校准的索引就是纯 TurboQuant任何位置都没有拟合状态其编码字节与 add 的分批或顺序无关。idx.calibration_state报告索引处于哪种状态状态含义uncalibrated未提交校准。功能完整只是没有 TQ 的召回增益。calibrated已提交校准每一行都按该校准编码——包括在calibrate调用之前添加的行该调用会重编码它们。样本是你的责任。calibrate会使用你给它的每一行。大约 1024 行在多数实测语料上能达到对全量语料拟合的 R10 半分之差内2048 行在所有语料上都能做到——但它必须是索引将容纳向量的有代表性、随机样本。同样大小的排序或聚类前缀会拟合出偏移且过窄的分位数反而主动破坏召回。传入整个语料总是安全的。calibrate可随时、反复调用。在已填充索引上它会从已存编码重编码每一行——无需原始向量。注意重编码能做到与不能做到什么用相同或邻近的配对重新拟合是廉价的编码会达到精确不动点。在大量未校准摄取之后校准会比先校准再添加损失几分召回重编码是第二次量化。尽量先校准再添加。严重偏置的早期校准无法通过重新拟合修复过窄的拟合在编码时已把坐标裁剪到外部质心任何后续配对都无法恢复被裁剪破坏的信息。需从源向量重建。校准在write/load、to_bytes/from_bytes、pickle 与 copy 上精确往返两类索引皆然。将索引清空到零向量仍保留其已提交校准。从源码结构看calibration_state的判断方式是检查 TQ shift 数组是否为空——normalize_calibration会在所有构造路径上把无效配对折叠为空数组calibrate拒绝提交空配对因此空即未校准turbovec/src/lib.rs。重编码路径则逐块解码旧编码、重建坐标与范数后重新编码同文件L2790-L2830区域这正是无需原始向量的实现基础。值得注意的是Python 绑定的批量分片逻辑会确保分片不跨校准边界避免在校准中途改变索引行为见 turbovec-python/python/turbovec/_interruptible.py。五、过滤与掩码的陷阱两种索引类型都支持把返回的 top-k限制到调用方提供的向量子集。与后置过滤先搜再丢弃不同内核不会把不允许的向量插入逐查询堆所以你总是从允许集合拿到至多k个结果而非更少。# IdMapIndex —— 外部 id 的 allowlist典型用法 allowed np.array([1003, 1010, 1042], dtypenp.uint64) scores, ids idx.search(queries, k10, allowlistallowed) # scores.shape (nq, min(k, n_allowed)) (nq, 3) # 3 个去重后的允许 id # TurboQuantIndex —— 槽位的 bool 掩码 mask np.ones(len(idx), dtypebool) mask[disabled_slots] False scores, slots idx.search(queries, k10, maskmask)输出形状是(nq, min(k, n_allowed))其中n_allowed是不同允许向量的数量——allowlist 中去重后的 id 数或掩码的mask.sum()——与k len(idx)时的收缩行为一致。没有-1/NaN填充需要定宽批次时请自行在调用方填充。掩码会被任何变更失效不只是改变长度的掩码命名的是槽位而swap_remove会重编号槽位——所以任何变更都会使掩码失效包括保持len(idx)不变的变更。每次变更后都要重建掩码。长度检查保护不了你。它只捕获大小差异ValueError: mask length 100 does not match index size 99一次swap_remove(i)add(...)组合能恢复原始长度却让不同的向量留在槽位i所以此前构建的掩码能通过校验随后静默选择了与你意图不同的向量集合。索引外部不会泄漏任何信息、也不抛错——只是选错了集合。IdMapIndex的 allowlist 没有这种失效模式因为它命名外部 id索引从不重编号 id。针对已删除 id 的 allowlist 条目会抛出KeyErrorRustSearchError::UnknownId而不是静默解析到其他向量。allowlist 条目唯一可能指到不同向量的方式是你自己用不同数据重新添加同一个 id。常见用例混合检索SQL/BM25 阶段产出候选 id 集合。访问控制或多租户查询只返回调用方可见的 id。时间窗口搜索例如只返回最近 7 天的文档。对应的内核一致性测试见 turbovec/tests/filtering.rs其不变量包括掩码搜索与先全量搜索再按允许集合后置过滤的 top-k 一致、mask None与全真掩码结果一致、effective_k收缩、错误路径的 panic/Err 行为等。六、文件格式与加载.tv——TurboQuantIndex┌───────────────────────────────────────────┐ │ magic TVPI (4 bytes) │ │ version u8 6 │ ├───────────────────────────────────────────┤ │ core header │ │ bit_width (u8) │ │ dim (u32 LE) │ │ n_vectors (u64 LE) │ ├───────────────────────────────────────────┤ │ Lloyd-Max codebook │ │ boundaries ((2^bit_width − 1) × f32 LE)│ │ centroids (2^bit_width × f32 LE) │ ├───────────────────────────────────────────┤ │ codes — sequential blocked layout │ │ n_byte_groups dim / (8 / bit_width) │ │ ceil(n_vectors / 32) │ │ × n_byte_groups × 32 bytes │ ├───────────────────────────────────────────┤ │ scales (n_vectors × f32 LE) │ │ per-vector length-renormalization │ ├───────────────────────────────────────────┤ │ TQ trailer │ │ n_calib (u32 LE) — 0 or dim │ │ shift (n_calib × f32 LE) │ │ scale (n_calib × f32 LE) │ └───────────────────────────────────────────┘代码载荷按 32 向量一组分块并补齐到整块磁盘上宽为ceil(n_vectors / 32) * 32个向量。bit_width 3时一个字节只容纳两个码而非 8/3使载荷比dim * bit_width / 8每向量的估算大约 33%。.tvim——IdMapIndex┌───────────────────────────────────────────┐ │ magic TVIM (4 bytes) │ │ version u8 6 │ ├───────────────────────────────────────────┤ │ core payload (same as .tv: header │ │ codebook codes scales TQ) │ ├───────────────────────────────────────────┤ │ slot_to_id (n_vectors × u64 LE) │ └───────────────────────────────────────────┘加载时反向的id → slot映射在内存中重建。slot_to_id表中的重复 id 会被判为损坏而拒绝。加载性能文件以搜索内核直接消费的架构中立顺序分块布局存储代码并附带 Lloyd-Max codebook因此加载直接为搜索缓存播种没有 O(n·dim) 重打包首次搜索也不需要求解 codebook。非 x86 直接使用存储布局x86 在加载时做一次廉价的块内 nibble 交错线程化 SIMD 扫描77 MB 索引约 2 ms。旋转是确定性的从dim重建耗时远低于 1 毫秒。存储的索引可以跨平台 加载 → 重新保存 字节级一致格式本身不引入平台依赖。七、内存序列化两种索引类型都在内存中反序列化其线上格式无需文件系统往返payload idx.to_bytes() # bytes与 write(path) 的文件字节级一致 restored IdMapIndex.from_bytes(payload) # 对 write() 文件应用与 load(path) 相同的校验to_bytes()返回的正是write(path)会写入文件的字节TurboQuantIndex为.tvIdMapIndex为.tvim。from_bytes(data)接受bytes或bytearray并应用与load对write()文件完全相同的校验——版本处理、结构级与值级检查、内嵌 codebook 检查v6 文件携带 Lloyd-Max codebook若文件 codebook 对其(bit_width, dim)无效则被拒绝——对任何通过原始io::*写入器手写文件的人都相关、.tvim重复 id 检查——损坏载荷抛ValueError没有文件可归责所以不是OSError。两者都释放 GIL。这是缓存与数据库列应走的路径也是两类索引自身pickle/copy支持及集成存储所基于的机制。pickle.dumps(idx)、copy.copy(idx)与copy.deepcopy(idx)对两类索引都可用——它们都归结为from_bytes(to_bytes())因此索引可以跨multiprocessing的spawn边界macOS 与 Windows 的默认启动方式包含索引的容器也可以被深拷贝。拷贝与原对象完全独立。to_bytes的一切性质都适用于 pickle——特别是校准状态精确往返。相等性与哈希保持基于身份所以idx pickle.loads(pickle.dumps(idx))为False即使两者持有相同向量。要检查保存与加载的索引是否一致请比较to_bytes()载荷。索引未定义__bool__真值判定回退到__len__不含向量的索引是假值idx idx or build_index()会丢弃一个完全正常的空索引。请用idx is None判断索引是否存在用len(idx)判断内容。索引也不接受用户属性idx.tag x抛AttributeError不可被继承class Sub(IdMapIndex): ...抛TypeError。两者都是有意为之实例__dict__不被垃圾回收器遍历穿过属性的引用环会泄漏整个索引属性也会被pickle/copy静默丢弃它们经由from_bytes(to_bytes())只携带载荷。子类实例同样会 pickle / copy 回基类静默改变类型。请通过你自己的对象持有索引来附加每索引状态。对已构建索引重新调用idx.__init__(dim...)完全无效——既不重置也不重塑——请新建索引而非试图原地重配。Rust API 上同样有to_bytes()/from_bytes([u8])以及两类索引的泛型流write_to_writerW: Write/load_from_readerR: Read。四者都搬运文件写入器产生的同一 v7 镜像to_bytes()与文件write()留在磁盘的字节一致write_to_writer按单元流式写出而非在内存中构建第二份副本。v7 成为唯一格式后模块级原始io::write*/io::load*入口已移除——请构建索引并使用其自身方法。Rust API 的TurboQuantIndex::serialized_len()返回to_bytes()将返回且write(path)将写入文件的精确字节数只凭索引几何计算、不序列化任何东西——用于预先给缓冲区、数据库列或配额检查定容。它是精确值而非上界to_bytes()也用它一次性分配缓冲区。八、增量保存 ——sync()write(path)重写整个文件。sync(path)只写入上次 sync 以来变化的内容到同一路径使用专为频繁小提交构建的第二种容器格式.tv/.tvimmagicTV7\0idx.sync(index.tv) # 对全新路径的首次 sync写入整个容器 idx.add(more_vectors) idx.swap_remove(3) idx.sync(index.tv) # 写入增量并提交 reloaded TurboQuantIndex.load(index.tv) # load() 能识别两种格式加载后的索引保持与来源路径的绑定因此会持续增量向前 sync 而非重写。成本是什么。追加写入新的 32 行块加一个提交头。删除不写任何块——它作为 redo 操作搭在提交头上后续 sync 会把它折叠进自己的块。整文件事件是显式calibrate()重新拟合会重编码每个已存编码与累计删除超过头的操作容量两者都通过write()使用的同一条临时文件 重命名路径整文件压缩。持久性。每次 sync 返回时都已持久——没有快速模式fsync 是sync_all而非仅数据的变体。任意字节处崩溃都保留上一次提交完好文件携带两个交替提交头撕裂的头校验失败后采用另一个每个头还列出自己 sync 写入的块及其摘要因此先于数据到达磁盘的提交会被检测到而非被误用。见版本与限制了解这不覆盖什么——写入之后到来的损坏。每路径单写入者。每次完整写入给文件盖一个随机 nonce因此若另一进程在绑定索引下方替换了文件下一次sync会报告而非覆盖它们的提交。不支持两进程并发 sync 同一路径该检查让不支持的场景响亮而非安全。sync()文件只能按路径读。from_bytes与load_from_reader只读write()格式。v7 容器需要随机访问——两个头槽、固定步长块单元、redo 操作——字节流无法满足to_bytes()也只会输出write()格式。把 v7 字节交给from_bytes会抛错说明并指向load(path)。持久性语义在测试中有系统验证例如 turbovec/tests/adversarial_durability.rs 反复执行同步 → 变更 → 再同步 → 加载比对to_bytes()状态的序列确认任意阶段的增量同步不丢失状态。九、版本与限制.tv与.tvim加载都会在分配之前校验头部bit_width必须是 2/3/4dim是 8 的正倍数且≤ 16384MAX_DIM——构造时施行的同一上限因此本构建能创建的索引也一定能加载回来每个载荷大小用 checked 算术计算并通过限长读取器读取。因此畸形或不可信文件抛出干净的错误而非 panic、除零或驱动超大分配。codebook、scale 与校准值还会在值级校验有限、在支撑内因此结构合法但字段携带越界值的文件会被拒绝而非加载。该校验不提供的是载荷完整性检查。两种格式都不对已存编码做校验和值级检查只拒绝越出合法范围的值——scale 中被翻转的尾数位仍是有限正浮点数被翻转的编码字节与合法字节无法区分。写入者之外到达的损坏磁盘故障、截断拷贝、坏传输因此会干净加载并静默改变搜索结果。实测数据翻转 4114 字节.tv文件全部 32,912 个位并逐一加载——1460 个翻转4%被拒绝31,452 个96%加载并返回不同索引。按区块分144 个头部位全部被拒、992 个 codebook 位中 988 被拒、3072 个 scale 位中 169 被拒、4128 个校准尾部位中 159 被拒、24,576 个编码位中 0 被拒。scale 与尾部的数字正是值级检查精确声称、不多做的体现把浮点翻成非有限、负或出支撑的位被捕获每个尾数翻转都不会。编码不携带任何校验这正是该列为零的原因也是它作为最大区块的原因。这是刻意的范围选择而非疏忽。保存是原子的写入中途崩溃会保留前一文件完好因此写入者不可能留下撕裂的索引超出范围的是写入之后到达的损坏。需要检测时请自行校验文件或把它放在做校验的文件系统上。n_calib 0的 TQ 尾部表示未校准索引否则等于dim。只读 v7v5 或 v6 文件会被拒绝并报出版本名。那些文件仍可由写出它们的发布版读取在那里重新保存即迁移路径。版本 1 到 4 早于 v5 旋转变更完全无法解码——其编码是在本构建无法复现的旋转下编码的——必须从源向量重建。核心头中dim 0表示惰性未提交索引。它只在n_vectors 0时合法加载后产生的索引dim为None直到首次add/add_with_ids。两种格式都携带 magic 版本字节跨次要版本稳定。破坏性变更提升版本字节。write()、to_bytes()与sync()都产出 v7write()与to_bytes()产出无人认领的快照sync()产出它认领后增量更新的容器见增量保存。v7 文件对早期 turbovec 发布版不可读其加载器拒绝版本字节而非误解析。十、深入阅读核心实现turbovec/src/lib.rsTurboQuantIndex、MAX_DIM 16384、MAX_INPUT_MAGNITUDE 1e16、from_parts、serialized_lenID 映射实现turbovec/src/id_map.rsIdMapIndex、try_search_with_allowlist搜索内核turbovec/src/search.rsNEON / AVX-512BW / AVX2 / 标量回退的 SIMD 流水线nq1 并行化门控Python 绑定入口turbovec-python/src/lib.rs、turbovec-python/python/turbovec/init.py过滤正确性测试turbovec/tests/filtering.rs持久性与同步测试turbovec/tests/adversarial_durability.rs、turbovec/tests/adversarial_durability_edges.rs官方 API 参考原文docs/api.md【免费下载链接】turbovecA vector index built on TurboQuant, written in Rust with Python bindings项目地址: https://gitcode.com/GitHub_Trending/tu/turbovec创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考