NumPy 1.14 - NumPy C Code Explanations

NumPy C कोड स्पष्टीकरण




numpy

NumPy C कोड स्पष्टीकरण

जब आप अपने उद्देश्य को भूल चुके होते हैं तो कट्टरता आपके प्रयासों को कम करती है। - जॉर्ज संतायना

एक प्राधिकरण एक ऐसा व्यक्ति है जो आपको वास्तव में जानने की तुलना में कुछ के बारे में अधिक बता सकता है। - अज्ञात

यह अध्याय कोड के कुछ नए टुकड़ों के पीछे के तर्क को समझाने का प्रयास करता है। इन स्पष्टीकरणों के पीछे उद्देश्य यह है कि किसी को कार्यान्वयन के पीछे के विचारों को समझने में सक्षम होने के लिए कोड में घूरने की तुलना में कुछ और आसानी से। शायद इस तरह, एल्गोरिदम को बेहतर बनाया जा सकता है, उधार लिया जा सकता है, और / या अनुकूलित किया जा सकता है।

मेमोरी मॉडल

Ndarray का एक मूलभूत पहलू यह है कि एक सरणी को किसी स्थान पर शुरू होने वाली मेमोरी के "चंक" के रूप में देखा जाता है। इस मेमोरी की व्याख्या स्ट्राइड सूचना पर निर्भर करती है। में प्रत्येक आयाम के लिए ए एन -आयामी आयाम, एक पूर्णांक (स्ट्राइड) यह निर्धारित करता है कि उस आयाम में अगले तत्व को प्राप्त करने के लिए कितने बाइट को छोड़ देना चाहिए। जब तक आपके पास एक एकल-खंड सरणी नहीं होती है, तब एक सरणी के माध्यम से ट्रैवर्सिंग करते समय इस कठोर जानकारी से परामर्श किया जाना चाहिए। कोड लिखना मुश्किल नहीं है जो स्ट्राइड्स को स्वीकार करता है, आपको बस (चार *) पॉइंटर्स का उपयोग करना होगा क्योंकि स्ट्राइड बाइट्स की इकाइयों में हैं। यह भी ध्यान रखें कि स्ट्रेट्स को तत्व आकार के यूनिट-गुणक नहीं होना चाहिए। यह भी याद रखें कि यदि सरणी के आयामों की संख्या 0 है (कभी-कभी रैंक-0 सरणी कहा जाता है), तो स्ट्राइड्स और आयाम चर NULL होते हैं।

PyArrayObject के PyArrayObject और डाइमेंशन मेंबर में मौजूद PyArrayObject के PyArrayObject , डेटा को कैसे एक्सेस किया जा सकता है, इस बारे में PyArrayObject में महत्वपूर्ण जानकारी होती है। विशेष रूप से, NPY_ARRAY_ALIGNED ध्वज तब सेट किया जाता है जब मेमोरी डेटा-टाइप ऐरे के अनुसार उपयुक्त सीमा पर होती है। यहां तक ​​कि अगर आपके पास स्मृति का एक सन्निहित हिस्सा है, तो आप यह नहीं मान सकते हैं कि किसी तत्व के लिए डेटा-प्रकार-विशिष्ट पॉइंटर को सुरक्षित करना सुरक्षित है। केवल अगर NPY_ARRAY_ALIGNED ध्वज सेट है, तो यह एक सुरक्षित ऑपरेशन है (कुछ प्लेटफार्मों पर यह काम करेगा लेकिन दूसरों पर, जैसे सोलारिस, यह एक बस त्रुटि का कारण होगा)। यदि आप सरणी के मेमोरी क्षेत्र में लिखने की योजना रखते हैं, तो NPY_ARRAY_WRITEABLE को भी सुनिश्चित किया जाना चाहिए। एक अपरिवर्तनीय स्मृति क्षेत्र के लिए एक संकेतक प्राप्त करना भी संभव है। कभी-कभी, NPY_ARRAY_WRITEABLE ध्वज सेट नहीं होने पर मेमोरी क्षेत्र में NPY_ARRAY_WRITEABLE सिर्फ असभ्य होगा। अन्य बार यह प्रोग्राम क्रैश का कारण बन सकता है ( जैसे एक डेटा-क्षेत्र जो केवल पढ़ने के लिए मेमोरी-मैप की गई फ़ाइल है)।

डेटा-टाइप एनकैप्सुलेशन

डेटा प्रकार ndarray का एक महत्वपूर्ण अमूर्त है। ऑपरेशन डेटा-प्रकार को देखने के लिए महत्वपूर्ण कार्यक्षमता प्रदान करेगा जो कि सरणी पर संचालित करने के लिए आवश्यक है। यह कार्यक्षमता PyArray_Descr संरचना के 'f' सदस्य द्वारा इंगित फ़ंक्शन पॉइंटर्स की सूची में प्रदान की गई है। इस प्रकार, 'f' सदस्य में उपयुक्त फ़ंक्शन पॉइंटर्स के साथ PyArray_Descr संरचना प्रदान करके डेटा-प्रकारों की संख्या को बस बढ़ाया जा सकता है। अंतर्निहित प्रकारों के लिए कुछ अनुकूलन हैं जो इस तंत्र को बाय-पास करते हैं, लेकिन डेटा-टाइप अमूर्त का बिंदु नए डेटा-प्रकारों को जोड़ने की अनुमति देना है।

