علوم وفضاء

vectorize: هل تُستدعى الدالة مرة واحدة لكل عنصر؟

ثلاثة عناصر وأربعة نداءات في الإعداد الأول.
سجل قيم الدالة في تجربتنا.

نضيف سجلًا إلى دالة صحيحة بسيطة، ثم نغلفها بثلاثة إعدادات. نقارن عدد النداءات وترتيب قيمها، مع حفظ المخرجات نفسها في كل تشغيل.

الاستكشاف جزء من سجل الدالة

عند غياب otypes، تستدعي vectorize الدالة على أول عنصر لاستنتاج الخرج؛ cache=True تحتفظ بنتيجة هذا النداء. [1] [2]

في التنفيذ المحدد، تستعمل الحلقة القيمة المخزنة مرة واحدة بدل استدعاء الدالة لذلك الموضع. [2]

سنراقب دالة لها ناتج صحيح ثابت لكل قيمة وسجل نداءات منفصل. نقرأ القائمة الناتجة والسجل كدليلين مختلفين؛ اتفاق الناتج لا يكشف وحده عدد مرات حسابه.

ننشئ ثلاثة أغلفة مستقلة: استنتاج بلا تخزين، واستنتاج مع تخزين، وتحديد نوع الخرج صراحة. هذا يحفظ نقطة بدء السجل لكل إعداد.

ثلاث قيم وناتج واحد

شغّلنا NumPy 2.3.5 على CPython 3.12.14. أنشأنا a=np.array([-3,1,4],dtype=np.int64). الأعداد مؤلفة؛ لم ننقل قائمة من مثال المصدر.

عرفنا own(x) بحيث تحسب value=int(x)، وتضيف value إلى log، ثم تعيد 3*value-2. التحويل إلى int يجعل سجلنا قائمة قيم صحيحة؛ لم نجعله تقريرًا عن نوع الوسيط عند كل نداء.

حسب مرجع Python المتوقع مباشرة: 3*(-3)-2=-11، و3*1-2=1، و3*4-2=10. القائمة المتوقعة [-11,1,10] مكتوبة من القاعدة المؤلفة قبل استدعاء الغلاف.

في كل إعداد من إعدادات مثالنا، أعاد استدعاء الغلاف هذه القائمة من int64 وبالشكل (3,). مساواة القيم هنا تسمح لنا بمتابعة اختلاف السجل دون إقحام دالة تغير ناتجها حسب عدد النداءات.

نكتب أسماء إعداداتنا مع النتائج. كلمة vectorize وحدها لا تحدد هل تركنا otypes غائبة، أو مررنا cache=True، أو ثبتنا نوع الخرج صراحة.

موضع أول يظهر مرتين

بدأنا بسجل فارغ وأنشأنا np.vectorize(own,cache=False)، دون otypes. استدعينا الغلاف على a. صار log=[-3,-3,1,4]؛ طوله 4 رغم أن طول a يساوي 3.

لكتابة مرجع السجل لهذا الإعداد، وضعنا قيمة a الأولى قبل قائمة القيم كلها: [-3]+[-3,1,4]. هي قائمة من أربعة مواضع؛ تكرار -3 ليس عنصرًا رابعًا أضفناه إلى مصفوفة المدخل.

أنشأنا دالة وسجلًا وغلافًا جديدًا مع np.vectorize(own,cache=True)، وبقيت otypes غائبة. صار السجل [-3,1,4] وطوله 3؛ بقيت المخرجات [-11,1,10] كما كانت.

في الضبط الثالث بدأنا من جديد واستعملنا np.vectorize(own,otypes=[np.int64],cache=False). سجلنا أيضًا [-3,1,4] مع المخرجات نفسها ونوعها وشكلها نفسيهما في مثالنا.

فحصنا بقاء بايتات a كما كانت بعد كل غلاف. السجل أثر جانبي مقصود في دالتنا؛ لم نسجل تعديل مصفوفة المدخل أو اختلاف قيم الخرج في هذه المقارنة.

كذلك افصل عدد استدعاءات الغلاف عن عدد نداءات own. المثال يتضمن ثلاثة استدعاءات أغلفة مستقلة، لكن مجموع نداءات الدوال المغلفة 4+3+3=10. إنشاء كائن الغلاف لا يدخل عدد استدعاءاته على a.

التخزين وتحديد الخرج يعطيان ثلاثة نداءات.
المخرجات نفسها في الإعدادات الثلاثة.

شبكة السجلات وحدودها

للشبكة أخذنا أطوالًا من واحد إلى أربعة، وكل عنصر أحد الأعداد -3 و0 و2. عدد قوائم القيم 3+9+27+81=120. لكل قائمة جربنا ميلًا 1 أو 3 وإزاحة -2 أو 1، فصار العدد 120*2*2=480 حالة.

في كل حالة تحسب الدالة slope*int(x)+offset وتضيف int(x) إلى سجل مستقل. القيم المتوقعة حسابات Python صحيحة، والسجل المتوقع قائمة القيم مع أولها مكررًا في إعداد الاستنتاج بلا تخزين، أو قائمة القيم وحدها في الضبطين.

أضفنا المثال [-3,1,4] مع الميل 3 والإزاحة -2. القيمتان 1 و4 خارج مجموعة عناصر الشبكة؛ لذلك هو حالة إضافية، والإجمالي 481 حالة.

لكل حالة ثلاث مجموعات تحقق: القيم والسجل وعدده وشكل الخرج ونوعه وبايتات المدخل في كل إعداد. مرت 481*3=1443 مجموعة، واستدعينا غلافًا جديدًا على المتجه مرة لكل مجموعة؛ الإجمالي 1443 استدعاء غلاف.

مجموع أطوال متجهات الشبكة 4*(1*3+2*9+3*27+4*81)=1704. لإعدادات حالة بطول n، المرجع يعد (n+1)+n+n=3*n+1 نداءً للدوال المغلفة؛ لذا مجموع الشبكة 3*1704+480=5592، ومع نداءات المثال العشرة يصير 5602.

النطاق متجهات int64 غير فارغة بطول واحد إلى أربعة وقيم صغيرة منتهية، ووسيط موضعي واحد وخرج Python صحيح منفرد. لكل مجموعة غلاف وسجل جديدان؛ لا تتراكم سجلات بين إعدادين.

لم نفحص غلافًا يعاد استعماله أو تخزينًا دائمًا، أو خرجًا عدديًا يعتمد على حالة السجل، أو signature أو excluded أو عدة مخرجات أو متجهًا فارغًا أو بثًا أو أنواع مدخل أخرى. لم نقس الأداء أو نشغّل إصدارًا أقدم.

عند قراءة سجل تطبيقك، احفظ تعريف الدالة وإعداد الغلاف والقيم والترتيب وعدد النداءات. افحص ذلك العدد مباشرة إذا كان يهمك، ثم راجع المخرجات في دليل مستقل.

الإعدادالسجلالخرج
otypes omitted / cache=False[-3,-3,1,4][-11,1,10]
otypes omitted / cache=True[-3,1,4][-11,1,10]
otypes=[np.int64] / cache=False[-3,1,4][-11,1,10]

للتوسع: غلاف vectorize ونوع الخرج.

المراجع

  1. NumPy vectorize
  2. NumPy 2.3.5 vectorize implementation

أُعدّ هذا المقال بمساعدة الذكاء الاصطناعي، وخضع لمراجعة ذاتية بالأداة نفسها. لا يُنسب إلى مراجع بشري أو خبير مستقل. منهجية المحتوى.