2025, Sep 26 23:37
SymPy में द्वितीय-क्रम Jacobian मैट्रिक्स: is_constant के भ्रम और शून्य अवकलज का सही समेटन
SymPy में द्वितीय-क्रम Jacobian बनाना सीखें: is_constant से सब constant क्यों दिखता है, शून्य अवकलज पहचानें और उन्हें सही तरीके से समेटें, कोड उदाहरण सहित
SymPy में द्वितीय-क्रम आंशिक अवकलजों का मैट्रिक्स बनाना दिखने में आसान है—लेकिन परिणाम में हर चीज़ अचानक “constant” बताई जाने लगे तो बात उलझ जाती है। अगर आपने is_constant() बुलाकर तुच्छ रूप से शून्य पद हटाने की कोशिश की और हर जगह True ही दिखा, तो आप SymPy द्वारा स्वतंत्र प्रतीकों के अवकलजों को मॉडल करने के तरीके से टकरा गए हैं। आइए समझें कि ऐसा क्यों होता है और “2nd order Jacobian” को साफ़-सुथरे शून्य-समेटन के साथ उपयोगी रूप में कैसे तैयार करें।
समस्या का सेटअप
उद्देश्य है: प्रतीकों के एक सेट के लिए प्रथम-क्रम आंशिक अवकलजों का मैट्रिक्स बनाना, और फिर प्रत्येक प्रतीक के सापेक्ष उनके अवकलजों को दोबारा जोड़कर एक द्वितीय-क्रम संरचना तैयार करना। नीचे वही प्रोग्राम-तर्क रखते हुए केवल-ASCII नामों के साथ एक न्यूनतम संस्करण है।
from sympy import symbols, Matrix, zeros, Derivative
sym_names = ["Delta", "Gamma", "v"]
def make_vars():
    return symbols(" ".join(sym_names))
def first_order_partials():
    grid = []
    vars_seq = make_vars()
    for i in range(len(sym_names)):
        left = vars_seq[i]
        row = []
        for j in range(len(sym_names)):
            right = vars_seq[j]
            if left == right:
                row.append(1)
            else:
                row.append(Derivative(left, right))
        grid.append(row)
    return vars_seq, Matrix(grid)
def second_order_partials():
    vars_seq, jmat = first_order_partials()
    out = zeros(len(vars_seq)**2, len(vars_seq))
    nrows, ncols = jmat.shape
    block_idx = 0
    for k in range(len(vars_seq)):
        denom = vars_seq[k]
        for r in range(nrows):
            for c in range(ncols):
                num = jmat[r, c]
                d2 = Derivative(num, denom)
                out[block_idx * len(vars_seq) + r, c] = d2
        block_idx += 1
    return out
इसे बनाने के बाद, जब आप तत्व-स्तर पर is_constant() से फ़िल्टर करने की कोशिश करते हैं, तो सब कुछ constant दिखाई देता है।
सब कुछ constant क्यों लगता है
मुख्य बात यह है कि एक स्वतंत्र प्रतीक का दूसरे के सापेक्ष आंशिक अवकलज शून्य होता है। SymPy भी ठीक इसी बात को एन्कोड करता है:
from sympy import symbols, Derivative, diff
x, y = symbols('x y')
d = Derivative(x, y)
# अमूल्यांकित अवकलज दिखाता है
print(d)
# SymPy जानता है कि यह constant है (यह 0 के बराबर है)
print(d.is_constant())
# अवकलज का मूल्यांकन 0 देता है
print(d.doit())
print(diff(x, y))
इसलिए Derivative(x, y).is_constant() True लौटाता है, क्योंकि अवकलज 0 है। यही तर्क Derivative(1, x) पर भी लागू होता है, जो 0 पर सिमट जाता है। अगर आपके प्रथम-क्रम मैट्रिक्स में स्थिर राशियाँ हैं या स्वतंत्र प्रतीकों के अवकलज हैं, तो उनके प्रत्येक द्वितीय अवकलज भी constant ही होंगे—इसी वजह से हर जगह True दिखता है।
गैर-स्थिर अवकलज कैसे पाएँ
यदि आप शून्य-न-होने वाले अवकलज की उम्मीद करते हैं, तो जिस अभिव्यक्ति का अवकलन हो रहा है उसे वास्तव में उस चर पर निर्भर होना चाहिए। SymPy में इसका अर्थ है फ़ंक्शन का उपयोग करना, न कि साधारण प्रतीक:
from sympy import symbols, Function, Derivative
x = symbols('x')
f = Function('f')
u = Derivative(f(x), x)
print(u)          # अमूल्यांकित ही रहता है
print(u.doit())   # अभी भी f(x) का प्रतीकात्मक अवकलज
print(u.is_constant())  # False
यह x के साथ सचमुच बदलने वाली अभिव्यक्ति को दर्शाने का एकमात्र तरीका है—स्वतंत्र, अकेले खड़े प्रतीक के विपरीत। प्रतीकों के समूह को एक-दूसरे के फ़ंक्शन बना देना संभव नहीं है।
तुच्छ शून्य पदों को सही ढंग से समेटना
यदि आपका उद्देश्य अवकलजों को अमूल्यांकित रखना है, पर जो निश्चित रूप से शून्य हैं उन्हें हटाना है, तो Derivative ऑब्जेक्ट पर is_constant() नहीं लगाना चाहिए। इसके बजाय, देखें कि जिस अभिव्यक्ति का अवकलन हो रहा है वह स्थिर है या नहीं, और उसी हिसाब से काम करें। यह पैटर्न Derivative(1, x) जैसी चीज़ों को हटा देता है, जबकि यदि आप उन्हें प्रतीकात्मक ही रखना चाहते हैं तो Derivative(x, y) को अमूल्यांकित रूप में रहने देता है। जब आप सचमुच शून्य पर समेटना चाहें, तो जहाँ अवकलज का .expr स्थिर हो, वहाँ 0 रख दें।
from sympy import symbols, Matrix, zeros, Derivative
sym_names = ["Delta", "Gamma", "v"]
def make_vars():
    return symbols(" ".join(sym_names))