अंतर्निहित डेटा-प्रकारों में से एक, शून्य डेटा-प्रकार मनमाने ढंग से संरचित प्रकारों के लिए अनुमति देता है जिसमें सरणी के तत्वों के रूप में 1 या अधिक फ़ील्ड होते हैं। एक फ़ील्ड वर्तमान संरचित प्रकार में ऑफसेट के साथ बस एक अन्य डेटा-प्रकार ऑब्जेक्ट है। मनमाने ढंग से नेस्टेड फ़ील्ड का समर्थन करने के लिए, शून्य प्रकार के लिए डेटा-टाइप एक्सेस के कई पुनरावर्ती कार्यान्वयन कार्यान्वित किए जाते हैं। एक सामान्य मुहावरा है कि शब्दकोश के तत्वों के माध्यम से चक्र करें और दिए गए ऑफसेट पर संग्रहीत डेटा-प्रकार ऑब्जेक्ट के आधार पर एक विशिष्ट ऑपरेशन करें। ये ऑफ़सेट मनमानी संख्या हो सकती है। इसलिए, गलत संरेखित आंकड़ों के सामना करने की संभावना को पहचानना चाहिए और यदि आवश्यक हो तो इसे ध्यान में रखा जाना चाहिए।

एनडी Iterators

न्यूमपी कोड में बहुत आम ऑपरेशन एक सामान्य, तार वाले, एन-आयामी सरणी के सभी तत्वों पर पुनरावृति करने की आवश्यकता है। एक सामान्य-उद्देश्य एन-आयामी लूप का यह ऑपरेशन एक एटरेटर ऑब्जेक्ट की धारणा में सार है। एन-डायमेंशनल लूप लिखने के लिए, आपको केवल एक ndarray से एक इटेरेटर ऑब्जेक्ट बनाना होगा, इट्रेटर ऑब्जेक्ट संरचना के डेटाटेपर सदस्य के साथ काम करना होगा और अगले ऑब्जेक्ट पर जाने के लिए PyArray_ITER_NEXT ऑब्जेक्ट पर मैक्रो PyArray_ITER_NEXT (यह) कॉल करना होगा। "अगला" तत्व हमेशा सी-सन्निहित क्रम में होता है। मैक्रो पहले विशेष C-contiguous, 1-D और 2-D मामलों को कवर करके काम करता है जो बहुत ही सरलता से काम करते हैं।

सामान्य स्थिति के लिए, पुनरावृति पुनरावृत्ति ऑब्जेक्ट में समन्वय काउंटर की सूची का ट्रैक रखकर काम करता है। प्रत्येक पुनरावृत्ति पर, अंतिम समन्वय काउंटर बढ़ाया जाता है (0 से शुरू)। यदि यह काउंटर उस आयाम में सरणी के आकार (एक पूर्व-संकलित और संग्रहीत मूल्य) से एक से कम है, तो काउंटर बढ़ा दिया जाता है और डेटापट्ट सदस्य उस आयाम में स्ट्रैड द्वारा बढ़ाया जाता है और मैक्रो समाप्त होता है। यदि किसी आयाम का अंत हो जाता है, तो अंतिम आयाम के लिए काउंटर शून्य पर रीसेट हो जाता है और डेटापट्ट को उस आयाम की शुरुआत में वापस ले जाया जाता है, जिससे उस आयाम में तत्वों की संख्या से कम से कम स्ट्राइड वैल्यू गुना घटाकर (यह है) पूर्व-संगृहीत और पुनरावृत्त वस्तुकारक के सदस्य में संग्रहीत)। इस स्थिति में, मैक्रो समाप्त नहीं होता है, लेकिन एक स्थानीय आयाम काउंटर को हटा दिया जाता है ताकि अगला-से-अंतिम आयाम उस भूमिका को बदल दे जो पिछले आयाम ने निभाई थी और पहले से वर्णित परीक्षणों को अगले-से-अंतिम पर फिर से निष्पादित किया गया है आयाम। इस तरह, मनमाने ढंग से स्ट्राइडिंग के लिए डेटाटैप को उचित रूप से समायोजित किया जाता है।

PyArrayIterObject संरचना के निर्देशांक सदस्य वर्तमान एनडी काउंटर को बनाए रखता है जब तक कि अंतर्निहित सरणी C-contiguous नहीं है, जिस स्थिति में समन्वय की गिनती पास-पास है। PyArrayIterObject का इंडेक्स मेंबर PyArrayIterObject के वर्तमान फ्लैट इंडेक्स पर नज़र रखता है। इसे PyArray_ITER_NEXT मैक्रो द्वारा अद्यतन किया गया है।

प्रसारण

न्यूमेरिक में, ufuncobject.c में गहरे दफन कोड की कई लाइनों में प्रसारण लागू किया गया था। NumPy में, प्रसारण की धारणा को समाप्त कर दिया गया है ताकि इसे कई स्थानों पर प्रदर्शित किया जा सके। प्रसारण PyArray_Broadcast फ़ंक्शन द्वारा नियंत्रित किया जाता है। इस फ़ंक्शन के लिए पास होने के लिए PyArrayMultiIterObject (या ऐसी कोई चीज़ जो बाइनरी समतुल्य है) की आवश्यकता होती है। PyArrayMultiIterObject प्रसारण आयाम के आकार और प्रत्येक आयाम में प्रसारण परिणाम के कुल आकार के साथ नज़र रखता है। यह प्रसारित किए जा रहे सरणियों की संख्या और प्रत्येक प्रसारित किए जा रहे सरणियों के लिए एक पुनरावृत्ति के लिए एक सूचक को भी ट्रैक रखता है।

PyArray_Broadcast फ़ंक्शन PyArray_Broadcast ले जाता है जो पहले से ही परिभाषित किया गया है और प्रत्येक आयाम में प्रसारण आकार निर्धारित करने के लिए उनका उपयोग करता है (उसी समय प्रसारित करने वाले PyMultiIter_New को बनाने के लिए तब PyMultiIter_New फ़ंक्शन का उपयोग करें)। फिर, पुनरावृत्तियों को समायोजित किया जाता है ताकि प्रत्येक पुनरावृत्त को लगता है कि यह प्रसारण आकार के साथ एक सरणी पर पुनरावृत्ति कर रहा है। यह पुनरावृत्तियों की संख्या को आयामों और प्रत्येक आयाम में आकार को समायोजित करके किया जाता है। यह काम करता है क्योंकि इट्रेटर स्ट्राइड्स को भी समायोजित किया जाता है। प्रसारण केवल लंबाई -1 आयामों को समायोजित (या जोड़ता है) करता है। इन आयामों के लिए, स्ट्राइड्स वेरिएबल को केवल 0 पर सेट किया जाता है, ताकि उस सरणी पर इट्रेटर के लिए डेटा-पॉइंटर न चले, क्योंकि ब्रॉडकास्टिंग ऑपरेशन विस्तारित आयाम पर संचालित होता है।

