عرّف المدخلات والمخرجات
سير العمل المُدار يشتغل مثل دالة Python تعمل على حوسبة بعيدة. قارن دالة محلية بنفس الكود كسير عمل:
Local Python
def my_workflow(circuits, shots=4096):
...
return {"counts": counts}
result = my_workflow(circuits=circuits, shots=1024)
في سير العمل المُدار، run() هو الاستدعاء، وget_arguments() يعطي سير العمل معاملاته، وsave_result() هو عبارة return الخاصة به. ولأن سير العمل يعمل كوظيفة منفصلة على حوسبة بعيدة، فهو يختلف عن استدعاء دالة محلية في ثلاث نواحٍ:
-
العميل يرمّز الوسائط والنتائج. كل قيمة لازم تكون قيمة يقدر العميل يرمّزها، وبعض الأنواع تتغير. راجع قراءة الوسائط في سير العمل.
-
الاستدعاء لا ينتظر.
run()يرجع وظيفة فورًا، وتقرأ النتيجة لاحقًا باستخدامjob.result(). راجع حفظ النتائج. -
لا شيء يتحقق من الاستدعاء. دالة Python ترفض أي وسيط مسمّى غير معروف، لكن سير العمل يتجاهله ما لم تُرفق مخططًا (schema). راجع أفضل ممارسة: تحقق من الوسائط باستخدام مخطط.
اقرأ الوسائط في سير العمل
مرّر المدخلات إلى سير العمل من دفتر الملاحظات المحلي:
run_workflow_remotely.ipynb
job = my_workflow.run(circuits=circuits, shots=1024)
لتفعيل ذلك، عدّل كود سير العمل ليجلب الوسائط باستخدام get_arguments()، الذي يرجع قاموسًا مفاتيحه هي أسماء الوسائط:
source_files/my_workflow.py
from qiskit_serverless import get_arguments
arguments = get_arguments()
circuits = arguments["circuits"]
shots = arguments.get("shots", 4096)
القاموس يحتوي فقط على الوسائط التي مرّرها التشغيل، لذلك استخدم arguments.get() مع قيمة افتراضية للوسائط الاختيارية. تقدر تستخدم أي اسم وسيط ما عدا الكلمات المحمية config وfunction_size، لأن run() يستخدمها مباشرة. كما أن run() يرفض compute_profile. لاختيار موارد الحوسبة، مرّر function_size.
لقراءة الحجم الذي تعمل به الوظيفة، استدعِ get_function_size() في كود سير العمل:
source_files/my_workflow.py
from qiskit_serverless import get_function_size
size = get_function_size() # For example, "M"
إذا لم يمرّر التشغيل function_size، يرجع get_function_size() الحجم الافتراضي لسير العمل. راجع اختر حجمًا لكل تشغيل.
أنواع الوسائط المدعومة
كل قيمة لازم تكون قابلة للتسلسل إلى JSON، أو كائنًا يقدر العميل يرمّزه، مثل دارة Qiskit أو مصفوفة NumPy. بما أن العميل يرمّز كل وسيط قبل إرسال الوظيفة وget_arguments() يفك ترميزه داخل سير العمل، يستلم سير العمل كائن Python الأصلي.
العميل يرسل int وfloat وstr وbool وNone وقوائم أو قواميس منها كما هي. ويرمّز الكائنات الأخرى، مثل QuantumCircuit أو numpy.ndarray أو عدد complex، كقاموس فيه وسم __type__ وقيمة __value__. تحتاج هذه الصيغة لما تكتب مخطط وسائط.
لأن JSON ليس فيه مكافئ لبعض الأنواع، تتغير هذه الأنواع أثناء الترميز. الـ tuples تصل كقوائم، ومفاتيح القواميس تصل كسلاسل نصية، وأعداد NumPy الصحيحة مثل np.int64 تصل كـ int. إذا لم يستطع العميل ترميز قيمة، مثل bytes أو np.float32، يرفع run() الاستثناء TypeError ولا ينشئ وظيفة.
بعض الكائنات لا يمكن تمريرها كوسائط. مثلًا، مرّر اسم الـ backend كسلسلة نصية بدلًا من كائن Backend، وأنشئ الـ backend داخل سير العمل. راجع تشغيل وظائف كمية من سير عمل.
حدود الحجم
وسائط تشغيل واحد يمكن أن تصل إلى 32 ميغابايت بعد الترميز. مثلًا، دارة واحدة من 100 كيوبت وعمق 100 تُرمَّز إلى حوالي 39 كيلوبايت، فيستطيع تشغيل واحد أخذ دفعة من حوالي 800 دارة من هذا النوع. للمدخلات الأكبر، ارفع ملفًا ومرّر اسمه كوسيط.
احفظ النتائج
استخدم save_result() في نهاية سير العمل لإرجاع نتيجة إلى دفتر الملاحظات. اقرأ النتيجة في دفترك باستخدام job.result():
run_workflow_remotely.ipynb
result = job.result()
لتفعيل ذلك، استدعِ save_result() في كود سير العمل:
source_files/my_workflow.py
from qiskit_serverless import save_result
if not save_result({"counts": counts, "circuit": isa_circuit}):
raise ValueError("The result could not be encoded.")
save_result() يرمّز النتيجة بنفس الطريقة التي يرمّز بها العميل الوسائط، وjob.result() يفك ترميزها. لذلك ممكن أن تحتوي النتيجة على أنواع الوسائط المدعومة، مثل الدارات ومصفوفات NumPy ونتيجة وظيفة primitive، مع نفس تغييرات الأنواع.
-
تحقق من القيمة المرجعة. إذا لم يستطع
save_result()ترميز القيمة، يسجّل تحذيرًا ويرجعFalseبدلًا من رفع خطأ. بدون فحص، ممكن تنتهي الوظيفة بالحالةDONEومن غير نتيجة. -
آخر
save_result()فقط هو الذي يُخزَّن. كل استدعاء يستبدل النتيجة المحفوظة. لإرجاع عدة قيم، احفظها في قاموس واحد، أو اكتبها في السجلات. -
القاموس الفارغ يعني لا نتيجة. إذا لم يستدعِ سير العمل
save_result()أبدًا، يرجعjob.result()القيمة{}.
لإرجاع ملفات، مثل مصفوفات كبيرة أو رسوم بيانية، احفظها في تخزين الملفات وأرجع أسماءها في النتيجة.
أفضل ممارسة: تحقق من الوسائط باستخدام مخطط
كأفضل ممارسة، أرفق JSON Schema بكل سير عمل يأخذ وسائط. المخطط يصف الوسائط التي يقبلها سير العمل، وسير العمل يتحقق من وسائط كل تشغيل مقابله قبل أن ينشئ الوظيفة. إذا لم تتطابق الوسائط، يفشل run() فورًا مع خطأ يسمّي الحقل الخاطئ، ولا ينشئ سير العمل وظيفة. بدون مخطط، تصل الوسائط الخاطئة إلى سير العمل بعد بدء الوظيفة، ويتجاهل سير العمل اسم وسيط مكتوبًا بشكل خاطئ.
اضبط المخطط باستخدام arguments_schema عند رفع سير العمل. مرّر قاموسًا، وليس سلسلة JSON نصية:
run_workflow_remotely.ipynb
from qiskit_ibm_catalog import ManagedWorkflow, ManagedWorkflowClient
arguments_schema = {
"type": "object",
"additionalProperties": False,
"required": ["circuits"],
"properties": {
"circuits": {
"type": "array",
"minItems": 1,
"items": {"$ref": "#/$defs/circuit"},
},
"shots": {"type": "integer", "minimum": 1, "maximum": 100000},
"backend_name": {"type": "string", "minLength": 1},
},
"$defs": {
"circuit": {
"type": "object",
"required": ["__type__", "__value__"],
"properties": {
"__type__": {"const": "QuantumCircuit"},
"__value__": {"type": "string"},
},
}
},
}
client = ManagedWorkflowClient()
my_workflow = client.upload(
ManagedWorkflow(
title="my-workflow",
entrypoint="my_workflow.py",
working_dir="./source_files/",
arguments_schema=arguments_schema,
)
)
انتبه للنقاط التالية عند كتابة مخطط:
-
صِف الصيغة المرمّزة لكائنات Qiskit وNumPy. المخطط يتحقق من الوسائط كما يرسلها العميل، لذلك الدارة هي كائن مثل
{"__type__": "QuantumCircuit", "__value__": "<base64-encoded QPY>"}، وليست سلسلة نصية أو مصفوفة. تحقق من وسم__type__، وعامل__value__كسلسلة نصية معتمة. المخطط الذي يصف الدارة كسلسلة نصية يرفض كل تشغيل صالح. -
اضبط
"additionalProperties": false. بدون هذا الإعداد، يتجاهل سير العمل اسم وسيط مكتوبًا بشكل خاطئ، مثلshotz، بدون تحذير، ويعمل بقيمته الافتراضية. صرّح بكل وسيط يقبله سير العمل قبل أن تفعّل هذا الإعداد. -
علّم الوسائط كمطلوبة. أضف كل وسيط يقرأه سير العمل بدون قيمة افتراضية إلى
required.
إذا لم تتطابق الوسائط مع المخطط، رسالة الخطأ تسمّي القاعدة وموقع القيمة الخاطئة:
'type' validation failed, schema requires 'integer': rejected value '4096'
path: ['shots']
المخطط يتحقق من نوع كل وسيط وشكله ونطاقه. المخطط لا يقدر يتحقق إذا كان وسيطان صالحان يتعارضان، أو إذا كانت الدارة تناسب backend. أبقِ هذه الفحوصات في كود سير العمل.
حدّث المخطط أو احذفه
-
لاستبدال المخطط، اضبط
arguments_schemaجديدًا وارفع سير العمل مرة ثانية. -
لحذف المخطط، ارفع سير العمل مع
arguments_schema={}. -
إذا رفعت سير العمل بدون
arguments_schema، يحتفظ سير العمل بالمخطط المخزَّن.
لرؤية المخطط الساري، اقرأ my_workflow.arguments_schema. القيمة None تعني أن سير العمل ليس له مخطط.
تحقق من الوسائط قبل التشغيل
استدعِ validate_arguments() للتحقق من مجموعة وسائط مقابل المخطط بدون بدء وظيفة:
run_workflow_remotely.ipynb
my_workflow.validate_arguments({"circuits": [circuit], "shots": 1024})
# {'valid': True}
إذا كانت الوسائط غير صالحة، يرفع validate_arguments() الاستثناء QiskitServerlessException مع نفس الرسالة التي يرجعها run() لتلك الوسائط. إذا لم يكن لسير العمل مخطط، فأي وسائط صالحة. النتيجة الصالحة لا تضمن نجاح run()، لأن run() يفحص أيضًا حدودًا أخرى، مثل عدد الوظائف النشطة.
قواعد المخطط
سير العمل المُدار يرفض الرفع إذا خالف المخطط أيًا من القواعد التالية:
-
يمكن أن يصل حجم المخطط إلى 64 كيبي بايت، مع حتى 64 مستوى تداخل وحتى 200 مخطط فرعي.
-
يجب أن تشير المراجع إلى داخل نفس المخطط، مثل
#/$defs/circuit، ويجب أن يُحَل كل مرجع. المراجع إلى عناوين URL أو ملفات غير مسموحة. -
يجب أن يكون المخطط كائنًا أو قيمة منطقية.
تجنّب أيضًا المشاكل التالية، التي لا يلتقطها الرفع:
-
محدِّدات الكمية المتداخلة في
pattern. نمط مثل^(a+)+$ممكن يأخذ وقتًا طويلًا جدًا للتقييم. سير العمل يوقف التحقق بعد حد وقت CPU ويرفض التشغيل. اكتب أنماطًا بدون محدِّد كمية حول مجموعة تحتوي أصلًا على واحد، مثل^ibm_[a-z0-9_]{1,40}$. -
falseكمخطط. المخططfalseيرفض كل تشغيل. لإزالة التحقق، استخدم{}.
استكشاف أخطاء المخطط وإصلاحها
| المشكلة | السبب |
|---|---|
| كل تشغيل يُرفض بسبب وسيط دارة. | المخطط يصف الدارة كسلسلة نصية أو مصفوفة. صِفها ككائن مع وسم __type__. |
| تشغيلات صالحة تُرفض. | additionalProperties هو false، والمخطط لا يصرّح بوسيط يمرّره التشغيل. path في الخطأ يسمّي الوسيط. |
| تشغيلات غير صالحة لا تُرفض. | سير العمل ليس له مخطط. تحقق من my_workflow.arguments_schema. |
| لا يُفحص أي تشغيل، رغم أنك ضبطت مخططًا. | المخطط مُرِّر كسلسلة JSON نصية. مرّر قاموسًا. |
الرفع يفشل مع خطأ 400. | المخطط يخالف إحدى قواعد المخطط. |
| الخطأ يقول إن التحقق استغرق وقت CPU كثيرًا. | pattern فيه محدِّدات كمية متداخلة. أعد كتابة النمط. |