def first_order_partials():
    grid = []
    vars_seq = make_vars()
    for i in range(len(sym_names)):
        left = vars_seq[i]
        row = []
        for j in range(len(sym_names)):
            right = vars_seq[j]
            if left == right:
                row.append(1)
            else:
                row.append(Derivative(left, right))
        grid.append(row)
    return vars_seq, Matrix(grid)
def second_order_partials_collapsed():
    vars_seq, jmat = first_order_partials()
    out = zeros(len(vars_seq)**2, len(vars_seq))
    nrows, ncols = jmat.shape
    block_idx = 0
    for k in range(len(vars_seq)):
        denom = vars_seq[k]
        for r in range(nrows):
            for c in range(ncols):
                d2 = Derivative(jmat[r, c], denom)
                # तुच्छ शून्य अवकलज हटाएँ (अभिव्यक्ति स्थिर है)
                out_idx = block_idx * len(vars_seq) + r
                out[out_idx, c] = 0 if d2.expr.is_constant() else d2
        block_idx += 1
    return out
इस तरीके से Derivative(1, Delta) जैसी कोई भी प्रविष्टि तुरंत 0 बन जाएगी। जिन प्रविष्टियों की अवकलनीय अभिव्यक्ति स्थिर नहीं है, वे प्रतीकात्मक अवकलज के रूप में बनी रहती हैं।
यह क्यों मायने रखता है
SymPy “constant” किसे मानता है, यह समझना भ्रामक संकेतों से बचाता है। Derivative पर is_constant() यह बताता है कि अवकलज का मान चरों से स्वतंत्र है या नहीं—यह नहीं कि अंदर की अभिव्यक्ति परिवर्तनशील है या नहीं। इसी कारण Derivative(x, y).is_constant() True होता है: अवकलज शून्य है। यदि आपको मूल्यांकन से पहले तय करना है कि कोई अवकलज तुच्छ रूप से शून्य है या नहीं, तो di.expr.is_constant() के जरिए उसी अभिव्यक्ति की जाँच करें जिसका अवकलन हो रहा है।
मुख्य निष्कर्ष
SymPy में उच्च-क्रम अवकलज मैट्रिस जोड़ते समय स्वतंत्र प्रतीकों और प्रतीकों के फ़ंक्शनों के बीच का फर्क स्पष्ट रखें। यदि परिवर्तनशीलता अपेक्षित है, तो Function के साथ उसे स्पष्ट रूप से व्यक्त करें। अगर लक्ष्य प्रतीकात्मक संरचना को बनाए रखते हुए सुनिश्चित शून्यों को हटाना है, तो derivative ऑब्जेक्ट के बजाय उसके .expr की जाँच करें और ऐसी प्रविष्टियों को 0 से बदल दें। यह छोटा-सा बदलाव कोड को SymPy की अर्थ-विधि के अनुरूप लाता है और एक ज्यादा साफ़, अधिक सूचनात्मक द्वितीय-क्रम मैट्रिक्स देता है।
यह लेख StackOverflow पर प्रश्न (लेखक: James Strieter) और Oscar Benjamin के उत्तर पर आधारित है।