विस्तारित आयामों के लिए 0-मूल्यवान स्ट्राइड्स का उपयोग करके न्यूमेरिक में प्रसारण हमेशा लागू किया गया था। यह NumPy में ठीक उसी तरह से किया जाता है। बड़ा अंतर यह है कि अब PyArrayIterObject के सरणी को PyArrayIterObject में ट्रैक रखा जाता है, प्रसारण परिणाम में शामिल PyArrayMultiIterObject का ट्रैक रखा जाता है, और PyArray_BroadCast कॉल व्यापक-कास्टिंग नियमों को लागू करता है।

स्केल स्कार्स

सरणी स्केलर पायथन प्रकारों का एक पदानुक्रम प्रदान करते हैं जो एक सरणी में संग्रहीत डेटा-प्रकार और पायथन-प्रकार के बीच एक-से-एक पत्राचार की अनुमति देते हैं जो किसी तत्व को सरणी से निकाले जाने पर वापस आ जाता है। इस नियम का अपवाद ऑब्जेक्ट सरणियों के साथ बनाया गया था। ऑब्जेक्ट सरणियाँ मनमानी पायथन वस्तुओं का विषम संग्रह हैं। जब आप किसी ऑब्जेक्ट सरणी से आइटम का चयन करते हैं, तो आपको मूल पायथन ऑब्जेक्ट वापस मिल जाता है (और ऑब्जेक्ट सरणी स्केलर मौजूद नहीं होता है लेकिन व्यावहारिक उद्देश्यों के लिए इसका उपयोग शायद ही कभी किया जाता है)।

सरणी स्केलर भी उसी तरीके और विशेषता प्रदान करते हैं, जो इस आशय के साथ सरणियों के रूप में होते हैं कि एक ही कोड का उपयोग मनमाना आयाम (0-आयाम सहित) का समर्थन करने के लिए किया जा सकता है। सरणी स्केलर केवल-पढ़ने योग्य (शून्य) हैं, जो शून्य स्केलर के अपवाद के साथ लिखा जा सकता है, ताकि संरचित सरणी फ़ील्ड सेटिंग अधिक स्वाभाविक रूप से काम करे ([0] ['f1'] = value )।

इंडेक्सिंग

सभी अजगर अनुक्रमण संचालन arr[index] पहले सूचकांक तैयार करने और सूचकांक प्रकार खोजने के द्वारा आयोजित किए जाते हैं। समर्थित सूचकांक प्रकार हैं:

  • पूर्णांक
  • newaxis
  • टुकड़ा
  • अंडाकार
  • पूर्णांक सरणियाँ / सरणी-पसंद (फैंसी)
  • बूलियन (एकल बूलियन सरणी); यदि इंडेक्स के रूप में एक से अधिक बूलियन सरणी है या आकृति बिल्कुल मेल नहीं खाती है, तो बूलियन सरणी को एक पूर्णांक सरणी में बदल दिया जाएगा।
  • 0-डी बूलियन (और पूर्णांक भी); 0-डी बूलियन सरणियां एक विशेष मामला है जिसे उन्नत अनुक्रमण कोड में संभाला जाना है। वे संकेत देते हैं कि 0-d बूलियन सरणी को एक पूर्णांक सरणी के रूप में व्याख्या किया जाना था।

साथ ही स्केलर सरणी विशेष मामला यह दर्शाता है कि एक पूर्णांक सरणी को एक पूर्णांक सूचकांक के रूप में व्याख्या की गई थी, जो महत्वपूर्ण है क्योंकि पूर्णांक सरणी सूचकांक एक प्रतिलिपि को बाध्य करता है, लेकिन यदि स्केलर वापस आ जाता है (पूर्ण पूर्णांक सूचकांक) तो अनदेखा कर दिया जाता है। तैयार इंडेक्स को उन्नत इंडेक्सिंग के लिए बाध्य मानों और प्रसारण त्रुटियों के अपवाद के साथ मान्य होने की गारंटी है। इसमें यह शामिल है कि एक दीर्घवृत्त को उदाहरण के लिए अपूर्ण सूचक के लिए जोड़ा जाता है जब एक दो आयामी सरणी को एक पूर्णांक के साथ अनुक्रमित किया जाता है।

अगला चरण इंडेक्स के प्रकार पर निर्भर करता है जो पाया गया था। यदि सभी आयामों को एक पूर्णांक के साथ अनुक्रमित किया जाता है तो एक स्केलर वापस आ जाता है या सेट हो जाता है। एक एकल बूलियन इंडेक्सिंग सरणी विशेष बूलियन फ़ंक्शन को कॉल करेगी। दीर्घवृत्त या स्लाइस वाले संकेत लेकिन कोई उन्नत अनुक्रमण हमेशा नए स्ट्राइड और मेमोरी ऑफ़सेट की गणना करके पुराने व्यू में दृश्य नहीं बनाएगा। इस दृश्य को या तो लौटाया जा सकता है या, असाइनमेंट के लिए, PyArray_CopyObject का उपयोग करके भरा जा सकता है। ध्यान दें कि PyArray_CopyObject को अन्य असाइनमेंट में अस्थायी सरणियों पर भी बुलाया जा सकता है, जब जटिल ऑब्जेक्ट असाइनमेंट का समर्थन करने के लिए जटिल असाइनमेंट का समर्थन करता है।

उन्नत अनुक्रमण

