يعيد نوع البيانات QBit تنظيم تخزين المتجهات لتسريع عمليات البحث التقريبي. فبدلاً من تخزين عناصر كل متجه معًا، يجمع مواقع البت نفسها عبر جميع المتجهات.
يتيح ذلك تخزين المتجهات بالدقة الكاملة مع تمكينك من اختيار مستوى التكميم الدقيق وقت البحث: اقرأ عددًا أقل من البتات لتقليل I/O وتسريع العمليات الحسابية، أو عددًا أكبر من البتات للحصول على دقة أعلى. وهكذا تستفيد من مزايا السرعة الناتجة عن تقليل نقل البيانات والحسابات بفضل التكميم، مع بقاء جميع البيانات الأصلية متاحة عند الحاجة.
للتصريح بعمود من النوع QBit، استخدم الصياغة التالية:
element_type – نوع كل عنصر من عناصر المتجه. الأنواع المسموح بها هي Int8 وBFloat16 وFloat32 وFloat64
dimension – بُعد كل متجه
stride – اختياري. عدد الأبعاد المخزنة معًا في مجموعة واحدة من التدفقات. عند عدم تحديده، تكون قيمته الافتراضية dimension (مجموعة واحدة). وعند تحديده، يجب أن تكون قيمة dimension من مضاعفات stride، وعندما تكون قيمة stride أصغر من dimension، يجب أن تكون أيضًا من مضاعفات 8. تُقسَّم أبعاد dimension إلى dimension / stride مجموعات متجاورة، وتُخزَّن مستويات البت الخاصة بكل مجموعة في تدفقات منفصلة. يتيح ذلك لعملية البحث ضمن أول D بُعدًا (حيث تكون D من مضاعفات stride) قراءة تدفقات المجموعات التي تغطي تلك الأبعاد فقط، وهو ما يفيد مع تضمينات Matryoshka.
باستخدام النوع QBit في تعريف عمود الجدول:
تُحوَّل المصفوفات إلى QBit عندما يتطابق طول المصفوفة مع بُعد QBit. ولا يلزم أن يتطابق نوع عنصر المصفوفة مع نوع عنصر QBit. ويُحوَّل إليه تلقائيًا أي نوع عناصر رقمي. يتيح لك ذلك نقل عمود تضمينات موجود مباشرةً إلى عمود QBit:
يمكن أيضًا إجراء التحويل بشكل صريح باستخدام CAST، على سبيل المثال CAST(embedding AS QBit(Float32, 8)).
تعيد عملية التحويل العكسي بناء المتجه الأصلي انطلاقًا من التمثيل المنقول على مستوى البت، لذا فإن تحويل QBit إلى Array يعيد القيم المخزنة. وهذا هو عكس تحويل المصفوفات إلى QBit:
تستخدم المصفوفة المُعاد إنشاؤها نوع العنصر الخاص بـ QBit، ثم تُحوَّل عناصرها بعد ذلك إلى نوع عنصر المصفوفة المطلوب. لذلك، يعمل أيضًا cast يغيّر نوع العنصر أيضًا، مثل التحويل من QBit(Float32, N) إلى Array(Float64).
تكون دورة التحويل Array -> QBit -> Array بلا فقدان بالنسبة إلى Int8 وFloat32 وFloat64. أما BFloat16، فهو يطابق تحويلًا مباشرًا إلى BFloat16 — والفقد الوحيد في الدقة هو دقة BFloat16 نفسها.
عندما لا تكون قيمة dimension مضاعفًا للعدد 8، تُحذف عناصر الحشو اللاحقة الموجودة في التمثيل الداخلي، لذلك تكون النتيجة دائمًا مكوّنة من dimension عنصرًا بالضبط.
يمكن تحويل QBit إلى QBit آخر ما دامت قيمة dimension (عدد عناصر المتجه) ثابتة. ويمكن أن يتغير كلٌّ من element_type وstride؛ أما التحويل إلى QBit ذي dimension مختلف فيؤدي إلى حدوث استثناء، لأن ذلك سيغيّر المتجه نفسه.
يؤدي تغيير element_type إلى إعادة تكوين المتجه وتحويل كل عنصر إلى النوع الجديد، تمامًا كما في تحويل Array المقابل: يكون التوسيع (على سبيل المثال من QBit(Float32, N) إلى QBit(Float64, N)) دقيقًا، بينما يؤدي التضييق إلى فقدان بعض الدقة بالطريقة نفسها التي يحدث بها ذلك عند تحويل Array إلى نوع أضيق.
إن تغيير stride فقط (مع الإبقاء على element_type نفسه) يعيد تجميع طبقات البتات المخزّنة من دون المساس بالقيم، لذا تكون العملية دائمًا من دون فقدان:
يوفّر QBit نمط وصول عبر الأعمدة الفرعية يتيح لك الوصول إلى مستويات بت الفردية في المتجهات المخزّنة. ويمكن الوصول إلى كل موضع بِت باستخدام الصيغة .N، حيث إن N هو موضع البِت:
يعتمد عدد الأعمدة الفرعية المتاحة على نوع العنصر (وعند استخدام stride، أيضًا على عدد مجموعات stride):
Int8: 8 أعمدة فرعية لكل مجموعة stride (1-8)
BFloat16: 16 عمودًا فرعيًا لكل مجموعة stride (1-16)
Float32: 32 عمودًا فرعيًا لكل مجموعة stride (1-32)
Float64: 64 عمودًا فرعيًا لكل مجموعة stride (1-64)
تتبع الأعمدة الفرعية ترتيب بحسب المجموعات: وبوجه عام، يقرأ vec.N مستوى البِت (N-1) % element_size من مجموعة stride ذات الرقم (N-1) / element_size. على سبيل المثال، مع QBit(BFloat16, 4096, 1024)، تُقسَّم الأبعاد الـ4096 إلى 4 مجموعات، تضم كل منها 1024 بُعدًا، لذا يوجد 64 عمودًا فرعيًا: تمثل vec.1 … vec.16 مستويات البِت لمجموعة stride الأولى (الأبعاد 1–1024)، بينما تنتمي vec.17 … vec.32 إلى المجموعة الثانية (الأبعاد 1025–2048)، وهكذا.
افتراضيًا، يخزّن QBit كل مستوى بت في single stream واحد يمتد عبر جميع أبعاد dimension، لذا فإن عملية البحث تقرأ دائمًا مستويات البت كاملةً عبر المتجه بأكمله. يقسّم المعلَّم الاختياري stride أبعاد dimension إلى dimension / stride مجموعات متجاورة، ويخزّن مستويات البت لكل مجموعة في تدفقات منفصلة. يتيح ذلك لعملية بحث تقتصر على أول D بُعد فقط (بحيث تكون D من مضاعفات stride) أن تقرأ فقط تدفقات المجموعات التي تغطي تلك الأبعاد — وهذا مفيد مع تضمينات Matryoshka، حيث تشكّل الأبعاد الأولى تضمينًا منخفض الأبعاد صالحًا للاستخدام.
هنا تُقسَّم الأبعاد الـ4096 إلى 4 مجموعات، في كل منها 1024 بُعدًا. وتتبع الأعمدة الفرعية ترتيبًا بحسب المجموعات: مع BFloat16 (16 مستوى بت)، تمثل vec.1 … vec.16 مستويات البت الستة عشر لأول مجموعة stride (الأبعاد 1–1024)، بينما تنتمي vec.17 … vec.32 إلى المجموعة الثانية (الأبعاد 1025–2048)، وهكذا. وبوجه عام، يقرأ vec.N مستوى البت (N-1) % element_size من مجموعة stride رقم (N-1) / element_size.
لتشغيل بحث منخفض الأبعاد، مرِّر عدد الأبعاد المطلوب قراءتها بوصفه الوسيط الرابع لدوال المسافة المنقولة (انظر أدناه). ويجب أن يحتوي المتجه المرجعي على هذا العدد من العناصر على الأقل (ويُتجاهل أي عناصر إضافية لاحقة)، كما يجب أن تكون القيمة من مضاعفات stride.
هذه هي دوال المسافة المستخدمة في البحث عن التشابه المتجهي والتي تستخدم نوع البيانات QBit:
بالنسبة إلى QBit ذي stride، تقبل هذه الدوال وسيطًا رابعًا اختياريًا هو used_dims — أي عدد الأبعاد الأولى المطلوب قراءتها — بحيث لا تُقرأ إلا مجموعات stride التي تغطي هذه الأبعاد. ويجب أن يحتوي المتجه المرجعي على used_dims عنصرًا على الأقل (ويُتجاهل أي عنصر زائد في النهاية، لذا يمكن إعادة استخدام متجه query كامل الحجم في بحث reduced-dimension دون الحاجة إلى اقتطاعه أولًا)، كما يجب أن تكون قيمة used_dims مضاعفًا لـ stride. آخر تعديل في ٢٣ يوليو ٢٠٢٦