2025, Nov 01 18:31
SymPy में _eval_Mod से मॉड्यूलो-अनुकूल कस्टम फ़ंक्शन बनाएं
जानें SymPy में मॉड्यूलो-अनुकूल कस्टम फ़ंक्शन बनाने की विधि: evaluation hooks व _eval_Mod कैसे काम करते हैं, और Mod प्रोटोकॉल से जोड़ में सही शेषफल कैसे मिलता है।
प्रतीकात्मक सिस्टम में कस्टम evaluation hooks वही फर्क पैदा करते हैं, जो केवल सुंदर दिखने वाली अभिव्यक्तियों और सच में गणना करने वाली अभिव्यक्तियों के बीच होता है। अगर आप अपना SymPy फ़ंक्शन परिभाषित कर रहे हैं और चाहते हैं कि वह बड़े व्यंजकों में मॉड्यूलो (modular) अंकगणित में भाग ले, तो साधारण ऑपरेटर ओवरलोडिंग पर्याप्त नहीं होगी। सही तरीका है SymPy के Mod के evaluation प्रोटोकॉल से जुड़ना।
समस्या की रूपरेखा
उद्देश्य है ऐसा प्रतीकात्मक फ़ंक्शन बनाना जिसे सीधे evaluate नहीं किया जा सके, लेकिन जो फिर भी कुछ गुण—जैसे मॉड्यूलो शेषफल—की गणना कर सके। सीधी ऑपरेटर ओवरलोडिंग सबसे सरल स्थिति में काम करती दिखती है, पर जैसे ही फ़ंक्शन किसी बड़े व्यंजक के भीतर आता है, मूल्यांकन रुक जाता है।
from sympy import Function
class Buzz(Function):
@classmethod
def eval(cls, k):
pass
def __mod__(self, m: int) -> int:
val, = self.args
return (val + 1) % m
print(Buzz(7) % 3)
# 2
print((Buzz(7) + 1) % 3)
# Mod(Buzz(7) + 1, 3)
print((Buzz(7) + 1) % 3 == 0)
# False
एकल उदाहरण पर सीधे % लगाना काम करता है, लेकिन उसे जोड़ (sum) के भीतर लपेटते ही मूल्यांकन अटक जाता है। अपेक्षा यह होती है कि जोड़ का शेषफल उसके घटकों के शेषफलों से निकाला जा सके, पर अभिव्यक्ति प्रतीकात्मक ही रह जाती है।
यह क्यों होता है
SymPy में अभिव्यक्तियों पर % ऑपरेटर आपके ऑब्जेक्ट का __mod__ वैसे नहीं बुलाता जैसा आप सोच सकते हैं। इसके बजाय, Expr.__mod__ एक प्रतीकात्मक Mod(self, other) बनाता है। इसके बाद SymPy इस Mod ऑब्जेक्ट से पूछता है कि उसे कैसे evaluate करना है, और Mod बदले में अभिव्यक्ति के हिस्सों पर _eval_Mod नाम की विधि खोजता है। अगर आपके कस्टम फ़ंक्शन में _eval_Mod लागू नहीं है, तो संयुक्त (composite) संदर्भों में अभिव्यक्ति आम तौर पर unevaluated ही रहती है।
सही तरीका: _eval_Mod लागू करें
किसी बड़े व्यंजक के भीतर मॉड्यूलो गणना चलाने के लिए अपने कस्टम प्रतीकात्मक फ़ंक्शन में _eval_Mod विधि जोड़ें। इस उपयोग के लिए आपको __mod__ ओवरलोड की आवश्यकता नहीं है, क्योंकि अभिव्यक्ति-स्तर का प्रोटोकॉल पहले से ही Mod के माध्यम से शेषफल की गणना कराता है, जो _eval_Mod को क्वेरी करता है।
from sympy import Function, Expr, Integer
class Zed(Function):
@classmethod
def eval(cls, k):
pass
def _eval_Mod(self, modv: Expr) -> Integer:
if not isinstance(modv, Integer):
raise TypeError
arg0, = self.args
return (arg0 + 1) % modv
print(Zed(7) % 3)
# 2
print((Zed(7) + 1) % 3)
# 0
इससे आपका फ़ंक्शन अभिव्यक्ति-स्तर की मॉड्यूलो गणनाओं में भाग लेने लगता है। एकल उदाहरण वाला केस पहले की तरह काम करता है और, सबसे अहम, अब जहाँ यह फ़ंक्शन जोड़ में आता है, वहाँ % के तहत अपेक्षित रूप से सरलीकरण हो जाता है।
दायरा और देखे गए व्यवहार
ऊपर का सुधार (Zed(7) + 1) % 3 जैसे मामलों पर लागू होता है। देखा गया है कि Zed(7) * 2 % 3 जैसा रूप Mod(2*Zed(7), 3) के रूप में बना रह सकता है। यानी जोड़-संबंधी संदर्भ गणनायोग्य हो जाते हैं, जबकि अन्य संरचनाएँ प्रतीकात्मक बनी रह सकती हैं।
SymPy के भीतर संबंधित उपकरण
SymPy की polynomial रूटीन सीधे modular arithmetic को सपोर्ट करती हैं। कुछ फ़ंक्शनों में modulus पैरामीटर होता है, जो finite rings पर बहुपदों को factor या simplify करते समय उपयोगी रहता है।
from sympy import symbols, factor
x = symbols('x')
print(factor(x**2 + 1))
# x**2 + 1
print(factor(x**2 + 1, modulus=2))
# (x + 1)**2
यह क्यों मायने रखता है
कई वर्कफ़्लो में वास्तविक पूर्णांक इतने बड़े होते हैं कि उन्हें प्रत्यक्ष रूप से बनाना संभव नहीं होता—वे केवल प्रतीकात्मक रूप में मौजूद रहते हैं। फिर भी आपको कुछ गुण, जैसे मॉड्यूलो शेषफल, निकालने की ज़रूरत पड़ सकती है। SymPy के evaluation hooks से एकीकरण करके आप अपने कस्टम फ़ंक्शन को संयोजित अभिव्यक्तियों में चलने योग्य बनाते हैं, बिना पूरे ऑब्जेक्ट का eager evaluation कराए।
निष्कर्ष
कस्टम SymPy फ़ंक्शनों को अभिव्यक्तियों में मॉड्यूलो अंकगणित के साथ सहयोगी बनाने के लिए operator overloading पर नहीं, बल्कि SymPy के expression प्रोटोकॉल पर भरोसा करें। अपनी फ़ंक्शन में _eval_Mod लागू करें ताकि Mod उसे बड़े सूत्रों के भीतर evaluate कर सके। बहुपदों के लिए याद रखें कि कई रूटीन modulus तर्क स्वीकार करती हैं। यह तरीका आपके ऑब्जेक्ट्स को प्रतीकात्मक बनाए रखता है, जबकि लक्षित गणना संभव करता है—ठीक वही जो चाहिए जब अंतर्निहित पूर्णांक संग्रहित करने के लिए बहुत बड़े हों।
यह लेख StackOverflow के प्रश्न (लेखक: sligocki) और Oscar Benjamin के उत्तर पर आधारित है।