अब तक सबसे जटिल मामला उन्नत अनुक्रमण है, जिसे विशिष्ट दृश्य आधारित अनुक्रमण के साथ जोड़ा जा सकता है या नहीं। यहाँ पूर्णांक सूचकांकों की व्याख्या दृश्य आधारित के रूप में की गई है। इसे समझने की कोशिश करने से पहले, आप इसकी सूक्ष्मताओं से खुद को परिचित करना चाह सकते हैं। उन्नत अनुक्रमण कोड में तीन अलग-अलग शाखाएँ और एक विशेष मामला होता है:

  • एक अनुक्रमण सरणी है और यह, साथ ही असाइनमेंट सरणी, को तुच्छ रूप से पुनरावृत्त किया जा सकता है। उदाहरण के लिए वे सन्निहित हो सकते हैं। इसके अलावा अनुक्रमणिका सरणी intp प्रकार की होनी चाहिए और असाइनमेंट में मान सरणी सही प्रकार का होना चाहिए। यह विशुद्ध रूप से एक तेज़ पथ है।
  • केवल पूर्णांक सरणी सूचक होते हैं, ताकि कोई सबर्रे मौजूद न हो।
  • देखें आधारित और उन्नत अनुक्रमण मिश्रित है। इस मामले में दृश्य आधारित अनुक्रमण उन उपश्रेणियों के संग्रह को परिभाषित करता है जिन्हें उन्नत अनुक्रमण द्वारा संयोजित किया जाता है। उदाहरण के लिए, arr[[1, 2, 3], :] लंबवत रूप से गिरफ्तार उपग्रहों को गिरफ्तार करके बनाई गई है arr[1, :] arr[2,:] , arr[2,:] , और arr[3, :]
  • एक सबर्रे है लेकिन इसका एक तत्व है। इस मामले को ऐसे संभाला जा सकता है जैसे कि कोई सबर्रे नहीं है, लेकिन सेटअप के दौरान कुछ देखभाल की जरूरत है।

यह तय करना कि क्या मामला लागू होता है, प्रसारण की जाँच करना और आवश्यक ट्रांसपोज़िशन का निर्धारण करना यह सब PyArray_MapIterNew में किया जाता है। स्थापित करने के बाद, दो मामले हैं। यदि कोई सबर्रे नहीं है या इसमें केवल एक ही तत्व है, तो कोई सबर्रे इटरेशन आवश्यक नहीं है और एक इटरेटर तैयार किया जाता है जो सभी अनुक्रमण सरणियों के साथ-साथ परिणाम या मान सरणी को पुनरावृत्त करता है। यदि एक सबर्रे है, तो तीन पुनरावृत्तियों को तैयार किया जाता है। अनुक्रमण सरणियों के लिए एक, परिणाम या मान सरणी के लिए एक (शून्य से इसकी उप-श्रेणी), और मूल और परिणाम / असाइनमेंट सरणी की उप-श्रेणियों के लिए एक। पहले दो पुनरावृत्तियों, बिंदुओं की शुरुआत (या गणना की अनुमति देते हैं) सबरे की शुरुआत में करते हैं, जो फिर सबर्रे पुनरावृत्ति को फिर से शुरू करने की अनुमति देता है।

जब उन्नत सूचकांक एक दूसरे के बगल में होते हैं तो ट्रांसपोज़िंग आवश्यक हो सकता है। सभी आवश्यक ट्रांसपोज़िंग को PyArray_MapIterSwapAxes द्वारा नियंत्रित किया जाता है और जब तक PyArray_MapIterNew को परिणाम आवंटित करने के लिए नहीं कहा जाता है, तब तक कॉलर द्वारा नियंत्रित किया जाता है।

तैयारी के बाद, प्राप्त करना और स्थापित करना अपेक्षाकृत आगे है, हालांकि पुनरावृत्ति के विभिन्न तरीकों पर विचार करने की आवश्यकता है। जब तक कि आइटम प्राप्त करने के दौरान केवल एक इंडेक्सिंग सरणी नहीं होती है, सूचकांकों की वैधता पहले ही जांच ली जाती है। अन्यथा यह अनुकूलन के लिए आंतरिक लूप में ही संभाला जाता है।

यूनिवर्सल फंक्शंस

यूनिवर्सल फ़ंक्शन कॉल करने योग्य ऑब्जेक्ट हैं जो लेते हैं एन आदानों और उत्पादन एम मूल 1-डी लूप्स को लपेटकर आउटपुट जो काम-के-तत्व को पूरी तरह से आसानी से उपयोग करने वाले कार्यों का उपयोग करता है जो मूल रूप से प्रसारण, टाइप-चेकिंग और बफर ज़बरदस्ती, और आउटपुट-तर्क हैंडलिंग को लागू करता है। नए सार्वभौमिक फ़ंक्शंस सामान्य रूप से C में बनाए जाते हैं, हालांकि पाइथन फ़ंक्शंस ( frompyfunc ) से यूफ़नक्स बनाने के लिए एक तंत्र है। उपयोगकर्ता को 1-डी लूप की आपूर्ति करनी चाहिए जो इनपुट स्केलर मानों को लेने के लिए मूल फ़ंक्शन को लागू करता है और परिणामस्वरूप स्केलर को उचित आउटपुट स्लॉट में लागू करता है जैसा कि कार्यान्वयन में बताया गया है।

सेट अप

प्रत्येक ufunc गणना में गणना स्थापित करने से संबंधित कुछ ओवरहेड शामिल हैं। इस ओवरहेड का व्यावहारिक महत्व यह है कि भले ही ufunc की वास्तविक गणना बहुत तेज है, आप सरणी और प्रकार-विशिष्ट कोड लिखने में सक्षम होंगे जो कि ufunc की तुलना में छोटे सरणियों के लिए तेजी से काम करेंगे। विशेष रूप से, 0-डी सरणियों पर कई गणना करने के लिए ufuncs का उपयोग करना अन्य पायथन-आधारित समाधानों की तुलना में धीमा होगा (चुपचाप आयातित स्कैलरमैथ मॉड्यूल सरणी स्केलरों को सटीक रूप से प्रस्तुत करने के लिए मौजूद है, जो काफी कम ओवरहेड के साथ ufunc आधारित गणनाओं का लुक-एंड-फील देता है )।

