2025, Oct 19 18:31
SymPy solve में चर-क्रम बदलने का असर: प्लेसहोल्डर और Groebner से स्थिर, संक्षिप्त समाधान
SymPy solve में चर-क्रम बदलने से समाधान का रूप कैसे बदलता है, उदाहरण से समझें। प्लेसहोल्डर और Groebner से आउटपुट को संक्षिप्त व स्थिर रखें। प्रदर्शन भी सुधरेगा।
SymPy में गैर-रेखीय तंत्र हल करते समय, solve में अज्ञातों का क्रम मात्र बदल देने से भी परिणाम का रूप बदल सकता है। हल गणितीय रूप से बराबर ही रहते हैं, लेकिन एक क्रमविन्यास पर अभिव्यक्ति बहुत फैल सकती है, जबकि दूसरे पर वही परिणाम सुसंगठित और छोटा रहता है। यह मार्गदर्शिका एक ठोस तंत्र पर यह व्यवहार दिखाती है, बताती है कि व्यवहार में यह अंतर क्यों दिखता है, और अस्थायी प्रतीक-समूहीकरण तथा Groebner-आधारित संरचना से आउटपुट को स्थिर रखने का भरोसेमंद तरीका प्रदर्शित करती है।
पुनरुत्पादन उदाहरण: वही तंत्र, चर का क्रम अलग
नीचे दिए तंत्र में तीन अज्ञात और कई पैरामीटर हैं। solve में अज्ञातों का क्रम अदल-बदल करने पर पहली और दूसरी समाधान शाखा मेल खाती है, लेकिन तीसरी शाखा में एक चर के लिए दिखाई देने वाली जटिलता क्रम पर निर्भर करती है।
from sympy.solvers import solve as ssolve
import sympy as sp
rho = sp.Symbol('rho')
pf = sp.Symbol('pf')
pb = sp.Symbol('pb')
nb = sp.Symbol('nb')
nt = sp.Symbol('nt')
g = sp.Symbol('g')
pt = sp.Symbol('pt')
delta = sp.Symbol('delta')
kappa = sp.Symbol('kappa')
nt = 1
eq_t = rho*(1 - 2**-nt)*pt + pf*pt*g - pt*(delta + kappa*nt)
eq_b = rho*(1 - 2**-nb)*pb + pf*pb*g - pb*(delta + kappa*nb)
eq_f = rho*pf + pb*rho*2**-nb - g*(pt + pb)*pf - delta*pf
eq_t = sp.simplify(eq_t)
sol = ssolve([eq_b, eq_t, eq_f], [pt, pb, pf], rational=True)
sol2 = ssolve([eq_f, eq_b, eq_t], [pf, pb, pt], rational=True)
दोनों कॉल समान शुरुआती दो समाधान शाखाएँ लौटाती हैं: (pf, pb, pt) = (0, 0, 0) और ((-(-2*kappa - 2*delta + rho))/(2*g), 0, (-delta + rho)/g)। तीसरी शाखा में pt और pf तो समान रहते हैं, पर pb का रूप क्रम पर निर्भर करते हुए अलग दिखता है। एक क्रम में pb बहुत बड़ी अभिव्यक्ति में फैल जाता है, जबकि दूसरे में वही चीज़ संक्षिप्त रूप में आती है।
संक्षिप्त शाखा में pt = 0 और pf = (2**nb*(kappa*nb + delta - rho) + rho)/(2**nb*g) होता है, और pb का मान है (-2**nb*kappa*delta*nb + 2**nb*kappa*nb*rho - 2**nb*delta**2 - 2**nb*rho**2 + 2**(nb + 1)*delta*rho - delta*rho + rho**2)/(2**nb*g*(kappa*nb + delta - rho)).
असल में हो क्या रहा है
यह अंतर गणितीय भिन्नता नहीं, बल्कि सरलीकरण का मुद्दा है। pb के दोनों रूप समतुल्य हैं; सीधे उनके अंतर को सरल करने पर शून्य मिलता है। SymPy में यह जाना-पहचाना व्यवहार है। lambdify के जरिए कुछ नमूना पैरामीटर मानों पर संख्यात्मक मानांकन करने से भी दोनों अभिव्यक्तियाँ मिलती दिखती हैं, यद्यपि केवल संख्यात्मक जाँचें प्रमाण नहीं होतीं।
अज्ञातों का क्रम बदलने से अंदरूनी elimination और simplification के रास्ते बदलते हैं, जिससे परिणाम समान होते हुए भी आकार और रूप में बड़ा अंतर दिख सकता है। इस तंत्र में वास्तव में तीन समाधान शाखाएँ हैं; बस उनका प्रस्तुतीकरण क्रम के साथ बदल जाता है।
आउटपुट को स्थिर रखने का व्यावहारिक तरीका
एक कारगर रणनीति यह है कि हल निकालने से पहले पैरामीटर-प्रधान उप-अभिव्यक्तियों को अस्थायी प्रतीकों में समूहित कर दें। आवर्ती खंडों को छोटे प्लेसहोल्डरों से बदलकर आप अभिव्यक्ति के फूलने को घटाते हैं और solve को चर-क्रम के प्रति कम संवेदनशील बनाते हैं। हल मिलने के बाद प्लेसहोल्डर वापस मूल पैरामीटरों से प्रतिस्थापित कर दें।
from sympy import var, nsimplify
from sympy.solvers import solve as ssolve
from itertools import permutations
eqs = [nsimplify(e, rational=True) for e in [eq_b, eq_t, eq_f]]
K0, K1, K10, K11, K12, K13, K2, K4, K5, K6, K7 = var('K0 K1 K10 K11 K12 K13 K2 K4 K5 K6 K7')
# संक्षिप्त समीकरण
eqc = [
    K2*(K6*pb + K7*pb + pb*pf),
    K2*pt*(K4 + pf),
    K13*(K10*pf + K11*pf + K12*pf*(pb + pt) + pb)
]
# प्लेसहोल्डर्स की बैक-सब्स्टिट्यूशन मैप
M = [
    (K13, K5),
    (K12, -2**nb*g/rho),
    (K11, -2**nb*delta/rho),
    (K10, 2**nb),
    (K7, K1*rho/g),
    (K6, -K0/g),
    (K5, rho/2**nb),
    (K4, -kappa/g - delta/g + rho/g/2),
    (K2, g),
    (K1, 1 - 1/2**nb),
    (K0, kappa*nb + delta)
]
# सभी क्रमचयों पर हल निकालना सुसंगत हो जाता है
vars3 = (pb, pf, pt)
all_solutions = [ssolve(es, vs) for es in permutations(eqc) for vs in permutations(vars3)]
unique_families = {frozenset(branch) for branches in all_solutions for branch in branches}
# एक प्रतिनिधि समाधान और मूल पैरामीटरों में पुन: प्रतिस्थापन
sample = all_solutions[0]
sol_expanded = [[comp.simplify() for comp in sp.Tuple(*s).subs(M)] for s in sample]
प्रतिस्थापन के बाद तीनों शाखाएँ संक्षिप्त और क्रमविन्यास से अपरिवर्तित रूप में मिलती हैं और मूल समीकरणों को संतुष्ट करती हैं। सीधे प्रतिस्थापन और सरलीकरण करने पर सभी अवशेष शून्य आते हैं।
# जाँचें कि समाधान मूल समीकरणों को संतुष्ट करते हैं
check = {E.subs(dict(zip(vars3, S))).cancel().simplify() for E in eqs for S in sol_expanded}
प्लेसहोल्डर हटाने पर समाधान-समुच्चय साफ़ और स्थिर रूप में दिखता है: पहले [0, 0, 0], फिर [0, (kappa + delta - rho/2)/g, (-delta + rho)/g], और अंत में [-(delta - rho)*(2**nb*(kappa*nb + delta) - rho*(2**nb - 1))/(2**nb*g*(kappa*nb + delta - rho)), kappa*nb/g + delta/g - rho/g + rho/(2**nb*g), 0]।
जाँच और जटिलता आकलन के लिए Groebner
एक और दृष्टि यह है कि Groebner बेस देखें। यह तंत्र groebner के साथ संभाला जा सकता है, लेकिन चर-क्रम बदलने पर ऑपरेशन की संख्या बदलती है। संक्षिप्त किए गए समीकरणों पर गिनतियाँ [63, 59, 63, 42, 32, 32] आती हैं, जबकि कच्चे समीकरणों पर वे [946, 344, 946, 213, 156, 156] हैं। यह इस अवलोकन से मेल खाता है कि बार-बार आने वाली पैरामीटर संरचना को समूहित करने से जटिलता घटती है और आगे की बीजीय प्रक्रिया अधिक स्थिर होती है।
यह क्यों मायने रखता है
जैकोबियन और ईगेन-वैल्यू जैसी आगे की प्रक्रियाओं में संक्षिप्त और क्रम-स्थिर अभिव्यक्तियाँ केवल दिखावे की बात नहीं हैं। ये प्रतीकात्मक अवकलन को तेज और कम भंगुर बनाती हैं, सहकर्मियों के लिए पठनीयता बढ़ाती हैं, और अभिव्यक्ति के फूलने से होने वाली ग़लत व्याख्या की संभावना घटाती हैं। संक्षिप्त तरीका गणित बदले बिना छोटे रूप देता है, और simplify के जरिए समतुल्यता-जाँच शुद्धता की पुष्टि करती है।
मुख्य बातें
ध्यान रखें कि solve, समाधान समान होने पर भी, चर-क्रम के साथ अलग दिखने वाले आउटपुट दे सकता है। रूप अलग लगें तो उनके अंतर पर simplify चलाकर समतुल्यता की पुष्टि करें। हल निकालने से पहले पैरामीटर-प्रधान खंडों को प्लेसहोल्डरों में समूहित करने से संक्षिप्त, स्थिर शाखाएँ मिलती हैं जो समीकरणों और चर के क्रमविन्यास से अप्रभावित रहती हैं। जरूरत पड़े तो Groebner का उपयोग करके बीजीय जटिलता नापें और जाँचें कि तंत्र की संरचना सुसंगत रूप से संभाली जा रही है।
यह लेख StackOverflow पर पूछे गए प्रश्न (लेखक: Wilco Verweij) और smichr के उत्तर पर आधारित है।