حساسية المقارنة
تسجّل هذه الصفحة اختباري ثبات مونت كارلو اللذين تقوم عليهما خريطة المقارنة: إعادةَ معاينة أوزان المحاور عبر فضائها كله مع تجميد الدرجات الفرعية، وإحداثَ اضطراب في كل درجة فرعية بمقدار نقطة على الأكثر عند الأوزان المنشورة. وهي تحمل التصميمين والبذور، وفواصلَ الرتب للمنتجات الاثنين والعشرين كلها، وإحصاءاتِ التقاطع في المواجهات المباشرة، وادّعاءً مسحوباً واحداً، وبرنامجاً يعيد إنتاج السجل.
اختبرت النسخةُ الأولى من صفحة المنهجية توزيناً بديلاً واحداً بالضبط، وأبلغت عن تقاطع واحد. وفعلت مراجعةُ الأقران ما كان ينبغي لنا فعله: مونت كارلو عبر فضاء الأوزان كله وعبر عدم يقين الدرجات الفرعية. وقد تبنّينا التصميم وأعدنا تشغيله بكودنا نحن بدل الاستشهاد بأرقام المراجِع. وهذه الصفحة تنشر أرقامنا؛ والسجلُّ الكامل للتشغيلات وفحصُ التوافق مع أرقام المراجِع في review/index/SCORES.md، وكلُّ درجة فرعية يستند إليها الاختباران على صفحة الدرجات.
الاختباران
يشغّل كل اختبار N = 200,000 عينة لكل محور:
- أوزانٌ مُعايَنة ودرجاتٌ مجمَّدة. أوزانٌ غير سالبة تُسحب بانتظام على سِمبلكس أوزان كل محور (سحوبات Exp(1) مُسوّاة)؛ وكلُّ درجة فرعية منشورة مثبَّتة. وهذا يسأل: كم من الترتيب هو حكمُ التوزين لدينا؟
- أوزانٌ مجمَّدة ودرجاتٌ مضطربة. الأوزانُ المنشورة محفوظة؛ ويُضاف ضجيجٌ منتظم مستقل ±1 إلى كل درجة فرعية في كل منتج، ثم يُقصّ إلى 0–10. وهذا يسأل: كم يتوقف على أن تكون أحكامُ التقدير بالأعداد الصحيحة مضبوطة تماماً؟
والرتبةُ هنا هي 1 زائد عددَ المنتجات التي درجتُها أعلى منها تماماً. التشغيل الأساسي: البذرة 20260807. وإعادةُ تنفيذ مستقلة (كودٌ ومولّدٌ مختلفان، البذرة 271828). وأُعيد الحساب في 2026-08-07 على الحقل المؤلَّف من 22 منتجاً حين قُيّمت Obscura: فالرتبةُ نسبية إلى الحقل، ولذلك أُعيد تشغيل الفواصل أدناه وصفُّ Obscura داخلها بدل تركها بائتة، والسجلُّ العامل في review/index/SCORES.md يحفظ تشغيلات الحقل المؤلَّف من 21 منتجاً بوصفها سجلَّ إعادة إنتاج نتائج المراجِع. وأُعيد الحساب مرة أخرى في اليوم نفسه حين أُعيد تقييم URnetwork على حكم التفعيل الافتراضي (سجلُّ المنهجية): فالجداولُ أدناه هي سجلُّ ما بعد إعادة التقييم، أُعيد تشغيله تحت التنفيذين والبذرتين معاً، وتشغيلاتُ ما قبل إعادة التقييم تبقى في جدول التشغيلات بالسجل العامل. وعلى الحقل المؤلَّف من 22 منتجاً يتفق التنفيذان على كل فاصل ووسيط عدا خمس خانات في اختبار الأوزان عند أزواج متجاورة تكاد تتعادل (بفارق 0.05 في الدرجة النقطية)، وهي مذكورة في السجل العامل؛ والخانات الخمس نفسها تختلف قبل إعادة التقييم وبعدها. وكلُّ فاصل من فواصل ضجيج الدرجات، وهو النوع الذي تنشره صفحةُ النظرة العامة، متطابق في التشغيلين، وكذلك صفّا URnetwork وObscura كاملين.
نتائج URnetwork
حرّك دخولُ Obscura إحصاءاتِ رتبة X: وسيطُ الأوزان من 3 إلى 4، وفاصلُ الضجيج من 2–3 إلى 2–4. ثم حرّكتها إعادةُ التقييم على التفعيل الافتراضي مرة أخرى: وسيطُ الأوزان من 4 إلى 3، وفاصلُ الأوزان من 2–5 إلى 1–4. ولم يتحرك أي إحصاء رتبة في اختبار الضجيج ولا أي إحصاء في Y في المرتين.
| الإحصاء | الاختبار 1، أوزانٌ مُعايَنة | الاختبار 2، ضجيج درجات ±1 |
|---|---|---|
| رتبة X، الوسيط (المئين 5–95) | 3 (1–4) | 3 (2–4) |
| درجة X، المئين 5–95 | (الأوزان متغيرة) | 6.793–7.813 |
| متقدّمة على Mullvad في X | 74.0% (بالضبط 71/96) | 99.9% |
| متقدّمة على Obscura في X | 100% (منحلّ؛ انظر أدناه) | 93.9% |
| متقدّمة على NymVPN في X | 67.0% (بالضبط 343/512) | 30.6% |
| رتبة Y، الوسيط (المئين 5–95) | 3 (1–15) | 7 (4–11) |
| درجة Y، المئين 5–95 | (الأوزان متغيرة) | 5.747–6.653 |
| Y دون 5.00 | أبداً، بالبناء (انظر الادّعاء المسحوب) | 0 من 200,000 (نحو 4.4σ تحت نموذج الضجيج هذا: نادر لا مستحيل) |
المحوران لا يحملان ثقة متساوية. تبقى رتبةُ URnetwork في X داخل 1–4 عبر كل توزين مُعايَن: فقرب رأس محور X، أيُّ حكم تتخذه يقرّر الترتيب، لكن المؤشر نفسه مستقرٌّ نسبياً تحت الأوزان. أما رتبتها في Y فتتراوح 1–15 في فضاء الأوزان نفسه، وحتى عند الأوزان المنشورة يكون وسيطُ ضجيج ±1 هو الرتبة 7، لا الرتبة 5 المتعادلة التي توحي بها درجتُها النقطية. ومؤشر Y أشد حساسية للأوزان من X بكثير، وكلُّ موضع Y على الخريطة ينبغي أن يُقرأ بثقة أقل بالقدر نفسه.
المواجهات المباشرة
Mullvad. يبقى رقما Mullvad أكثرَ جملة مفيدة متاحة عن هذا المخطط، وقد غيّرت إعادةُ التقييم على التفعيل الافتراضي ما يقولانه. فقبلها كان تقدّمُ URnetwork على Mullvad في X عند 55.55% تحت عدم يقين الأوزان، أي قلبَ عملة يتوقف كلياً تقريباً على ترتيب البنية (الفصل مع التشفير من طرف إلى طرف، 0.60 مجتمعين) فوق التحقق (0.30). وبعد إعادة التقييم صار فرقُ الدرجات الفرعية (+5, +3, −3, −1): تتقدّم URnetwork في جزأي البنية معاً، وتتقدّم Mullvad في التحقق، وبفارق ضيق في ما بعد الكم، وتعطي المعاينةُ الوزنية نفسها التقدّمَ لـURnetwork في 74.0% من الوقت (وحجمُ السِمبلكس بالضبط 71/96 ≈ 73.96%). وتحت ضجيج درجات ±1 عند الأوزان المنشورة يصير 99.9%. ولم يعد التقدّم قلبَ عملة، لكن حساسية الأوزان المتبقية تبقى في صورة محدودة النطاق: فالقارئ الذي يزن التحقق بنحو نصف المحور لا يزال يحصل على Mullvad متقدّمة، ويبقى ذلك قراءةً معقولة لا خطأ. والمثالُ المشتغَل عليه في قسم إعادة التوزين أدناه.
Obscura. انقلبت المقارنة مع إعادة التقييم. فالاثنتان تتعادلان الآن في البنية (E+S 16–16، والساقان المحتويتان مفعّلتان افتراضياً عند كلتيهما، وعند Obscura بالبناء) وتتعادلان في V، فتصير الفجوةُ كلها، 0.70، جزءَ ما بعد الكم الذي تفتقده Obscura. وفرقٌ خالص في جزء واحد، (0, 0, 0, +7)، يجعل اختبار الأوزان منحلاً: فأي توزين بوزن غير صفري لما بعد الكم يحفظ الترتيب، ولذلك فإن «100% من الأوزان المُعايَنة» تصف شكل الفرق لا متانته. والاختبارُ المفيد هو ضجيج الدرجات، وتحته تتقدّم URnetwork في 93.9% من الوقت: خارج متناول نقطة واحدة، لأن أكبر تأرجح بنقطة واحدة في جزء واحد هو 0.35 مقابل فجوة 0.70. وكان الإحصاء قبل إعادة التقييم 69.5%، وهو لم يكن خارج ذلك المتناول؛ وعند درجات ما قبل إعادة التقييم كان الفرق (−1, 0, 0, +5) بحصة سِمبلكس دقيقة 5/6 = 83.33% (والمُعايَن 83.4%). والذي يُغلق المسافة بين الاثنتين حقاً هو أن تشحن Obscura تشفير ما بعد الكم.
NymVPN. اشترت إعادةُ التقييم تجاوراً جديداً في الاتجاه الآخر: فـNymVPN، عند 7.50، تجلس الآن بمقدار 0.20 فوق 7.30 لـURnetwork، أقربَ مما تجلس Obscura تحتها. وعند الأوزان المنشورة تتقدّم NymVPN. وعبر الأوزان المُعايَنة تتقدّم URnetwork بنسبة 67.0%: فالفرق (−1, −1, −1, +7)، ولذلك يدور الترتيب كله على ما إذا كان وزن ما بعد الكم يتجاوز الثُّمن، والحصةُ بالضبط (7/8)³ = 343/512. وتحت ضجيج الدرجات تحتفظ NymVPN بتقدّمها في 69.4% من السحوبات (وURnetwork متقدّمة في 30.6%). وهذا الزوج مطبوعٌ بوصفه متجاوراً وغير مستقر، تماماً كما كانت Obscura وURnetwork قبل إعادة التقييم.
فواصل الرتب لكل منتج
المئين 5–95 مع الوسيط بين قوسين، على الحقل المؤلَّف من 22 منتجاً بعد إعادة التقييم، تشغيلُ إعادة التنفيذ، البذرة 271828. وصفّا URnetwork وObscura متطابقان تحت التشغيل الأساسي. وقد غيّرت إعادةُ التقييم ست خانات، كلها في عمود أوزان X: URnetwork من 2–5 (4) إلى 1–4 (3)، وTor من 1–4 إلى 1–5، وObscura من 3–10 إلى 4–10، ووسيطُ Mullvad من 3 إلى 4، وIVPN من 2–8 إلى 3–8، وExpressVPN من 2–12 إلى 3–12.
| المنتج | رتبة X، الأوزان | رتبة X، ضجيج الدرجات | رتبة Y، الأوزان | رتبة Y، ضجيج الدرجات |
|---|---|---|---|---|
| Tor | 1–5 (1) | 1–1 (1) | 2–22 (19) | 21–22 (22) |
| NymVPN | 2–9 (3) | 2–3 (2) | 18–21 (20) | 20–21 (20) |
| URnetwork | 1–4 (3) | 2–4 (3) | 1–15 (3) | 4–11 (7) |
| Obscura | 4–10 (6) | 3–5 (4) | 9–19 (15) | 12–19 (16) |
| Mullvad | 1–7 (4) | 4–6 (5) | 4–12 (7) | 4–11 (7) |
| IVPN | 3–8 (5) | 5–7 (6) | 6–16 (11) | 6–13 (10) |
| Apple Private Relay | 5–15 (9) | 6–8 (7) | 13–22 (20) | 13–19 (17) |
| Windscribe | 7–14 (11) | 7–10 (8) | 1–5 (2) | 1–5 (3) |
| PIA | 7–15 (12) | 8–13 (10) | 9–15 (11) | 7–14 (11) |
| ExpressVPN | 3–12 (8) | 8–13 (10) | 4–10 (6) | 4–11 (7) |
| Proton VPN | 9–16 (14) | 9–15 (12) | 1–5 (3) | 1–6 (3) |
| NordVPN | 6–13 (10) | 9–15 (12) | 4–10 (6) | 4–11 (7) |
| Tailscale | 6–18 (13) | 10–16 (13) | 1–8 (3) | 1–3 (1) |
| Cloudflare WARP | 7–16 (11) | 11–16 (14) | 1–16 (5) | 1–6 (3) |
| Surfshark | 7–16 (12) | 11–17 (15) | 8–13 (9) | 5–12 (8) |
| Orchid | 9–17 (16) | 12–17 (15) | 19–22 (21) | 20–22 (21) |
| Mysterium | 16–18 (18) | 16–20 (18) | 7–20 (18) | 14–19 (17) |
| TunnelBear | 15–19 (17) | 16–20 (18) | 8–16 (13) | 9–16 (13) |
| Sentinel | 17–20 (20) | 17–20 (19) | 9–19 (16) | 15–19 (18) |
| IPVanish | 19–20 (19) | 17–21 (19) | 12–19 (16) | 9–16 (13) |
| Hotspot Shield | 21–21 (21) | 20–21 (21) | 8–18 (12) | 9–16 (13) |
| Hola | 22–22 (22) | 22–22 (22) | 6–20 (17) | 14–19 (17) |
ولا تبالغ الفواصلُ في شيء بمصادفة تناظر النموذجين. فنموذجُ ضجيج ±1 بسيط عن قصد، وليس توزيعَ خطأ تجريبياً؛ ودرسُه أن الثقة في الرتبة تتطلب عدمَ يقين في الدرجات المُسنَدة، لا مجموعةً ثانية من الأوزان فحسب.
هل أعدنا إنتاج نتائج المراجِع؟
نعم، على الحقل المؤلَّف من 21 منتجاً الذي قاسه المراجِع، قبل تقييم Obscura. أبلغ المراجِع (البذرة 20260807، و200,000 عينة، وكوده هو): 55.64% تقدّماً على Mullvad؛ ودرجةَ X 6.341–7.359 ودرجةَ Y 5.749–6.652 تحت الضجيج؛ وفواصلَ رتب 2–5 و1–15 و2–3 و4–11 مع وسيط Y عند 7. وأعادت تشغيلاتُنا على ذلك الحقل فواصلَ الرتب والوسطاءَ أنفسَهم، وحدودَ درجات ضمن 0.005، و55.55% على التقاطع؛ وسجلُّ الحقل المؤلَّف من 21 منتجاً محفوظ في review/index/SCORES.md، وإحصاءاتُ Mullvad الثنائية تنتقل إلى إعادة التشغيل على 22 منتجاً من دون تغيير ضمن خطأ المعاينة. وكان للتقاطع جوابٌ دقيق يُختبر به الجميع: فعند الدرجات التي قاسها المراجِع، كان فرقُ الدرجات الفرعية بين URnetwork وMullvad (+4, +3, −3, −3)، ونسبةُ سِمبلكس الأوزان في جانب URnetwork تُحسب فتساوي 109/196 ≈ 55.61%. وكلُّ تقدير مونت كارلو، تقديرُنا وتقديرُ المراجِع، يقع ضمن خطأ المعاينة (±0.22 نقطة عند 2σ لهذا الحجم من العينة) من القيمة الدقيقة. ونتيجةٌ خارجية لا نستطيع إعادة إنتاجها كانت ستكون اكتشافاً؛ ونتيجةٌ نستطيع إعادة إنتاجها هي اكتشاف أيضاً. وأرقامُ المراجِع هي سجلُّ ما قبل إعادة التقييم: فإعادةُ تقييم URnetwork على التفعيل الافتراضي في اليوم نفسه لاحقاً غيّرت صفَّها في X، وقيمةُ التقاطع الدقيقة الحالية هي 71/96، مشتقةً ومُعايَنة في الجداول أعلاه. ولا يزال فحصُ 109/196 يعيد إنتاج نفسه على درجات ما قبل إعادة التقييم، وهي ما قاسه المراجِع.
ادّعاء مسحوب، مسجَّل
حتى 2026-08-07 كانت صفحةُ المنهجية تدّعي أن موضع Y لـURnetwork متينٌ تحت الأوزان: أي أن لا إعادةَ توزين لأجزاء الجودة تستطيع إنزال Y دون 5.00. وحكمُ المراجِع، «صحيحٌ رياضياً لكنه خاوٍ من الدليل»، مقبولٌ كاملاً. فكلُّ درجة فرعية لـURnetwork في Y أُسنِدت عند ≥ 5، ولذلك فكلُّ متوسط موزون محدَّب لها ≥ 5 بالبناء: نشرنا خاصيةً في مدخلاتنا نحن كأنها دليلُ ثبات، وكان الادّعاء سيبقى «صحيحاً» مهما كانت تلك المدخلات خاطئة. والاعتراضُ نفسه يُحيل الادّعاءَ المكافئ على محور X إلى التقاعد قبل أن يقوله أحد. فدرجاتُ URnetwork الفرعية في X كلها ≥ 5 أيضاً، ولذلك فـ«لا إعادةَ توزين تُنزل X دون 5.00» مضمونٌ بالقدر نفسه وخاوٍ بالقدر نفسه. ولا تظهر أيٌّ من العبارتين في هذه المجموعة دليلاً مرة أخرى. وما يمكن قوله من السجل هو في الجداول أعلاه: تحت إعادة التوزين يكون المخرَجُ المفيد هو الرتبة لا عبورَ الخط، والاختبارُ المفيد للخطين هو عدم يقين الدرجات، وتحته يكون المئينُ الخامس لـY لدى URnetwork هو 5.747، وهي عبارةٌ عن نموذج ضجيج بسيط لا عن العالم. والذي يستطيع فعلاً إنزال Y دون الخط هو الدليل: قياسٌ خارجي يضع زمن الاستجابة أو معدل النقل تحت منتصف المقياس. ولا وجود لشيء منه في أي من الاتجاهين، وقاعدةُ «لا أرقام مخترعة» في هذه المجموعة هي سببُ جلوس جزأي الأداء عند مرساة الصنف البنيوي 5. والادّعاءُ مسحوبٌ لا محذوف، لأن قيمة هذا السجل أنه يحتفظ بأخطائه هو.
المواضع داخل الضجيج
حساسيةُ النقطة الواحدة ليست مونت كارلو، لكنها تجيب عن السؤال نفسه عند الحدود، ولذلك تُسجَّل هنا مع البقية.
- Apple Private Relay يجلس ضمن ضجيج الخطين معاً في آن واحد، +0.10 في X و−0.05 في Y، وحده عند تقاطع المحورين، ولذلك ينبغي أن يُقرأ على أنه على الخطين، لا ساكناً في أي ربع، بما في ذلك خانةُ «أكاديمي / بحثي» التي تحطّ فيها إشاراته الصارمة. وأحكامُ التقدير فيه تميل في الاتجاهين: فتقييم E أو S بنقطة أقل يضع X عند 4.85 أو 4.75، عائداً إلى يسار الخط، وتقييم S عند 8 يضعه يمينه بوضوح عند 5.45. وتلك الحساسية هي سبب تعليق الخريطة عليه بدل ادّعائه.
- Obscura تجلس ضمن ضجيج خط Y من فوقه (+0.10). وانتماؤها إلى يمين خط X واثقٌ عند +1.60 ومتينٌ تحت الضجيج: 0 من 200,000 عينة ضجيج ±1 وضعتها يسار الخط، والمئينُ الخامس لـX عندها 6.12. لكنها حساسة للأوزان: فـ32.6% من الأوزان المُعايَنة بانتظام تضعها يساراً، بدافع PQ 0 وV 6 حين تُسحب أوزانُ التحقق وما بعد الكم عالية. أما جانب Y فقلبُ عملة حقيقي ولا يُدّعى به شيء: إذ تهبط Y دون 5.00 في 36% من عينات ضجيج ±1 وفي 50.0% من الأوزان المُعايَنة، وجزءُ T الوحيد المعلَّم بأنه حكمُ تقدير يحرّكها إلى أي من الجانبين (T 5 يعطي 4.85، وT 7 يعطي 5.35).
- وMysterium (−0.10) وHola (−0.15) وSentinel (−0.20) يجلسون ضمن الضجيج تحت خط Y، وانتماؤهم فوقه أو تحته ليس حكماً واثقاً؛ وعجزُهم في X هو الإشارة.
- ومن فوق، تتجاوز TunnelBear وIPVanish وHotspot Shield خطَّ Y بمقدار 0.45–0.50 فقط، على درجتَي L وT من صنف مراكز البيانات؛ والقارئُ الذي يقيّم ثبات أساطيلها بنقطة أقل سيضعها عليه أو تحته.
- وخطُّ X مسافةٌ صافية لكل من عداه. فلا شيء غير Apple يقترب منه بأقل من 0.40 (Windscribe من اليسار؛ وIVPN عند 0.70 وObscura عند 1.60 من اليمين)، ولذلك لا إعادةَ تقييم بنقطة واحدة تنقل أي منتج آخر عبر انقسام «القابل للتحقق / القائم على الثقة». وApple هو المنتج الوحيد الذي يتوقف جانبُه من ذلك الخط بحق على حكم تقدير واحد.
إعادات التوزين التي تغيّر الترتيب
تضع الأوزانُ الثابتة البنيةَ (S + E = 0.60) فوق التحقق (V = 0.30). أعِد التوزين نحو التحقق بقوة كافية — مثلاً E 0.15 / S 0.25 / V 0.50 / PQ 0.10 — فتتجاوز Mullvad (7.00) شبكةَ URnetwork (6.90) على المحور؛ وادفع وزن التحقق أعلى من ذلك، فتتجاوزها IVPN في النهاية أيضاً. وقبل إعادة التقييم على التفعيل الافتراضي كان مثالٌ أهون كافياً (V عند 0.45)، وكان رقمُ الفضاء كله قلبَ عملة: فعبر الأوزان المُعايَنة بانتظام كانت URnetwork تتقدّم على Mullvad في 55.55% من الوقت فقط. وبعد إعادة التقييم صارت تتقدّم في 74.0%، ولم يعد مثالُ V 0.45 يقلب الترتيب (URnetwork 7.00، وMullvad 6.70). والترتيبُ في رأس المحور يقوم الآن على درجات البنية الفرعية كما يقوم على الأوزان، لكن القارئ الذي يزن التدقيقات وأدلة المحاكم بنحو نصف المحور لا يزال يحصل على Mullvad متقدّمة وليس مخطئاً. وصفحاتُ كل منتج تقول لذلك القارئ أصلاً أن يختار Mullvad أو IVPN حتى تُغلق URnetwork ثغرةَ تدقيق بروتوكولها.
والرافعةُ نفسها مشدودةً في الاتجاه الآخر هي مثالُ IVPN المشتغَل عليه: فتوزينٌ تسوده البنية — E 0.35 / S 0.45 / V 0.10 / PQ 0.10 — يُنزل IVPN إلى 4.90، إلى يسار الخط بقليل، وMullvad عند 5.00 بالضبط عليه. وانتماءُ IVPN إلى الربع الأعلى الأيمن يتوقف على بقاء وزن التحقق كبيراً. والأوزانُ المجمَّدة تُجيزه، والاعتمادُ مسجَّل هنا على أي حال.
وعلى محور الجودة، وزنُ Eg عند 0.20 هو ما يرفع هامش URnetwork ويُبقي WARP وPrivate Relay وTor في الأسفل. والقارئُ الذي يعدّ هوية الخروج أمراً متخصصاً يضغط انتشار Y، ورتبةُ URnetwork في Y تتراوح بين 1 و15 عبر فضاء الأوزان المُعايَن. وقد ختمت نسخةٌ أسبق من صفحة المنهجية هذا التحليل بادّعاء أن لا إعادةَ توزين تستطيع إنزال Y لـURnetwork دون 5.00؛ وذلك الادّعاء مسحوب، والقسمُ أعلاه يسجّل السبب.
ما الذي يحرّك هذا السجل
ثلاثةُ أمور تحرّك نتائج هذه الصفحة بدل أن تجادلها. أن تشحن Obscura تشفير ما بعد الكم يُغلق الزوج المنحلّ الوحيد (المواجهات المباشرة أعلاه). وقياسٌ خارجي لزمن الاستجابة أو معدل النقل يحلّ محل المراسي البنيوية التي يسمّيها قسمُ الادّعاء المسحوب أرضاً رخوة في محور الجودة. وأيُّ إعادة تقييم تحت القاعدة السارية في صفحة المنهجية تعيد تشغيل الاختبارين على الحقل كله، كما فعلت إضافةُ Obscura وإعادةُ التقييم على التفعيل الافتراضي.
برنامج إعادة الإنتاج
تنفيذُ الفحص المتقاطع، على الحقل المؤلَّف من 22 منتجاً وبصفّ URnetwork بعد إعادة التقييم. وهو يعيد توليد صفّه من جدول التشغيلات في review/index/SCORES.md، وإحصاءاتِ رتبة URnetwork، وجدولَ كل منتج أعلاه (Python 3، وnumpy ≥ 2). ويؤكّد أن كل مجموع منشور في X وY يُعاد حسابه بالضبط من مصفوفتي الدرجات الفرعية قبل المعاينة، وترتيبُ السحب مهم لإعادة الإنتاج الدقيقة.
import numpy as np
SEED, N = 271828, 200_000
# name, X parts [X1 e2e, X2 sep, X3 verif, X4 pq], Y parts [Y1 L, Y2 T, Y3 Eg, Y4 In, Y5 A]
# URnetwork X row is the post-rescore [8, 8, 6, 7]; pre-rescore was [7, 8, 6, 5]
PRODUCTS = [
("URnetwork", [8, 8, 6, 7], [5, 5, 8, 8, 7]),
("Tor", [10, 10, 10, 4], [2, 1, 1, 9, 9]),
("NymVPN", [9, 9, 7, 0], [5, 4, 2, 5, 4]),
("Obscura", [8, 8, 6, 0], [5, 6, 5, 5, 4]),
("Mullvad", [3, 5, 9, 8], [7, 8, 5, 5, 4]),
("IVPN", [3, 5, 8, 8], [7, 7, 5, 4, 4]),
("Apple Private Relay", [7, 7, 3, 0], [8, 7, 2, 1, 2]),
("Windscribe", [3, 5, 7, 0], [7, 7, 6, 7, 7]),
("PIA", [3, 3, 8, 0], [7, 7, 4, 5, 4]),
("ExpressVPN", [3, 3, 5, 9], [7, 8, 4, 6, 4]),
("Proton VPN", [3, 3, 7, 0], [7, 8, 4, 6, 8]),
("NordVPN", [3, 3, 5, 6], [7, 8, 4, 6, 4]),
("Tailscale", [6, 2, 5, 0], [9, 9, 5, 4, 6]),
("Cloudflare WARP", [3, 4, 3, 5], [9, 8, 2, 4, 8]),
("Surfshark", [3, 3, 4, 5], [7, 8, 4, 5, 4]),
("Orchid", [3, 5, 3, 0], [6, 3, 3, 3, 3]),
("Mysterium", [3, 3, 3, 0], [6, 4, 6, 3, 4]),
("TunnelBear", [3, 2, 4, 0], [7, 6, 3, 5, 5]),
("Sentinel", [3, 3, 2, 0], [6, 4, 4, 6, 4]),
("IPVanish", [3, 2, 3, 0], [7, 7, 3, 4, 4]),
("Hotspot Shield", [3, 1, 2, 0], [7, 7, 2, 5, 5]),
("Hola", [1, 0, 0, 0], [5, 4, 5, 3, 7]),
]
WX = np.array([0.25, 0.35, 0.30, 0.10])
WY = np.array([0.30, 0.25, 0.20, 0.10, 0.15])
names = [p[0] for p in PRODUCTS]
XS = np.array([p[1] for p in PRODUCTS], float)
YS = np.array([p[2] for p in PRODUCTS], float)
UR, MV = names.index("URnetwork"), names.index("Mullvad")
OB, NY = names.index("Obscura"), names.index("NymVPN")
# sanity: every published total must recompute exactly (SCORES.md tables, product order above)
EXP_X = [7.30, 9.40, 7.50, 6.60, 6.00, 5.70, 5.10, 4.60, 4.20, 4.20, 3.90,
3.90, 3.70, 3.55, 3.50, 3.40, 2.70, 2.65, 2.40, 2.35, 1.70, 0.25]
EXP_Y = [6.20, 3.30, 4.00, 5.10, 6.20, 5.85, 4.95, 6.80, 5.75, 6.10, 6.70,
6.10, 7.25, 6.70, 6.00, 3.90, 4.90, 5.45, 4.80, 5.45, 5.50, 4.85]
assert np.allclose(XS @ WX, EXP_X) and np.allclose(YS @ WY, EXP_Y)
def ranks(scores): # rank = 1 + #{strictly greater}
return 1 + (scores[:, None, :] > scores[:, :, None]).sum(axis=2)
def q(a, p):
return np.quantile(a, p, method="nearest")
def sim_A(rng, subs, w, chunk=50_000):
R, S = [], []
for start in range(0, N, chunk):
m = min(chunk, N - start)
sc = rng.dirichlet(np.ones(len(w)), size=m) @ subs.T
R.append(ranks(sc)); S.append(sc)
return np.vstack(R), np.vstack(S)
def sim_B(rng, subs, w, chunk=20_000):
R, S = [], []
for start in range(0, N, chunk):
m = min(chunk, N - start)
noisy = np.clip(subs[None] + rng.uniform(-1, 1, (m,) + subs.shape), 0, 10)
sc = noisy @ w
R.append(ranks(sc)); S.append(sc)
return np.vstack(R), np.vstack(S)
rng = np.random.default_rng(SEED) # draw order matters for exact reproduction:
rAX, sAX = sim_A(rng, XS, WX) # 1) test 1, X axis
rAY, sAY = sim_A(rng, YS, WY) # 2) test 1, Y axis
rBX, sBX = sim_B(rng, XS, WX) # 3) test 2, X axis
rBY, sBY = sim_B(rng, YS, WY) # 4) test 2, Y axis
print("exact P(UR>MV | test 1) = 71/96 =", 71 / 96) # pre-rescore: 109/196
print("test 1: P(UR>MV on X) =", (sAX[:, UR] > sAX[:, MV]).mean())
print("test 2: P(UR>MV on X) =", (sBX[:, UR] > sBX[:, MV]).mean())
print("exact P(UR>Obscura | test 1) = 1 (pure X4 difference; pre-rescore: 5/6)")
print("test 1: P(UR>Obscura on X) =", (sAX[:, UR] > sAX[:, OB]).mean())
print("test 2: P(UR>Obscura on X) =", (sBX[:, UR] > sBX[:, OB]).mean())
print("exact P(UR>NymVPN | test 1) = 343/512 =", 343 / 512)
print("test 1: P(UR>NymVPN on X) =", (sAX[:, UR] > sAX[:, NY]).mean())
print("test 2: P(UR>NymVPN on X) =", (sBX[:, UR] > sBX[:, NY]).mean())
print("test 2: UR X score 5-95%:", q(sBX[:, UR], 0.05), q(sBX[:, UR], 0.95))
print("test 2: UR Y score 5-95%:", q(sBY[:, UR], 0.05), q(sBY[:, UR], 0.95))
print("test 2: UR Y < 5.00 in", int((sBY[:, UR] < 5).sum()), "of", N)
for label, r in (("AX", rAX), ("BX", rBX), ("AY", rAY), ("BY", rBY)):
print(label, "UR rank p5/med/p95:", int(q(r[:, UR], .05)), int(q(r[:, UR], .5)), int(q(r[:, UR], .95)))
print("| Product | X rank, weights | X rank, score noise | Y rank, weights | Y rank, score noise |")
print("|---|---|---|---|---|")
f = lambda r, j: f"{int(q(r[:, j], .05))}–{int(q(r[:, j], .95))} ({int(q(r[:, j], .5))})"
for j in np.argsort(-(XS @ WX), kind="stable"):
print(f"| {names[j]} | {f(rAX, j)} | {f(rBX, j)} | {f(rAY, j)} | {f(rBY, j)} |")