जब एक ufunc कहा जाता है, तो कई चीजें करनी चाहिए। इन सेटअप ऑपरेशनों से एकत्रित जानकारी को लूप-ऑब्जेक्ट में संग्रहित किया जाता है। यह लूप ऑब्जेक्ट एक सी-संरचना है (जो पायथन ऑब्जेक्ट बन सकता है लेकिन इसे इस तरह से इनिशियलाइज़ नहीं किया जाता है क्योंकि यह केवल आंतरिक रूप से उपयोग किया जाता है)। इस लूप ऑब्जेक्ट में PyArray_Broadcast के साथ उपयोग किए जाने वाले लेआउट की आवश्यकता है ताकि प्रसारण को उसी तरह से संभाला जा सके जिस तरह से इसे कोड के अन्य अनुभागों में संभाला जाता है।

पहली बात यह है कि थ्रेड-विशिष्ट वैश्विक शब्दकोश में बफर-आकार, त्रुटि मास्क और संबंधित त्रुटि ऑब्जेक्ट के लिए वर्तमान मानों को देखना है। त्रुटि मास्क की स्थिति नियंत्रित करती है कि त्रुटि स्थिति पाए जाने पर क्या होता है। यह ध्यान दिया जाना चाहिए कि हार्डवेयर त्रुटि झंडे की जाँच प्रत्येक 1-डी लूप निष्पादित होने के बाद ही की जाती है। इसका मतलब यह है कि यदि इनपुट और आउटपुट सरणियाँ सन्निहित हैं और सही प्रकार की हैं ताकि एक एकल-डी लूप का प्रदर्शन किया जाए, तो झंडे की जाँच तब तक नहीं की जा सकती है जब तक कि सरणी के सभी तत्वों की गणना नहीं की जाती है। इन मूल्यों को एक थ्रेड-विशिष्ट शब्दकोश में देखने में समय लगता है जिसे आसानी से सभी लेकिन बहुत छोटे सरणियों के लिए अनदेखा कर दिया जाता है।

जाँच करने के बाद, थ्रेड-विशिष्ट वैश्विक चर, इनपुट का मूल्यांकन यह निर्धारित करने के लिए किया जाता है कि ufunc कैसे आगे बढ़ना चाहिए और यदि आवश्यक हो तो इनपुट और आउटपुट सरणियों का निर्माण किया जाता है। कोई इनपुट जो सरणियाँ नहीं हैं, सरणियों में परिवर्तित हो जाती हैं (यदि आवश्यक हो तो संदर्भ का उपयोग करके)। कौन से इनपुट स्केलर हैं (और इसलिए 0-डी सरणियों में परिवर्तित किया गया है) नोट किया गया है।

अगला, इनपुट सरणी प्रकारों के आधार पर ufunc के लिए उपलब्ध 1-D छोरों से एक उपयुक्त 1-D लूप का चयन किया जाता है। इस 1-डी लूप का चयन उपलब्ध हस्ताक्षरों के खिलाफ डेटा-प्रकार के इनपुट के हस्ताक्षर से मिलान करने के लिए किया जाता है। बिल्ट-इन प्रकारों के अनुरूप सिग्नेचर को ufunc संरचना के प्रकार सदस्य में संग्रहीत किया जाता है। उपयोगकर्ता-परिभाषित प्रकारों से संबंधित हस्ताक्षर डेटा-प्रकार की संख्या (तर्क-सूची सूची में पहला उपयोगकर्ता-परिभाषित प्रकार) द्वारा कुंजीबद्ध उपयोगकर्ता नाम शब्दकोश में एक CObject रूप में संग्रहीत मुख्य तत्व के साथ फ़ंक्शन-सूचना की एक लिंक्ड-सूची में संग्रहीत होते हैं। कुंजी के रूप में उपयोग किया जाता है)। हस्ताक्षर तब तक खोजे जाते हैं जब तक कि एक हस्ताक्षर नहीं मिल जाता है, जिसमें इनपुट सरणियों को सभी सुरक्षित रूप से डाला जा सकता है (किसी भी स्केलर तर्कों को अनदेखा करना जो परिणाम के प्रकार को निर्धारित करने की अनुमति नहीं है)। इस खोज प्रक्रिया का निहितार्थ यह है कि "कम प्रकार" को "बड़े प्रकार" से नीचे रखा जाना चाहिए जब हस्ताक्षर संग्रहीत किए जाते हैं। यदि कोई 1-डी लूप नहीं मिला है, तो एक त्रुटि की सूचना दी जाती है। अन्यथा, तर्क_सूची को संग्रहीत हस्ताक्षर के साथ अद्यतन किया जाता है - अगर कास्टिंग आवश्यक है और 1-डी लूप द्वारा ग्रहण किए गए आउटपुट प्रकारों को ठीक करना है।

यदि ufunc में 2 इनपुट और 1 आउटपुट है और दूसरा इनपुट एक ऑब्जेक्ट एरे है तो एक विशेष-केस चेक किया जाता है ताकि NotImplemented लौटाया जाए यदि दूसरा इनपुट ndarray नहीं है, तो __array_yaryity__ विशेषता है, और इसमें __r {op है } __ विशेष विधि। इस तरह, पायथन को दूसरी वस्तु को जेनेरिक ऑब्जेक्ट-ऐरे गणनाओं का उपयोग करने के बजाय ऑपरेशन को पूरा करने का मौका देने के लिए संकेत दिया जाता है। यह अनुमति देता है (उदाहरण के लिए) गुणन ऑपरेटर 1-डी लूप को ओवरराइड करने के लिए विरल मैट्रिस।

