MilleMiglia: مولّد حالات واقعي للوجستيات الميل الأوسط
MilleMiglia هو مُولِّد ينتج حالات واقعية للوجستيات الميل الأوسط لأغراض المقارنة المرجعية والبحث. يشرح هذا الدليل ما الذي ينتجه، ولماذا تُعد الواقعية مهمة لمشكلات التوجيه والتجميع، وكيف يمكن للممارسين استخدام الحالات المولّدة لاختبار الخوارزميات قبل نشرها على شبكات الشحن الحية.
ملخص سريع
MilleMiglia هو مُولِّد ينتج حالات واقعية للوجستيات الميل الأوسط لأغراض المقارنة المرجعية والبحث. يشرح هذا الدليل ما الذي ينتجه، ولماذا تُعد الواقعية مهمة لمشكلات التوجيه والتجميع، وكيف يمكن للممارسين استخدام الحالات المولّدة لاختبار الخوارزميات قبل نشرها على شبكات الشحن الحية.
MilleMiglia: مولّد حالات واقعي للوجستيات الميل الأوسط
تعيش أبحاث التحسين أو تموت حسب حالات الاختبار الخاصة بها. فالاستدلال الإرشادي لتوجيه المركبات الذي يبدو ممتازًا على مجموعة مرتبة من النقاط المتناثرة عشوائيًا قد يتصرف بشكل مختلف تمامًا عندما تتجمع الطلبات على طول ممر طريق سريع، ويصل السائقون إلى حدود ساعات الخدمة، ولا تكون المستودعات قابلة للتبادل. نشرت Google Research MilleMiglia، الموصوف بأنه مولّد حالات واقعي للوجستيات الميل الأوسط. تشرح هذه المقالة الغرض من هذه الفئة من الأدوات، وكيفية إنشاء بيئة عمل حولها، وكيفية التفكير في التكوين والاستخدام، وبالقدر نفسه من الأهمية — أين يتوقف الدليل العام وتبدأ الأحكام الهندسية.
المصدر الأساسي لكل ما هو واقعي أدناه هو منشور مدونة Google Research على <https://research.google/blog/millemiglia-a-realistic-instance-generator-for-middle-mile-logistics>. ويحمل سجل المصدر المشار إليه الطابع الزمني 2026-09-18T17:46:09.000Z. وحيثما أصف ممارسات المجال أو مقابض التكوين أو سير عمل التحقق، أقول ذلك صراحةً وأتعامل معه على أنه تفسير وليس كميزة موثّقة.
لماذا تحتاج لوجستيات الميل الأوسط إلى مولّد حالات خاص بها
تُقسَّم اللوجستيات عادةً إلى ثلاث مراحل. يغطي الميل الأول الاستلام والتجميع من نقاط المنشأ. ويغطي الميل الأخير المرحلة النهائية إلى العميل أو المتجر. أما الميل الأوسط فيقع بينهما: النقل بعيد المدى والإقليمي للشحنات المجمّعة بين مراكز التوزيع ومراكز التنفيذ ومراكز الفرز ومحطات العبور.
لمشكلات الميل الأوسط شكل مميز:
- بنية المحور والعمود. الطلب ليس موزعًا بشكل منتظم على الخريطة؛ بل يتركز في منشآت اختيرت مواقعها قبل سنوات لأسباب لا علاقة لها بخوارزميتك.
- طلب مجمّع لكن غير متجانس. حمولة المقطورة ليست طردًا. ففئات الشحن وأعداد المنصات وحدود الحجم مقابل الوزن كلها تقيّد كيفية دمج الحمولات.
- إيقاع زمني. جداول النقل الخطي مبنية حول أوقات القطع ونوافذ مواعيد الأرصفة وحدود الورديات. والمسار المثالي هندسيًا لكنه يفوت موعد رصيف ما لا قيمة له.
- اقتران الموارد. الجرارات والمقطورات والسائقون موارد منفصلة بقواعد منفصلة. وقد تُبطل الساعات المتبقية للسائق خطة اعتبرت طبقة التوجيه أنها قابلة للتنفيذ.
- عدم تماثل التكلفة. القرارات المكلفة عادةً تتعلق بعدد الرحلات وعدد المركبات ومقدار أميال السير الفارغ — وليس بالتسلسل الدقيق للمحطات القليلة الأخيرة.
صُممت حالات المعيار الكلاسيكية إلى حد كبير لعائلة مشكلات مختلفة، عادةً الميل الأخير أو التوجيه العام ذي السعة المحدودة. وهي ممتازة لمقارنة الخوارزميات على أساس مشترك، لكنها قد تقلل من تمثيل السمات البنيوية التي تهيمن على أداء الميل الأوسط. تلك هي الفجوة التي يُفترض أن يسدّها مولّد الحالات الواقعي: ليس لاستبدال المعايير العامة، بل لإضافة توزيع من الحالات يشبه أكثر ما يواجهه المشغلون فعليًا.
ما هو MilleMiglia — وما يقوله الدليل العام
استنادًا بشكل صارم إلى المصدر الأساسي، فإن MilleMiglia هو مولّد حالات واقعي موجّه للوجستيات الميل الأوسط. عنوان منشور Google Research هو الحقيقة المرتكزة هنا: مولّد، موجه نحو الواقعية، محدد النطاق بالميل الأوسط.
هذا ادعاء أضيق مما قد يبدو للوهلة الأولى، ويستحق الانضباط حياله. فالوصف المنشور لا يثبت (بحسب الدليل المتاح لي) صيغة ملف محددة، ولا واجهة سطر أوامر محددة، ولا حزمة محددة على أي سجل، ولا مخطط معاملات موثّق، ولا أرقام معيارية منشورة. لذلك يجب على أي شخص يبني على MilleMiglia أن يتعامل مع صفحة المصدر الرسمية كمرجع لتفاصيل الواجهة، وأن يتعامل مع هذه المقالة كدليل على سير العمل المحيط: إعداد البيئة، والتفكير في التكوين، وبناء منصة الاختبار، والتحقق من الواقعية.
ميزة القيمة لمولّد كهذا هي قابلية إعادة الإنتاج. فبدلًا من الأمل في أن يكون لدى المتعاون نفس مجموعة البيانات الاحتكارية التي لديك، تشاركان بذرة وتكوينًا ومعرّف إصدار — ويعيد الطرفان توليد نفس الحالات. هذه الخاصية هي ما يجعل النتائج قابلة للمقارنة بين الفرق.
المتطلبات
قبل تثبيت أي شيء، تأكد من توفر ما يلي. القائمة محافظة عمدًا وتعكس الأدوات العامة لهذا النوع من الأعمال البحثية.
- Python 3.10 أو أحدث. تخلت المنظومة العلمية الحديثة إلى حد كبير عن المفسرات الأقدم. تحقق من المصدر الرسمي للحصول على المتطلب الدقيق، لأن ذلك هو القيد الملزم.
- بيئة افتراضية. لا تثبّت عملًا بحثيًا في مفسر النظام أبدًا؛ فتعارضات التبعيات في هذا المجال شائعة.
- Git، للحصول على المصدر ولتسجيل الالتزام الدقيق الذي استخدمته.
- المكتبات العددية الأساسية: NumPy وpandas لمعالجة البيانات، وNetworkX إذا أردت فحص بنية الرسم البياني لشبكة مولّدة، وMatplotlib للتشخيص.
- حلّال توجيه واحد على الأقل لاستهلاك الحالات. يُعد Google OR-Tools خيارًا افتراضيًا معقولًا؛ والحلّالات التجارية أو الاستدلالات الإرشادية المخصصة تعمل بالقدر نفسه، لأن مخرجات المولّد بيانات، وليست واجهة حلّال.
- مساحة قرص وذاكرة كافية. عمليات مسح الحالات المولّدة رخيصة فرديًا وكبيرة بشكل مفاجئ إجمالًا. بضعة غيغابايتات من القرص الحر لأرشيفات الحالات نقطة بداية معقولة.
التثبيت خطوة بخطوة
تحذير أولًا: الأوامر أدناه هي سقالات بيئة، وليست نقلًا لتعليمات التثبيت الرسمية لـ MilleMiglia. وحيثما لزم عنوان مستودع أو اسم حزمة، تركت عنصرًا نائبًا. خذ تلك القيم من صفحة المصدر الرسمية.
تأكد من إصدار المفسر لديك قبل إنشاء أي شيء.
python3 --versionأنشئ بيئة افتراضية معزولة وفعّلها. إبقاؤها خارج دليل مشروعك يمنع إضافتها بالخطأ إلى التحكم بالإصدارات.
python3 -m venv ~/.venvs/millemiglia
source ~/.venvs/millemiglia/bin/activateحدّث أدوات التغليف. غالبًا ما تفشل إصدارات pip الأقدم على الحزم الحديثة.
python -m pip install --upgrade pip setuptools wheelأنشئ دليل عمل بمجلدات منفصلة للحالات المولّدة وملفات التكوين والمخرجات والسجلات. فصل هذه يجعل أرشفة تجربة لاحقًا أمرًا بسيطًا.
mkdir -p ~/work/millemiglia/{src,instances,configs,outputs,logs}
cd ~/work/millemigliaاحصل على مصدر المولّد. استبدل العنصر النائب بموقع المستودع الفعلي الوارد في الوثائق الرسمية.
git clone <MILLEMIGLIA_REPO_URL> src/millemigliaسجّل الالتزام الدقيق. هذا هو معرّف الإصدار الذي يجب أن تستشهد به إلى جانب أي نتائج.
cd src/millemiglia && git rev-parse HEAD | tee ../../logs/commit.txtإذا كان العمل يُشحن كحزمة Python قابلة للتثبيت، فثبّته في الوضع القابل للتحرير حتى تصبح التغييرات المحلية سارية دون إعادة تثبيت.
python -m pip install -e .ثبّت منظومة التحليل والحلّال المحيطة. هذه أدوات عامة الغرض، مستقلة عن المولّد نفسه.
python -m pip install numpy pandas networkx matplotlib
python -m pip install ortoolsجمّد البيئة المحلولة. إعادة إنتاج معيار ما تتطلب إعادة إنتاج البيئة، وليس البذرة فقط.
python -m pip freeze > ../../configs/requirements.lock.txtأخيرًا، اضبط بذرة تجزئة حتمية للجلسة. عشوائية التجزئة في Python مصدر كلاسيكي للتباين بين التشغيلات في الشيفرة التي تمر على المجموعات أو القواميس.
export PYTHONHASHSEED=0التكوين: من البذرة إلى السيناريو
التكوين هو حيث يكسب المولّد كلمة "واقعي" أو يخسرها. مخطط المعاملات الدقيق هو تفصيل واجهة يجب أن تقرأه من الوثائق الرسمية. ما يلي هو قائمة أبعاد يجب أن يعرضها عادةً مولّد الميل الأوسط، مصاغة بحيث يمكنك ربطها بأي صيغة يستخدمها MilleMiglia فعليًا. تعامل مع YAML أدناه كقالب توضيحي، وليس كمخطط موثّق.
# ILLUSTRATIVE TEMPLATE — map these concepts to the real parameter names.
scenario:
name: "metro-region-baseline"
seed: 20260918 # reproducibility anchor
horizon_hours: 24 # planning window
network:
num_hubs: 8
hub_placement: "clustered" # not uniform random
service_time_minutes: [20, 45]
demand:
total_loads: 400
size_distribution: "lognormal"
temporal_profile: "peaked" # cutoffs and shift starts
fleet:
vehicle_types: ["van", "straight_truck", "tractor_trailer"]
capacity_units: "pallets"
max_drive_hours: 11
depot_assignment: "fixed"
costs:
per_mile: 1.0
per_hour: 1.0
fixed_dispatch: 150.0ثلاثة مبادئ تهم أكثر من أي حقل فردي:
بذرة لكل شيء. المولّد غير الحتمي تمامًا بحسب بذرة ما لا يمكنه دعم بحث قابل لإعادة الإنتاج. سجّل البذرة إلى جانب اسم الحالة، دائمًا.
معاملات للتوزيعات، وليس لقيم مفردة. العمليات الحقيقية متنوعة. المولّد الذي يُصدر ملف تعريف طلب ثابتًا واحدًا ينتج نوعًا واحدًا من الحالات. التوزيعات على أحجام الطلب وأوقات الخدمة وأنماط الوصول تتيح لك المسح عبر الأنظمة والعثور على حيث تختفي ميزة الخوارزمية.
افصل الواقعية البنيوية عن الواقعية الإحصائية. الواقعية البنيوية تعني أن طوبولوجيا الشبكة تبدو كنظام محور وعمود — منشآت متجمعة، مسافات معقولة بين المحاور، عدم تماثل بين الممرات. والواقعية الإحصائية تعني أن التوزيعات الحدية للحمولات والأوقات والتكاليف تطابق بيانات التشغيل. وقد يمتلك المولّد إحداهما دون الأخرى، ويستحق اختبارهما بشكل مستقل.
أمثلة الاستخدام
توليد مجموعة حالات
النمط النموذجي هو مسح على البذور بتكوين ثابت، ينتج ملف حالة واحدًا لكل بذرة. الاستدعاء أدناه يستخدم نقطة دخول نائبة؛ استبدلها بالنقطة الحقيقية.
cd ~/work/millemiglia
for seed in 1 2 3 4 5; do
python -m millemiglia.generate \
--config configs/baseline.yaml \
--seed "$seed" \
--out "instances/baseline_${seed}.json" \
2>> "logs/generate_${seed}.err"
doneتكتب الحلقة حالة كل بذرة في ملفها الخاص وتلتقط stderr بشكل منفصل، مما يجعل الإخفاقات الجزئية واضحة بدلًا من أن تكون صامتة.
التحقق من مخطط المخرجات
قبل إدخال أي شيء إلى حلّال، تأكد من أن الملفات كاملة ومتسقة داخليًا. أسماء الحقول أدناه توضيحية؛ كيّفها مع المخطط الفعلي.
import json
from pathlib import Path
REQUIRED = {"seed", "hubs", "loads", "vehicles"}
def validate(path: Path) -> bool:
try:
data = json.loads(path.read_text())
except json.JSONDecodeError:
print(f"{path.name}: malformed JSON")
return False
missing = REQUIRED - set(data)
if missing:
print(f"{path.name}: missing {sorted(missing)}")
return False
total_capacity = sum(v["capacity"] for v in data["vehicles"])
total_demand = sum(l["size"] for l in data["loads"])
if total_demand > total_capacity:
print(f"{path.name}: infeasible by construction "
f"({total_demand} > {total_capacity})")
return False
return True
good = [p for p in sorted(Path("instances").glob("*.json")) if validate(p)]
print(f"{len(good)} instances passed validation")فحص السعة رخيص ويلتقط الفئة الأكثر شيوعًا من أخطاء المولّد: حالات لا يمكن لأي خوارزمية حلّها أبدًا.
مقارنة الحالات المولّدة ببيانات التشغيل
هذا هو اختبار الواقعية الأهم. صدّر مجموعة صغيرة من السمات لكل مسار أو لكل ممر من كل من حالاتك المولّدة ومجموعة بيانات مرجعية، ثم قارن التوزيعات وليس المتوسطات فقط.
import pandas as pd
generated = pd.read_csv("outputs/generated_lane_features.csv")
reference = pd.read_csv("data/reference_lane_features.csv")
for column in ["stops_per_route", "lane_distance_km", "load_utilisation"]:
print(column)
print(" generated:", generated[column].describe()[["mean", "std"]].to_dict())
print(" reference:", reference[column].describe()[["mean", "std"]].to_dict())تطابق المتوسطات مع تباعد غير متطابق في الانحرافات المعيارية راية حمراء: فهو عادةً يعني أن المولّد ينتج حالة متوسطة معقولة وتباعدًا غير معقول. وغالبًا ما تكون الحلّالات أكثر حساسية للتباين من النزعة المركزية.
اختبار الإجهاد لحلّال عبر أنظمة مختلفة
بمجرد أن تكون لديك مجموعة حالات، فإن التجربة الطبيعية هي مسح الأنظمة. وبدلًا من رقم واحد، أبلغ عن منحنى.
import subprocess, time, json
from pathlib import Path
results = []
for inst in sorted(Path("instances").glob("baseline_*.json")):
start = time.perf_counter()
proc = subprocess.run(
["python", "-m", "your_solver", "--instance", str(inst),
"--time-limit", "60"],
capture_output=True, text=True,
)
elapsed = time.perf_counter() - start
results.append({
"instance": inst.name,
"seconds": round(elapsed, 3),
"ok": proc.returncode == 0,
})
Path("outputs/solver_sweep.json").write_text(json.dumps(results, indent=2))شغّل هذا عند عدة قيم لمعامل مقياس الطلب وستحصل على منحنى تحجيم — أكثر إفادة بكثير من درجة تجميعية واحدة، وأصعب بكثير في التلاعب.
التحقق من الواقعية قبل أن تثق بمعيار
المولّد الذي يدّعي الواقعية يستدعي سؤالًا محددًا: واقعي وفقًا لماذا؟ أربعة فحوص تستحق أن تُؤسَّس.
فحص بنيوي. ارسم شبكة المحاور. هل المنشآت متجمعة على طول ممرات معقولة، أم متناثرة كما لو كانت بأخذ عينات منتظم؟ الانتظام هو الافتراضي الذي يجب أن تتغلب عليه الواقعية.
فحص توزيعي. قارن التوزيعات الحدية لحجم الطلب ووقت الخدمة ومسافة الممر مع البيانات المرجعية. استخدم مخططات الكميات، وليس الإحصاءات الموجزة فقط.
فحص القيود. تحقق من أن القيود الضيقة تُلزم فعليًا. إذا لم تنشط حدود ساعات السائق أبدًا، فالمولّد لا يمارس الجزء من المشكلة الذي يجعل الميل الأوسط صعبًا.
فحص التمييز. تأكد من أن الحالات تفصل بين الخوارزميات. المعيار الذي تحصل فيه كل طريقة معقولة على النتيجة نفسها يقيس الضوضاء. وبالمقابل، الحالات التي تكون كلها سهلة تافهة أو كلها غير قابلة للحل لا تحمل إشارة أيضًا.
المولّد الذي يجتاز الفحوص البنيوية والتوزيعية لكنه يفشل في فحص التمييز هو محاكاة أمينة للشيء الخطأ — وهو نمط فشل مهم يجب مراقبته.
حيث ينتهي الدليل
الصدق بشأن الحدود جزء من المحتوى التقني. الحقائق المتحقق منها ضيقة: نشرت Google Research مولّد حالات واقعيًا للوجستيات الميل الأوسط، تحت عنوان "MilleMiglia: A realistic instance generator for middle-mile logistics"، على الرابط المذكور أعلاه، مع سجل مصدر بطابع زمني 2026-09-18T17:46:09.000Z.
كل شيء آخر في هذه المقالة — قالب المعاملات، وشيفرة منصة الاختبار، وقائمة التحقق، وقائمة المتطلبات — هو ممارسة هندسية معيارية للتعامل مع أي مولّد حالات في هذا المجال. وهو مقدَّم كسقالات للتكييف، وليس كتوثيق لواجهة MilleMiglia.
تترتب على ذلك نتيجتان عمليتان. أولًا، تعامل مع المصدر الرسمي كمرجع موثوق للواجهة؛ لا تنشر أوامر نائبة كما لو كانت حقيقية. ثانيًا، عند نشر النتائج، استشهد بإصدار المولّد والبذرة صراحةً، لأن ذلك هو ما يجعل المقارنة قابلة لإعادة الإنتاج.
الخاتمة
يعالج MilleMiglia فجوة منهجية حقيقية: فقد تم قياس تحسين الميل الأوسط إلى حد كبير على حالات بُنيت لعائلات مشكلات أخرى. والمولّد الموجّه نحو حالات ميل أوسط واقعية يمنح الباحثين وسيلة لاختبار الخوارزميات مقابل بنية المحور والعمود، والطلب المجمّع غير المتجانس، وقيود الوقت التشغيلية — والأهم من ذلك، القيام بذلك بشكل قابل لإعادة الإنتاج عبر مشاركة البذور بدلًا من مجموعات البيانات.
سير العمل العملي غير مبهر لكنه فعّال. اعزل البيئة، وثبّت الالتزام، وبذّر كل شيء، وامسح التكوينات بدلًا من نقاط مفردة، وتحقق من الواقعية على أربعة محاور: البنية، والتوزيع، وإلزام القيود، والقدرة التمييزية. وأثمن مخرجات المولّد ليست أي حالة فردية، بل المنحنى الذي يرسمه حلّالك عبر عائلة منها — والإبلاغ الأمين عن النقطة التي يستوي فيها ذلك المنحنى.



