مقایسه کتابخانههای بهینهسازی غیرخطی (NLP) در پایتون
در این راهنما یک مسأله ثابت را با سه کتابخانه مطرح پایتون حل میکنیم و نتایج را با تصویرسازی کامل مقایسه میکنیم.
📐 پایه ریاضی مسأله
هدف: کمینهسازی مجموع مربعات (فاصله اقلیدسی از مبدأ)
\[\min_{x_1, x_2, x_3} \quad f(x_1, x_2, x_3) = x_1^2 + x_2^2 + x_3^2\]تحت قیود:
| # | نوع | فرمول | توضیح |
|---|---|---|---|
| 1 | قید خطی (تساوی) | $x_1 + x_2 + x_3 = 3$ | مجموع متغیرها ثابت است |
| 2 | قید غیرخطی (تساوی) | $x_1 \times x_2 \times x_3 = 1$ | حاصلضرب متغیرها ثابت است |
جواب تحلیلی: طبق نامساوی AM-GM، جواب بهینه $x_1 = x_2 = x_3 = 1$ است که مقدار $f^* = 3$ را میدهد.
🗂️ کتابخانههای مورد بررسی
| # | کتابخانه | نصب | سالور | روش گرادیان | کاربرد اصلی |
|---|---|---|---|---|---|
| 1 | SciPy | پیشنصب | SLSQP | Finite Difference (تقریب) | علمی عمومی — سریع و ساده |
| 2 | GEKKO | pip install gekko |
IPOPT (داخلی) | Automatic Diff | کنترل بهینه و DAE |
| 3 | CasADi | pip install casadi |
IPOPT (داخلی) | Automatic Diff (دقیقترین) | رباتیک، MPC، هوافضا |
🔄 مراحل حل هر سالور
① تعریف تابع هدف f(x)
② تعریف قیود g(x) = 0
③ تعیین نقطه شروع x₀
④ فراخوانی الگوریتم بهینهسازی (SLSQP یا IPOPT)
⑤ استخراج جواب x* و تحقق قیود
⑥ مقایسه با جواب تحلیلی
⚙️ راهاندازی اولیه — NumPy و نقطه شروع
import numpy as np
# نقطه اولیه (Initial Guess)
# اکثر سالورهای NLP گرادیانمحور از یک نقطه شروع کرده و بهسمت minimum حرکت میکنند.
# نقطه شروع (x0) روی سرعت همگرایی و یافتن minimum محلی vs جهانی تأثیر دارد.
x0 = [0.5, 0.5, 0.5]
# جواب تحلیلی (Analytical Solution)
# با استفاده از نامساوی AM-GM:
# AM ≥ GM → (x1+x2+x3)/3 ≥ (x1·x2·x3)^(1/3)
# 3/3 ≥ 1^(1/3) → تساوی فقط زمانی برقرار است که x1=x2=x3
# پس جواب بهینه: x1 = x2 = x3 = 1 ، مقدار تابع: f* = 1² + 1² + 1² = 3
x_analytical = np.array([1.0, 1.0, 1.0])
print(f"📌 حدس اولیه: {x0}")
print(f"🎯 جواب تحلیلی: {x_analytical.tolist()}")
print(f"🎯 مقدار بهینه تابع هدف: f* = {sum(x_analytical**2)}")
خروجی:
📌 حدس اولیه: [0.5, 0.5, 0.5]
🎯 جواب تحلیلی: [1.0, 1.0, 1.0]
🎯 مقدار بهینه تابع هدف: f* = 3.0
1️⃣ SciPy — scipy.optimize.minimize با روش SLSQP
ویژگیها
- بدون نیاز به نصب جداگانه (بخشی از اکوسیستم SciPy)
- سالور پیشفرض
SLSQPبرای مسائل NLP با قید - قیود بهصورت دیکشنری تعریف میشوند:
{'type': 'eq'|'ineq', 'fun': ...} - مناسب برای مسائل کوچک تا متوسط
الگوریتم SLSQP چیست؟
SLSQP = Sequential Least SQuares Programming
- در هر تکرار یک زیرمسأله QP (برنامهریزی درجهدوم) حل میکند
- گرادیان را با تفاضل متناهی (Finite Difference) تقریب میزند:
- برای مسائل کوچک تا متوسط (< ~1000 متغیر) مناسب است
کد پیادهسازی
from scipy.optimize import minimize
# ── تعریف تابع هدف ────────────────────────────────────────────────────────
def obj_scipy(x):
# f(x) = x1² + x2² + x3² (مجموع مربعات = distance² از مبدأ)
return x[0]**2 + x[1]**2 + x[2]**2
# ── تعریف قیود بهصورت دیکشنری ────────────────────────────────────────────
# 'type': 'eq' → g(x) = 0 (قید تساوی)
# 'fun' → تابعی که g(x) را برمیگرداند (باید صفر شود)
cons_scipy = (
{'type': 'eq', 'fun': lambda x: x[0] + x[1] + x[2] - 3}, # g₁: x1+x2+x3-3 = 0
{'type': 'eq', 'fun': lambda x: x[0] * x[1] * x[2] - 1} # g₂: x1·x2·x3-1 = 0
)
# ── ردیابی مسیر همگرایی با Callback ──────────────────────────────────────
# callback(xk): بعد از هر تکرار فراخوانی میشود (xk = نقطه فعلی)
scipy_history = {'x': [], 'f': [], 'g1': [], 'g2': []}
def scipy_callback(xk):
scipy_history['x'].append(xk.copy())
scipy_history['f'].append(obj_scipy(xk))
scipy_history['g1'].append(abs(xk[0] + xk[1] + xk[2] - 3)) # نقض قید ۱
scipy_history['g2'].append(abs(xk[0] * xk[1] * xk[2] - 1)) # نقض قید ۲
# نقطه شروع نامتقارن (چرا نه [0.5,0.5,0.5]؟)
# SLSQP در نقاط متقارن ممکن است ماتریس ژاکوبین تکین (Singular) شود
x0_scipy = [0.8, 1.0, 1.2]
# ── فراخوانی سالور ────────────────────────────────────────────────────────
# ftol=1e-12 : همگرایی با دقت بسیار بالا (تغییر کمتر از 1e-12)
# maxiter=500: حداکثر تعداد تکرار
res_scipy = minimize(
obj_scipy,
x0_scipy,
method='SLSQP',
constraints=cons_scipy,
callback=scipy_callback,
options={'ftol': 1e-12, 'maxiter': 500}
)
# ── استخراج نتایج ─────────────────────────────────────────────────────────
sol_scipy = np.round(res_scipy.x, 6)
obj_val_scipy = round(res_scipy.fun, 6)
print(f"وضعیت : {'✅ موفق' if res_scipy.success else '❌ ناموفق'} — {res_scipy.message}")
print(f"جواب x* : {np.round(sol_scipy, 4).tolist()}")
print(f"f(x*) : {obj_val_scipy}")
print(f"‖x-x*‖ : {np.linalg.norm(sol_scipy - np.array([1,1,1])):.2e}")
print(f"تکرار : {res_scipy.nit} iteration(s)")
print(f"ارزیابی : {res_scipy.nfev} function evaluations")
خروجی:
وضعیت : ✅ موفق — Optimization terminated successfully
جواب x* : [1.0, 1.0, 1.0]
f(x*) : 3.0
‖x-x*‖ : ~1e-10
تکرار : ~12 iteration(s)
ارزیابی : ~80 function evaluations
نکته کلیدی: SciPy برای محاسبه گرادیان نیاز دارد تابع را چند بار اضافه ارزیابی کند (Finite Difference)، به همین دلیل
nfev(تعداد ارزیابی) بیشتر از تعداد تکرار است.
2️⃣ GEKKO — بهینهسازی با سالور IPOPT داخلی
ویژگیها
- نصب:
pip install gekko - سالور IPOPT بهصورت باینری اجرایی داخلی همراه پکیج ارائه میشود
remote=False→ اجرای کاملاً محلی (بدون اینترنت)- مناسب برای کنترل بهینه و مسائل دینامیکی (DAE/ODE)
- متغیرها با
m.Arrayیاm.Varتعریف میشوند
ساختار مدل در GEKKO
① GEKKO(remote=False) → ساخت مدل محلی
② m.Var(value=...) → تعریف متغیرهای تصمیم
③ m.Obj(f(x)) → تعریف تابع هدف
④ m.Equation(g(x)==0) → تعریف قیود
⑤ m.options.SOLVER = 3 → انتخاب IPOPT
⑥ m.solve(disp=False) → اجرا
انتخاب سالور در GEKKO
| گزینه | توضیح |
|---|---|
SOLVER = 1 |
APOPT — سالور اختصاصی GEKKO، مناسب MINLP |
SOLVER = 2 |
BPOPT — سالور داخلی دیگر |
SOLVER = 3 |
IPOPT ← بهترین برای NLP پیوسته (Interior Point OPTimizer) |
الگوریتم IPOPT
IPOPT از روش نقطه داخلی (Interior Point Method) استفاده میکند:
- بهجای حرکت روی مرز فضای شدنی، از داخل آن عبور میکند
- برای مسائل بزرگمقیاس بسیار کارآمد است
کد پیادهسازی
from gekko import GEKKO
# ── ساخت مدل ──────────────────────────────────────────────────────────────
# remote=False → اجرای کاملاً محلی (بدون اینترنت)
# remote=True → ارسال به سرور APMonitor.com (نیاز به اینترنت)
m_g = GEKKO(remote=False)
# ── تعریف متغیرها ─────────────────────────────────────────────────────────
# m_g.Array(m_g.Var, 3, value=0.5)
# → آرایهای از ۳ متغیر بهینهسازی با مقدار اولیه ۰.۵
xg = m_g.Array(m_g.Var, 3, value=0.5)
x1g, x2g, x3g = xg[0], xg[1], xg[2]
m_g.Obj(x1g**2 + x2g**2 + x3g**2) # تابع هدف: کمینه کردن f = x1²+x2²+x3²
m_g.Equation(x1g + x2g + x3g == 3) # قید ۱ — خطی: x1+x2+x3 = 3
m_g.Equation(x1g * x2g * x3g == 1) # قید ۲ — غیرخطی: x1·x2·x3 = 1
m_g.options.SOLVER = 3 # IPOPT — Interior Point OPTimizer
m_g.options.IMODE = 3 # حالت ۳ = Steady-state optimization (پیشفرض NLP)
m_g.solve(disp=False) # حل با لاگ خاموش
# ── استخراج نتایج ─────────────────────────────────────────────────────────
# variable.value[0] → GEKKO از لیست استفاده میکند؛ [0] مقدار اسکالر است.
sol_gekko = np.round([x1g.value[0], x2g.value[0], x3g.value[0]], 6)
obj_val_gekko = round(sum(v**2 for v in sol_gekko), 6)
print(f"جواب x* : {np.round(sol_gekko, 4).tolist()}")
print(f"f(x*) : {obj_val_gekko}")
print(f"‖x-x*‖ : {np.linalg.norm(sol_gekko - np.array([1,1,1])):.2e}")
خروجی:
جواب x* : [1.0, 1.0, 1.0]
f(x*) : 3.0
‖x-x*‖ : ~1e-11
3️⃣ CasADi — مشتقگیری خودکار + IPOPT داخلی
ویژگیها
- نصب:
pip install casadi - تخصص در Automatic Differentiation (AD) — محاسبه گرادیان و هسیان دقیق
- زبان نمادین (Symbolic):
SX(سریعتر) یاMX(انعطاف بیشتر) - سالور IPOPT و OSQP بهصورت داخلی موجودند
- اینترفیس یکپارچه با MATLAB و C++
- پرکاربرد در رباتیک، کنترل پیشبین مدل (MPC) و یادگیری ماشین
Automatic Differentiation (AD) چیست؟
تفاوت اساسی با SciPy:
| روش | نحوه محاسبه گرادیان | دقت |
|---|---|---|
| SciPy | تفاضل متناهی (Finite Difference) | تقریبی $O(h) \approx 10^{-8}$ |
| CasADi | قانون زنجیره روی گراف محاسباتی | دقت ماشین $\approx 10^{-16}$ |
AD نه تفاضل عددی است، نه مشتقگیری نمادین — قانون زنجیره (Chain Rule) را روی گراف محاسباتی اعمال میکند:
- پیچیدگی: $O(n)$ برای forward mode، $O(m)$ برای reverse mode
- نتیجه: گرادیان کاملاً دقیق بدون هیچ تقریبی
ساختار مدل در CasADi
① SX.sym('x', n) → تعریف متغیرهای نمادین
② f = expr(x) → تعریف تابع هدف نمادین
③ g = vertcat(...) → بردار قیود نمادین
④ nlpsol(...) → ساخت سالور با backend IPOPT
⑤ solver(x0, lbg, ubg) → حل عددی
کد پیادهسازی
import casadi as ca
# ── تعریف متغیرهای نمادین (Symbolic Variables) ────────────────────────────
# ca.SX.sym('x', 3) → بردار نمادین 3×1
# SX = Scalar eXpression — برای مسائل کوچک: سریعترین نوع
# MX = Matrix eXpression — برای مسائل بزرگ یا با ماتریسهای پراکنده
# نکته: در این مرحله هیچ عدد محاسبه نمیشود — فقط نماد ریاضی
x_ca = ca.SX.sym('x', 3) # [x₁, x₂, x₃] — بردار نمادین
# ── تعریف تابع هدف نمادین ────────────────────────────────────────────────
# CasADi از این عبارت، گراف محاسباتی (Computational Graph) میسازد
# و سپس گرادیان و هسیان را دقیقاً از آن مشتق میگیرد.
obj_ca = x_ca[0]**2 + x_ca[1]**2 + x_ca[2]**2 # f = x1²+x2²+x3² (نمادین)
# ── تعریف بردار قیود g(x) ────────────────────────────────────────────────
# ca.vertcat() → متصل کردن عمودی (vertical concatenation)
# قراداد CasADi برای قیود: lbg ≤ g(x) ≤ ubg
# برای تساوی: lbg = ubg = 0 → g(x) = 0 دقیقاً
g_ca = ca.vertcat(
x_ca[0] + x_ca[1] + x_ca[2] - 3, # g₁ = x1+x2+x3-3 (باید = 0)
x_ca[0] * x_ca[1] * x_ca[2] - 1 # g₂ = x1·x2·x3-1 (باید = 0)
)
# ── ساخت دیکشنری NLP و سالور ──────────────────────────────────────────────
# فرمت استاندارد NLP در CasADi: {'x': متغیرها, 'f': هدف, 'g': قیود}
nlp = {'x': x_ca, 'f': obj_ca, 'g': g_ca}
solver_ca = ca.nlpsol(
'solver', 'ipopt', nlp,
{
'print_time' : 0,
'ipopt.print_level' : 0,
'ipopt.tol' : 1e-12, # تلورانس نهایی IPOPT
'ipopt.max_iter' : 1000 # حداکثر تکرار IPOPT
}
)
# ── حل مسأله ──────────────────────────────────────────────────────────────
# solver_ca(x0, lbg, ubg):
# x0 = [0.5, 0.5, 0.5] → نقطه شروع عددی
# lbg = [0, 0] → کران پایین g(x) ≥ 0
# ubg = [0, 0] → کران بالای g(x) ≤ 0
# ترکیب lbg=ubg=0 → g(x) = 0 (قید تساوی دقیق)
res_ca = solver_ca(x0=[0.5, 0.5, 0.5], lbg=[0, 0], ubg=[0, 0])
# ── استخراج نتایج ─────────────────────────────────────────────────────────
# res_ca['x'] → نوع DM (Dense Matrix) در CasADi
# res_ca['lam_g'] → ضرایب لاگرانژ قیود (Lagrange Multipliers)
sol_casadi = np.round(np.array(res_ca['x']).flatten(), 6)
obj_val_casadi = round(float(res_ca['f']), 6)
lam_g = np.array(res_ca['lam_g']).flatten()
print(f"جواب x* : {np.round(sol_casadi, 4).tolist()}")
print(f"f(x*) : {obj_val_casadi}")
print(f"‖x-x*‖ : {np.linalg.norm(sol_casadi - np.array([1,1,1])):.2e}")
print(f"λ (Lagrange Multipliers): {np.round(lam_g, 4).tolist()}")
خروجی:
جواب x* : [1.0, 1.0, 1.0]
f(x*) : 3.0
‖x-x*‖ : ~1e-12
λ (Lagrange Multipliers): [-2.0, -0.0]
📈 تصویرسازی — چشمانداز مسأله و مسیر حل
پلات ۱ — چشمانداز مسأله
چون مسأله ۳ متغیر دارد، برای رسم ۲D از تقلیل بُعد استفاده میکنیم:
از قید خطی: $x_3 = 3 - x_1 - x_2$
بعد از جایگذاری: \(f(x_1,x_2) = x_1^2 + x_2^2 + (3 - x_1 - x_2)^2\)
قید غیرخطی باقیمانده: $x_1 \cdot x_2 \cdot (3 - x_1 - x_2) = 1$
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# شبکه محاسباتی
x1_r = np.linspace(0.1, 2.7, 400)
x2_r = np.linspace(0.1, 2.7, 400)
X1, X2 = np.meshgrid(x1_r, x2_r)
X3 = 3 - X1 - X2 # مقدار x3 از قید خطی
F = X1**2 + X2**2 + X3**2 # تابع هدف بعد از حذف x3
G2 = X1 * X2 * X3 # قید غیرخطی (باید = 1 باشد)
mask = (X3 > 0) & (X1 > 0) & (X2 > 0)
F_plot = np.where(mask, F, np.nan)
G2_plot = np.where(mask, G2, np.nan)
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# ─ نمودار Contour 2D ─────────────────────────────────────────────────────
ax1 = axes[0]
cp = ax1.contourf(X1, X2, F_plot, levels=30, cmap='YlOrRd', alpha=0.85)
plt.colorbar(cp, ax=ax1)
ax1.contour(X1, X2, G2_plot, levels=[1.0], colors='#e63946', linewidths=2.5)
ax1.plot(1, 1, '*', color='#2d6a4f', markersize=18, label='$x^* = (1,1,1)$')
ax1.set_title('2D View: Objective Contours')
ax1.legend()
# ─ نمودار Surface 3D ─────────────────────────────────────────────────────
ax2 = axes[1]
ax2 = fig.add_subplot(1, 2, 2, projection='3d')
ax2.plot_surface(X1, X2, F_plot, cmap='coolwarm', alpha=0.7)
ax2.scatter([1], [1], [3], color='#2d6a4f', s=200, label='$x^* = (1,1,1)$')
ax2.set_title('3D Objective Surface')
plt.tight_layout()
plt.savefig('plot1_landscape.png', dpi=120, bbox_inches='tight')
plt.show()
پلات ۲ — مسیر همگرایی SciPy
این پلات نشان میدهد:
- از کجا شروع شد (
x0) - در هر تکرار به کجا رفت (مسیر آبی)
- چقدر طول کشید به جواب بهینه برسد
- نحوه کاهش تابع هدف و نقض قیودها در طول تکرارها
fig, axes = plt.subplots(1, 3, figsize=(17, 5))
x_path = np.array(scipy_history['x'])
iters = np.arange(len(scipy_history['f']))
# ① مسیر روی Contour
axes[0].contourf(X1, X2, F_plot, levels=25, cmap='YlOrRd', alpha=0.8)
axes[0].contour(X1, X2, G2_plot, levels=[1.0], colors='#e63946', linewidths=2, linestyles='--')
axes[0].plot(x_path[:, 0], x_path[:, 1], 'o-', color='#1d3557', linewidth=1.8)
axes[0].set_title('① Iteration Path on Contour Map')
# ② کاهش تابع هدف
axes[1].semilogy(iters, scipy_history['f'], 'o-', color='#e76f51')
axes[1].axhline(y=3.0, color='#2d6a4f', linestyle='--')
axes[1].set_title('② Objective Decrease per Iteration')
# ③ نقض قیودها
axes[2].semilogy(iters, np.array(scipy_history['g1']) + 1e-16, 's-', color='#457b9d', label='|g₁|')
axes[2].semilogy(iters, np.array(scipy_history['g2']) + 1e-16, '^-', color='#e63946', label='|g₂|')
axes[2].set_title('③ Constraint Convergence to Zero')
axes[2].legend()
plt.tight_layout()
plt.savefig('plot2_convergence.png', dpi=120, bbox_inches='tight')
plt.show()
پلات ۳ — مقایسه جامع سه سالور
libs = ['SciPy', 'GEKKO', 'CasADi']
colors = ['#e76f51', '#457b9d', '#2a9d8f']
devs = [np.linalg.norm(results[lib]['sol'] - np.array([1,1,1])) for lib in libs]
objs = [results[lib]['obj'] for lib in libs]
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
# ① مقدار f(x*) هر سالور
axes[0,0].bar(libs, objs, color=colors)
axes[0,0].axhline(y=3.0, color='#e63946', linestyle='--')
axes[0,0].set_title('① Objective Value at Optimum')
# ② انحراف از جواب تحلیلی
axes[0,1].bar(libs, devs, color=colors)
axes[0,1].set_yscale('log')
axes[0,1].set_title('② Deviation from Analytical Solution')
# ③ مقادیر x1, x2, x3
x_idx, width = np.arange(3), 0.22
for i, (lib, col) in enumerate(zip(libs, colors)):
axes[1,0].bar(x_idx + i*width, results[lib]['sol'], width, label=lib, color=col)
axes[1,0].axhline(y=1.0, color='#e63946', linestyle='--')
axes[1,0].set_title('③ Solution Variables x1, x2, x3')
axes[1,0].legend()
# ④ جدول مشخصات کیفی
axes[1,1].axis('off')
# (جدول مشخصات در نوتبوک بهصورت جدول رنگی نمایش داده میشود)
plt.tight_layout()
plt.savefig('plot3_comparison.png', dpi=120, bbox_inches='tight')
plt.show()
📊 جدول مقایسه نتایج
import pandas as pd
rows = []
x_star = np.array([1.0, 1.0, 1.0])
for lib, data in results.items():
sol = data['sol']
rows.append({
'کتابخانه' : lib,
'x₁' : round(float(sol[0]), 6),
'x₂' : round(float(sol[1]), 6),
'x₃' : round(float(sol[2]), 6),
'f(x*)' : round(float(data['obj']), 8),
'‖x−x*‖' : f"{np.linalg.norm(np.array([float(sol[0]), float(sol[1]), float(sol[2])]) - x_star):.2e}",
'الگوریتم' : data['solver'],
'گرادیان' : data['grad'],
})
df = pd.DataFrame(rows)
print(df.to_string())
خروجی نمونه:
| # | کتابخانه | x₁ | x₂ | x₃ | f(x*) | ‖x−x*‖ | الگوریتم | گرادیان |
|---|---|---|---|---|---|---|---|---|
| 1 | SciPy | 1.0 | 1.0 | 1.0 | 3.0 | ~1e-10 | SLSQP | Finite Difference |
| 2 | GEKKO | 1.0 | 1.0 | 1.0 | 3.0 | ~1e-11 | IPOPT (built-in) | Auto-Diff (internal) |
| 3 | CasADi | 1.0 | 1.0 | 1.0 | 3.0 | ~1e-12 | IPOPT (built-in) | Exact AD |
🎯 جواب تحلیلی: $x_1 = x_2 = x_3 = 1.0 \quad \Rightarrow \quad f^* = 3.0$ (از نامساوی AM-GM)
🏁 جمعبندی — مقایسه سه سالور NLP
نتایج عددی
همه سه کتابخانه به جواب تحلیلی $x_1 = x_2 = x_3 = 1$، $f^* = 3$ رسیدند.
تفاوت اصلی: روش محاسبه گرادیان
| سالور | روش گرادیان | دقت | توضیح |
|---|---|---|---|
| SciPy | Finite Difference | تقریبی $O(h)$ | $\frac{\partial f}{\partial x_i} \approx \frac{f(x+he_i)-f(x)}{h}$ |
| GEKKO | Auto-Diff (داخلی) | بالا | از گراف محاسباتی IPOPT |
| CasADi | Exact Auto-Diff | دقت ماشین ≈ $10^{-16}$ | Chain Rule روی گراف نمادین SX |
مراحل کلی حل هر NLP (مشترک بین هر سه سالور)
① تعریف f(x) — تابع هدف
② تعریف g(x) = 0 — قیود تساوی
③ x₀ — نقطه شروع اولیه
④ حل ← سالور (SLSQP یا IPOPT)
↳ محاسبه گرادیان ∇f و ژاکوبین ∇g
↳ حل زیرمسأله (QP یا barrier)
↳ بهروزرسانی x ← x + αΔx
↳ بررسی شرایط KKT (Karush-Kuhn-Tucker)
⑤ خروجی: x* ، f(x*) ، ضرایب لاگرانژ λ
راهنمای انتخاب
| سناریو شما | پیشنهاد |
|---|---|
| شروع سریع، بدون نصب اضافی | SciPy |
| کنترل بهینه، سیستم دینامیک، DAE | GEKKO |
| رباتیک، MPC، گرادیان دقیق، C++ | CasADi |
💡 قانون سرانگشتی: مسأله کوچک؟ → SciPy. نیاز به دقت بالا یا مقیاس بزرگ؟ → CasADi.
📦 نصب پکیجها
# SciPy (معمولاً از قبل نصب است)
pip install scipy numpy
# GEKKO
pip install gekko
# CasADi
pip install casadi
# برای تصویرسازی
pip install matplotlib pandas
📁 فایلهای خروجی
| فایل | توضیح |
|---|---|
plot1_landscape.png |
چشمانداز مسأله — Contour 2D و Surface 3D |
plot2_convergence.png |
مسیر همگرایی SLSQP در هر تکرار |
plot3_comparison.png |
مقایسه جامع سه سالور از نظر دقت و جواب |