निर्दिष्ट बफ़र आकार से छोटी इनपुट सरणियों के लिए, प्रतियां सभी गैर-सन्निहित, गलत संरेखित या बाइटऑर्डर सीमाओं से बनी होती हैं ताकि यह सुनिश्चित हो सके कि छोटे सरणियों के लिए, एक एकल लूप का उपयोग किया जाता है। फिर, सभी इनपुट सरणियों के लिए सरणी पुनरावृत्तियों का निर्माण किया जाता है और पुनरावृत्तियों के परिणामस्वरूप संग्रह को एकल आकार में प्रसारित किया जाता है।

आउटपुट तर्क (यदि कोई है) तो संसाधित किए जाते हैं और किसी भी लापता रिटर्न सरणियों का निर्माण किया जाता है। यदि कोई प्रदान आउटपुट सरणी में सही प्रकार नहीं है (या गलत संरेखित है) और बफर आकार से छोटा है, तो एक नया आउटपुट सरणी विशेष WRITEBACKIFCOPY ध्वज सेट के साथ बनाया गया है। फ़ंक्शन के अंत में, PyArray_ResolveWritebackIfCopy कहा जाता है ताकि इसकी सामग्री वापस आउटपुट सरणी में कॉपी हो जाए। आउटपुट तर्कों के लिए Iterators तब संसाधित किए जाते हैं।

अंत में, यह सुनिश्चित करने के बारे में निर्णय लिया जाता है कि लूपिंग तंत्र को कैसे निष्पादित किया जाए ताकि यह सुनिश्चित हो सके कि इनपुट सरणियों के सभी तत्वों को सही प्रकार के आउटपुट सरणियों का उत्पादन करने के लिए संयोजित किया गया है। लूप निष्पादन के लिए विकल्प एक-लूप (सन्निहित, संरेखित और सही डेटा प्रकार के लिए), तार-लूप (गैर-संक्रामक लेकिन अभी भी संरेखित और सही डेटा प्रकार के लिए), और एक बफर लूप (गलत-संरेखित या गलत डेटा के लिए) हैं प्रकार)। जिसके आधार पर निष्पादन विधि के लिए कहा जाता है, लूप तब सेटअप और गणना की जाती है।

फंक्शन कॉल

यह खंड बताता है कि तीन अलग-अलग प्रकार के निष्पादन के लिए मूल सार्वभौमिक फ़ंक्शन गणना लूप कैसे सेटअप और निष्पादित किया जाता है। यदि NPY_ALLOW_THREADS को संकलन के दौरान परिभाषित किया जाता है, तो जब तक कोई ऑब्जेक्ट सरणियाँ शामिल नहीं होती हैं, तो लूप्स को कॉल करने से पहले पायथन ग्लोबल इंटरप्रेटर लॉक (GIL) जारी किया जाता है। त्रुटि शर्तों को संभालने के लिए आवश्यक होने पर इसे फिर से अधिग्रहीत किया जाता है। 1-डी लूप पूरा होने के बाद ही हार्डवेयर त्रुटि झंडे की जाँच की जाती है।

वन लूप

यह सब से सरल मामला है। Ufunc को अंतर्निहित 1-D लूप को एक बार कॉल करके निष्पादित किया जाता है। यह तभी संभव है जब हमने इनपुट और आउटपुट दोनों के लिए सही प्रकार (बाइट-ऑर्डर सहित) का डेटा संरेखित किया हो और सभी सरणियों में एक समान स्ट्रैड (या तो सन्निहित, 0-डी या 1-डी) हो। इस मामले में, 1-डी कम्प्यूटेशनल लूप को पूरे सरणी के लिए गणना की गणना करने के लिए एक बार कहा जाता है। ध्यान दें कि हार्डवेयर त्रुटि झंडे की गणना पूरी गणना के बाद ही की जाती है।

कटा हुआ लूप

जब इनपुट और आउटपुट सरणियों को गठबंधन किया जाता है और सही प्रकार का होता है, लेकिन स्ट्राइडिंग एक समान (गैर-सन्निहित और 2-डी या बड़ा) नहीं है, तो गणना के लिए एक दूसरा लूपिंग संरचना कार्यरत है। यह दृष्टिकोण इनपुट और आउटपुट तर्कों के लिए सभी पुनरावृत्तियों को सभी सबसे बड़े आयामों पर पुनरावृत्त करने के लिए परिवर्तित करता है। आंतरिक लूप तब अंतर्निहित 1-डी कम्प्यूटेशनल लूप द्वारा नियंत्रित किया जाता है। बाहरी लूप परिवर्तित iterators पर एक मानक पुनरावृत्ति लूप है। प्रत्येक 1-डी लूप के पूरा होने के बाद हार्डवेयर त्रुटि झंडे की जाँच की जाती है।

बफर लूप

यह वह कोड होता है जो उस स्थिति को संभालता है जब इनपुट या / या आउटपुट सरणियों को या तो गलत समझा जाता है या अंतर्निहित 1-डी लूप की अपेक्षा से गलत डेटा-प्रकार (बाइट-स्वैप किए जाने सहित)। सरणियों को भी गैर-सन्निहित माना जाता है। कोड बहुत काम करता है जैसे कि इनर 1-डी लूप को छोड़कर स्ट्रेंड-लूप को संशोधित किया जाता है ताकि इनपुट्स पर प्री-प्रोसेसिंग की जाए और पोस्टफॉर्म्स को बफ़साइज़ चंक्स में आउटपुट पर किया जाए (जहाँ bufsize एक यूजर सेटल होने योग्य है पैरामीटर)। अंतर्निहित 1-डी कम्प्यूटेशनल लूप उस डेटा पर कहा जाता है जिसे कॉपी किया जाता है (यदि उसे होने की आवश्यकता है)। सेटअप कोड और लूप कोड इस मामले में काफी अधिक जटिल है क्योंकि इसे संभालना है:

  • अस्थायी बफ़र्स की स्मृति आवंटन
  • इनपुट और आउटपुट डेटा (गलत संरेखित और / या गलत डेटा-प्रकार) पर बफ़र्स का उपयोग करना है या नहीं, यह तय करना
  • कॉपी करना और संभवतः किसी इनपुट या आउटपुट के लिए डेटा डालना जिसके लिए बफ़र्स आवश्यक हैं।
  • विशेष-आवरण वस्तु सारणी ताकि प्रतियां और / या कास्ट आवश्यक होने पर संदर्भ मायने रखता है।
  • आंतरिक 1-डी लूप को बफ़साइज़ चंक्स में तोड़ना (संभव शेष के साथ)।

फिर से, हार्डवेयर त्रुटि झंडे प्रत्येक 1-डी लूप के अंत में जांचे जाते हैं।

अंतिम उत्पादन में हेरफेर

Ufuncs अन्य सरणी जैसी कक्षाओं को इंटरफ़ेस के माध्यम से मूल रूप से पारित करने की अनुमति देता है जिसमें किसी विशेष वर्ग के इनपुट आउटपुट को उसी वर्ग के लिए प्रेरित करेंगे। वह तंत्र जिसके द्वारा यह कार्य किया जाता है, निम्नलिखित है। यदि कोई भी इनपुट ndarrays नहीं है और __array_wrap__ विधि को परिभाषित करता है, तो सबसे बड़ा __array_priority__ विशेषता वाला वर्ग सभी आउटपुट के प्रकार (किसी भी आउटपुट __array_priority__ के अपवाद के साथ) को निर्धारित करता है। इनपुट सरणी के __array_wrap__ विधि को इनपुट के रूप में ufunc से लौटाए जा रहे ndarray के साथ बुलाया जाएगा। समर्थित __array_wrap__ फ़ंक्शन की दो कॉलिंग शैलियाँ हैं। पहला ndarray को पहले तर्क के रूप में लेता है और दूसरे तर्क के रूप में "संदर्भ" का एक टपल। संदर्भ (ufunc, तर्क, आउटपुट तर्क संख्या) है। यह पहली कॉल की कोशिश की गई है। यदि कोई TypeError होता है, तो फ़ंक्शन को केवल ndarray के साथ पहले तर्क के रूप में कहा जाता है।

तरीके

Ufuncs की तीन विधियाँ हैं जिन्हें सामान्य-उद्देश्य ufuncs के समान गणना की आवश्यकता होती है। ये कम हो जाते हैं, संचित हो जाते हैं, और फिर से लाल हो जाते हैं। इनमें से प्रत्येक विधि के लिए एक लूप के बाद सेटअप कमांड की आवश्यकता होती है। बिना तत्वों, एक-तत्व, तार-पाश और बफ़र-लूप के अनुरूप विधियों के लिए चार लूप शैलियाँ संभव हैं। ये समान मूल लूप शैलियाँ हैं जैसा कि सामान्य उद्देश्य फ़ंक्शन कॉल के लिए कार्यान्वित किया जाता है, बिना तत्व और एक-तत्व के मामलों के अलावा जो इनपुट-सरणी ऑब्जेक्ट के क्रमशः 0 और 1 तत्व होने पर विशेष-केस होते हैं।

सेट अप

सभी तीन तरीकों के लिए सेटअप फ़ंक्शन build_reduce है। यह फ़ंक्शन एक लूपिंग लूप ऑब्जेक्ट बनाता है और इसे लूप को पूरा करने के लिए आवश्यक मापदंडों से भरता है। सभी विधियाँ केवल ufuncs पर काम करती हैं जो 2-इनपुट लेती हैं और 1 आउटपुट लौटाती हैं। इसलिए, अंतर्निहित 1-डी लूप को [ otype , otype , otype ] के एक हस्ताक्षर को चुना जाता है, जहां otype अनुरोधित कटौती डेटा-प्रकार है। बफर साइज़ और एरर हैंडलिंग को तब (प्रति-थ्रेड) वैश्विक संग्रहण से पुनर्प्राप्त किया जाता है। छोटे सरणियों के लिए जो गलत संरेखित हैं या गलत डेटा-प्रकार हैं, एक प्रतिलिपि बनाई जाती है ताकि कोड के अन-बफर अनुभाग का उपयोग किया जाए। फिर, लूपिंग रणनीति का चयन किया जाता है। यदि सरणी में 1 तत्व या 0 तत्व है, तो एक सरल लूपिंग विधि का चयन किया जाता है। यदि सरणी गलत-संरेखित नहीं है और सही डेटा-प्रकार है, तो स्ट्रिंग लूपिंग का चयन किया जाता है। अन्यथा, बफरिंग लूपिंग का प्रदर्शन किया जाना चाहिए। लूपिंग पैरामीटर तब स्थापित किए जाते हैं, और रिटर्न ऐरे का निर्माण किया जाता है। आउटपुट ऐरे अलग आकार का होता है, जो इस बात पर निर्भर करता है कि क्या विधि कम हो गई है, संचित हो गई है या फिर पुन: तैयार हो गई है। यदि कोई आउटपुट एरे पहले से ही उपलब्ध है, तो यह आकार की जाँच की जाती है। यदि आउटपुट सरणी C- सन्निहित, संरेखित और सही डेटा प्रकार नहीं है, तो WRITEBACKIFCOPY ध्वज सेट के साथ एक अस्थायी प्रतिलिपि बनाई जाती है। इस तरह, तरीके एक अच्छी तरह से व्यवहार किए गए आउटपुट सरणी के साथ काम करने में सक्षम होंगे, लेकिन परिणाम को सही आउटपुट सरणी में वापस कॉपी किया जाएगा जब PyArray_ResolveWritebackIfCopy को फ़ंक्शन पूरा होने पर बुलाया जाता है। अंत में, पुनरावृत्तियों को सही अक्ष पर लूप पर सेट किया जाता है (विधि को प्रदान की गई धुरी के मूल्य के आधार पर) और सेटअप रूटीन वास्तविक गणना दिनचर्या में लौटता है।

कम करें

Ufunc के सभी तरीके इनपुट और आउटपुट तर्क के साथ एक ही अंतर्निहित 1-डी कम्प्यूटेशनल लूप का उपयोग करते हैं ताकि उचित कमी आए। उदाहरण के लिए, कम करने के कामकाज की कुंजी यह है कि 1-डी लूप को आउटपुट के साथ कहा जाता है और दूसरा इनपुट मेमोरी में समान स्थिति की ओर इशारा करता है और दोनों का चरण-आकार 0. है। पहला इनपुट इंगित कर रहा है चयनित अक्ष के लिए उपयुक्त स्ट्राइड द्वारा दिए गए चरण-आकार के साथ इनपुट सरणी। इस तरह, ऑपरेशन किया जाता है

\ start {align *} o & = & i [0] \\ o & = & i [k] \ textrm {<op>} o \ quad k = 1 \ ldots N \ end {align *}

कहा पे एन 1 इनपुट में तत्वों की संख्या है, मैं , ओ आउटपुट है, और मैं [k] है कश्मीर ^ {\ textrm {वें}} का तत्व मैं चयनित अक्ष के साथ। यह बुनियादी संचालन 1 से अधिक आयाम वाले सरणियों के लिए दोहराया जाता है ताकि चयनित अक्ष के साथ प्रत्येक 1-डी उप-सरणी के लिए कमी हो। चयनित आयाम के साथ एक पुनरावृत्त इस लूपिंग को संभालता है।

बफ़र्ड लूप के लिए, लूप फ़ंक्शन को कॉल करने से पहले डेटा को कॉपी और कास्ट करने के लिए ध्यान रखा जाना चाहिए क्योंकि अंतर्निहित लूप सही डेटा-प्रकार (बाइट-ऑर्डर सहित) के संरेखित डेटा की अपेक्षा करता है। बफ़र किए गए लूप को इस प्रतिलिपि को संभालना होगा और कास्ट से पहले लूप फ़ंक्शन को कॉल करने से पहले कास्टिंग करना होगा जो कि उपयोगकर्ता द्वारा निर्दिष्ट bufsize से अधिक नहीं है।

संचय करें

संचित फ़ंक्शन आउटपुट में उस कम फ़ंक्शन के समान है और दूसरा इनपुट आउटपुट को इंगित करता है। अंतर यह है कि दूसरा इनपुट वर्तमान आउटपुट पॉइंटर के पीछे एक मेमोरी को इंगित करता है। इस प्रकार, किया गया ऑपरेशन है

\ start {align *} o [0] & = i [0] \\ o [k] & = और i [k] \ textrm {<op>} o [k-1] \ quad k = 1 \ ldots एन \ अंत {संरेखित *}

आउटपुट में इनपुट के समान आकार है और प्रत्येक 1-डी लूप पर काम करता है एन तत्वों जब चयनित अक्ष में आकार है एन 1 । फिर से, बफ़र्ड लूप्स अंतर्निहित 1-डी कम्प्यूटेशनल लूप को कॉल करने से पहले डेटा को कॉपी और कास्ट करने का ध्यान रखते हैं।

Reduceat

Reduceat फ़ंक्शन कम और संचय कार्यों दोनों का एक सामान्यीकरण है। यह सूचकांक द्वारा निर्दिष्ट इनपुट सरणी की सीमाओं को कम करता है। अतिरिक्त सूचक तर्क यह सुनिश्चित करने के लिए जांचा जाता है कि लूप गणना होने से पहले प्रत्येक इनपुट चयनित सरणी के साथ इनपुट सरणी के लिए बहुत बड़ा नहीं है। लूप कार्यान्वयन को कोड का उपयोग करके नियंत्रित किया जाता है जो कि कम किए गए कोड के समान होता है क्योंकि कई बार सूचकांक इनपुट में तत्व होते हैं। विशेष रूप से: पहला इनपुट पॉइंटर इंडेक्स सरणी द्वारा इंगित सही स्थान पर इनपुट सरणी में अंतर्निहित 1-डी कम्प्यूटेशनल लूप बिंदुओं को पारित करता है। इसके अलावा, आउटपुट पॉइंटर और दूसरा इनपुट पॉइंटर अंतर्निहित 1-डी लूप पॉइंट को मेमोरी में उसी स्थिति में ले जाता है। 1-डी कम्प्यूटेशनल लूप का आकार वर्तमान सूचकांक और अगले सूचकांक के बीच का अंतर निर्धारित किया जाता है (जब वर्तमान सूचकांक अंतिम सूचकांक है, तो अगले सूचकांक को चयनित आयाम के साथ सरणी की लंबाई माना जाता है )। इस तरह, 1-डी लूप निर्दिष्ट सूचकांकों में कमी को लागू करेगा।

मिस-संरेखित या एक लूप डेटा-प्रकार जो इनपुट और / या आउटपुट डेटा-प्रकार से मेल नहीं खाता है, बफ़र कोड का उपयोग करके नियंत्रित किया जाता है जहां-जहां डेटा को एक अस्थायी बफर में कॉपी किया जाता है और कॉल करने से पहले आवश्यक होने पर सही डेटा-टाइप कास्ट किया जाता है अंतर्निहित 1-डी फ़ंक्शन। अस्थायी बफ़र्स (तत्व) आकारों में उपयोगकर्ता सेलेबल बफर-साइज़ मान से बड़ा नहीं होता है। इस प्रकार, लूप को लचीला होना चाहिए ताकि अंतर्निहित 1-डी कम्प्यूटेशनल लूप को कॉल किया जा सके, जो कि कुल गणना को बफर-आकार से बड़ा नहीं होने के लिए कुल बार पूरा करेगा।

Original text