mathmethods-mcp 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mathmethods/__init__.py +7 -0
- mathmethods/__main__.py +6 -0
- mathmethods/compiler.py +202 -0
- mathmethods/core/__init__.py +36 -0
- mathmethods/core/differentiation.py +122 -0
- mathmethods/core/dynamic_1d.py +567 -0
- mathmethods/core/dynamic_2d_conservative.py +283 -0
- mathmethods/core/dynamic_2d_lanchester.py +264 -0
- mathmethods/core/dynamic_2d_linear.py +269 -0
- mathmethods/core/dynamic_2d_non_homogeneous.py +710 -0
- mathmethods/core/dynamic_2d_nonlinear.py +298 -0
- mathmethods/core/integration.py +396 -0
- mathmethods/core/interpolation.py +126 -0
- mathmethods/core/monte_carlo.py +312 -0
- mathmethods/core/ode.py +154 -0
- mathmethods/core/root_finding.py +339 -0
- mathmethods/core/utils.py +226 -0
- mathmethods/server.py +1414 -0
- mathmethods_mcp-0.1.0.dist-info/METADATA +436 -0
- mathmethods_mcp-0.1.0.dist-info/RECORD +24 -0
- mathmethods_mcp-0.1.0.dist-info/WHEEL +5 -0
- mathmethods_mcp-0.1.0.dist-info/entry_points.txt +2 -0
- mathmethods_mcp-0.1.0.dist-info/licenses/LICENSE +21 -0
- mathmethods_mcp-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import sympy as sp
|
|
3
|
+
import math
|
|
4
|
+
from typing import Dict, Any, List
|
|
5
|
+
|
|
6
|
+
class Dynamic2DLinearService:
|
|
7
|
+
@staticmethod
|
|
8
|
+
def clasificar_sistema(A) -> str:
|
|
9
|
+
traza = float(np.trace(A))
|
|
10
|
+
determinante = float(np.linalg.det(A))
|
|
11
|
+
discriminante = traza**2 - 4 * determinante
|
|
12
|
+
autovalores = np.linalg.eigvals(A)
|
|
13
|
+
|
|
14
|
+
if abs(determinante) < 1e-10:
|
|
15
|
+
return "Caso degenerado / determinante cero"
|
|
16
|
+
if determinante < 0:
|
|
17
|
+
return "Silla"
|
|
18
|
+
if discriminante > 1e-10:
|
|
19
|
+
if traza < 0:
|
|
20
|
+
return "Nodo estable"
|
|
21
|
+
elif traza > 0:
|
|
22
|
+
return "Nodo inestable"
|
|
23
|
+
else:
|
|
24
|
+
return "Nodo con traza cero / caso especial"
|
|
25
|
+
elif abs(discriminante) <= 1e-10:
|
|
26
|
+
if traza < 0:
|
|
27
|
+
return "Nodo degenerado estable"
|
|
28
|
+
elif traza > 0:
|
|
29
|
+
return "Nodo degenerado inestable"
|
|
30
|
+
else:
|
|
31
|
+
return "Caso no hiperbolico"
|
|
32
|
+
else:
|
|
33
|
+
parte_real = float(np.real(autovalores[0]))
|
|
34
|
+
if abs(parte_real) < 1e-10:
|
|
35
|
+
return "Centro"
|
|
36
|
+
elif parte_real < 0:
|
|
37
|
+
return "Foco estable"
|
|
38
|
+
else:
|
|
39
|
+
return "Foco inestable"
|
|
40
|
+
|
|
41
|
+
@staticmethod
|
|
42
|
+
def descripcion_comportamiento(clasificacion: str) -> str:
|
|
43
|
+
if "Silla" in clasificacion:
|
|
44
|
+
return "Las trayectorias se acercan en una direccion y se alejan en otra."
|
|
45
|
+
if "Nodo estable" in clasificacion:
|
|
46
|
+
return "Todas las trayectorias cercanas tienden al equilibrio sin oscilar."
|
|
47
|
+
if "Nodo inestable" in clasificacion:
|
|
48
|
+
return "Las trayectorias se alejan del equilibrio sin oscilar."
|
|
49
|
+
if "Foco estable" in clasificacion:
|
|
50
|
+
return "Las trayectorias giran en espiral hacia el equilibrio."
|
|
51
|
+
if "Foco inestable" in clasificacion:
|
|
52
|
+
return "Las trayectorias giran en espiral alejandose del equilibrio."
|
|
53
|
+
if "Centro" in clasificacion:
|
|
54
|
+
return "Las trayectorias cerradas rodean el equilibrio."
|
|
55
|
+
if "degenerado" in clasificacion:
|
|
56
|
+
return "El sistema tiene autovalores repetidos; puede requerir analizar autovectores."
|
|
57
|
+
return "Caso especial; requiere analisis adicional."
|
|
58
|
+
|
|
59
|
+
@staticmethod
|
|
60
|
+
def rk4_sistema(A, B, X0, t0, t_fin, h):
|
|
61
|
+
n_steps_t = max(1, int(round((t_fin - t0) / h)))
|
|
62
|
+
t_vals = np.linspace(t0, t_fin, n_steps_t + 1)
|
|
63
|
+
X_vals = np.zeros((len(t_vals), 2))
|
|
64
|
+
X_vals[0] = X0
|
|
65
|
+
|
|
66
|
+
for i in range(len(t_vals) - 1):
|
|
67
|
+
ti = t_vals[i]
|
|
68
|
+
Xi = X_vals[i]
|
|
69
|
+
h_loc = t_vals[i + 1] - ti
|
|
70
|
+
|
|
71
|
+
k1 = A @ Xi + B
|
|
72
|
+
k2 = A @ (Xi + h_loc * k1 / 2) + B
|
|
73
|
+
k3 = A @ (Xi + h_loc * k2 / 2) + B
|
|
74
|
+
k4 = A @ (Xi + h_loc * k3) + B
|
|
75
|
+
|
|
76
|
+
X_vals[i + 1] = Xi + (h_loc / 6) * (k1 + 2 * k2 + 2 * k3 + k4)
|
|
77
|
+
|
|
78
|
+
return t_vals, X_vals
|
|
79
|
+
|
|
80
|
+
@staticmethod
|
|
81
|
+
def solve(payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
82
|
+
a = float(payload.get('a', 3.0))
|
|
83
|
+
b = float(payload.get('b', 1.0))
|
|
84
|
+
c = float(payload.get('c', 1.0))
|
|
85
|
+
d = float(payload.get('d', 3.0))
|
|
86
|
+
e = float(payload.get('e', 0.0))
|
|
87
|
+
f = float(payload.get('f', 0.0))
|
|
88
|
+
x0 = float(payload.get('x0', 1.0))
|
|
89
|
+
y0 = float(payload.get('y0', 1.0))
|
|
90
|
+
t0 = float(payload.get('t0', 0.0))
|
|
91
|
+
t_fin = float(payload.get('t_fin', 10.0))
|
|
92
|
+
h = float(payload.get('h', 0.01))
|
|
93
|
+
x_min = float(payload.get('x_min', -5.0))
|
|
94
|
+
x_max = float(payload.get('x_max', 5.0))
|
|
95
|
+
y_min = float(payload.get('y_min', -5.0))
|
|
96
|
+
y_max = float(payload.get('y_max', 5.0))
|
|
97
|
+
cantidad_trayectorias = int(payload.get('cantidad_trayectorias', 16))
|
|
98
|
+
|
|
99
|
+
A = np.array([[a, b], [c, d]], dtype=float)
|
|
100
|
+
B = np.array([e, f], dtype=float)
|
|
101
|
+
|
|
102
|
+
det_A = float(np.linalg.det(A))
|
|
103
|
+
equilibrio_unico = abs(det_A) > 1e-12
|
|
104
|
+
equilibrio_pnto = None
|
|
105
|
+
if equilibrio_unico:
|
|
106
|
+
eq = -np.linalg.solve(A, B)
|
|
107
|
+
equilibrio_pnto = {"x": float(eq[0]), "y": float(eq[1])}
|
|
108
|
+
|
|
109
|
+
autovalores, autovectores = np.linalg.eig(A)
|
|
110
|
+
traza = float(np.trace(A))
|
|
111
|
+
discriminante = traza**2 - 4 * det_A
|
|
112
|
+
clasificacion = Dynamic2DLinearService.clasificar_sistema(A)
|
|
113
|
+
comportamiento = Dynamic2DLinearService.descripcion_comportamiento(clasificacion)
|
|
114
|
+
|
|
115
|
+
list_autovalores = []
|
|
116
|
+
for av in autovalores:
|
|
117
|
+
list_autovalores.append({
|
|
118
|
+
"real": float(np.real(av)),
|
|
119
|
+
"imag": float(np.imag(av))
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
list_autovectores = []
|
|
123
|
+
for i in range(len(autovalores)):
|
|
124
|
+
list_autovectores.append({
|
|
125
|
+
"vx": float(np.real(autovectores[0, i])),
|
|
126
|
+
"vy": float(np.real(autovectores[1, i]))
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
X0 = np.array([x0, y0], dtype=float)
|
|
130
|
+
t_vals, X_vals = Dynamic2DLinearService.rk4_sistema(A, B, X0, t0, t_fin, h)
|
|
131
|
+
|
|
132
|
+
dxdt_vals = [float(val) for val in (A @ X_vals.T + B[:, None])[0]]
|
|
133
|
+
dydt_vals = [float(val) for val in (A @ X_vals.T + B[:, None])[1]]
|
|
134
|
+
|
|
135
|
+
principal_trajectory = {
|
|
136
|
+
"t": t_vals.tolist(),
|
|
137
|
+
"x": X_vals[:, 0].tolist(),
|
|
138
|
+
"y": X_vals[:, 1].tolist(),
|
|
139
|
+
"dxdt": dxdt_vals,
|
|
140
|
+
"dydt": dydt_vals
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
automatic_trajectories = []
|
|
144
|
+
n_side = int(math.ceil(math.sqrt(cantidad_trayectorias)))
|
|
145
|
+
if n_side > 1:
|
|
146
|
+
x0_auto = np.linspace(x_min, x_max, n_side)
|
|
147
|
+
y0_auto = np.linspace(y_min, y_max, n_side)
|
|
148
|
+
for xi in x0_auto:
|
|
149
|
+
for yi in y0_auto:
|
|
150
|
+
_, X_auto = Dynamic2DLinearService.rk4_sistema(A, B, np.array([xi, yi]), t0, t_fin, h)
|
|
151
|
+
automatic_trajectories.append({
|
|
152
|
+
"x": X_auto[::5, 0].tolist(),
|
|
153
|
+
"y": X_auto[::5, 1].tolist()
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
# Limpieza de decimales nulos (.0) para SymPy
|
|
157
|
+
def to_num(val):
|
|
158
|
+
return int(val) if val.is_integer() else val
|
|
159
|
+
|
|
160
|
+
a_sym, b_sym = to_num(a), to_num(b)
|
|
161
|
+
c_sym, d_sym = to_num(c), to_num(d)
|
|
162
|
+
e_sym, f_sym = to_num(e), to_num(f)
|
|
163
|
+
x0_sym, y0_sym = to_num(x0), to_num(y0)
|
|
164
|
+
t0_sym = to_num(t0)
|
|
165
|
+
|
|
166
|
+
xs = sp.Function('x')
|
|
167
|
+
ys = sp.Function('y')
|
|
168
|
+
t_sym = sp.Symbol('t', real=True)
|
|
169
|
+
|
|
170
|
+
sol_homogenea_latex = []
|
|
171
|
+
try:
|
|
172
|
+
# Para la solución homogénea, forzamos B a ser [0, 0]
|
|
173
|
+
eq1_h = sp.Eq(sp.diff(xs(t_sym), t_sym), a_sym * xs(t_sym) + b_sym * ys(t_sym))
|
|
174
|
+
eq2_h = sp.Eq(sp.diff(ys(t_sym), t_sym), c_sym * xs(t_sym) + d_sym * ys(t_sym))
|
|
175
|
+
sol_homogenea = sp.dsolve([eq1_h, eq2_h], ics={xs(t0_sym): x0_sym, ys(t0_sym): y0_sym})
|
|
176
|
+
|
|
177
|
+
if isinstance(sol_homogenea, list):
|
|
178
|
+
sol_homogenea_latex = [sp.latex(eq) for eq in sol_homogenea]
|
|
179
|
+
else:
|
|
180
|
+
sol_homogenea_latex = [sp.latex(sol_homogenea)]
|
|
181
|
+
except Exception:
|
|
182
|
+
sol_homogenea_latex = ["SymPy no pudo resolver la parte homogénea."]
|
|
183
|
+
|
|
184
|
+
# Solución General (No Homogénea)
|
|
185
|
+
sol_general_latex = []
|
|
186
|
+
try:
|
|
187
|
+
eq1_g = sp.Eq(sp.diff(xs(t_sym), t_sym), a_sym * xs(t_sym) + b_sym * ys(t_sym) + e_sym)
|
|
188
|
+
eq2_g = sp.Eq(sp.diff(ys(t_sym), t_sym), c_sym * xs(t_sym) + d_sym * ys(t_sym) + f_sym)
|
|
189
|
+
sol_general = sp.dsolve([eq1_g, eq2_g], ics={xs(t0_sym): x0_sym, ys(t0_sym): y0_sym})
|
|
190
|
+
|
|
191
|
+
if isinstance(sol_general, list):
|
|
192
|
+
sol_general_latex = [sp.latex(eq) for eq in sol_general]
|
|
193
|
+
else:
|
|
194
|
+
sol_general_latex = [sp.latex(sol_general)]
|
|
195
|
+
except Exception:
|
|
196
|
+
sol_general_latex = ["SymPy no pudo resolver el sistema completo."]
|
|
197
|
+
|
|
198
|
+
# Solución Particular (Punto de equilibrio si B es constante)
|
|
199
|
+
sol_particular_latex = []
|
|
200
|
+
if equilibrio_unico and equilibrio_pnto:
|
|
201
|
+
sol_particular_latex = [
|
|
202
|
+
f"x_p(t) = {equilibrio_pnto['x']}",
|
|
203
|
+
f"y_p(t) = {equilibrio_pnto['y']}"
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
# Nulclinas
|
|
207
|
+
nulclina_x_pts = []
|
|
208
|
+
if abs(b) > 1e-9:
|
|
209
|
+
nulclina_x_pts = [[x_min, (-a * x_min - e) / b], [x_max, (-a * x_max - e) / b]]
|
|
210
|
+
elif abs(a) > 1e-9:
|
|
211
|
+
nulclina_x_pts = [[-e / a, y_min], [-e / a, y_max]]
|
|
212
|
+
|
|
213
|
+
nulclina_y_pts = []
|
|
214
|
+
if abs(d) > 1e-9:
|
|
215
|
+
nulclina_y_pts = [[x_min, (-c * x_min - f) / d], [x_max, (-c * x_max - f) / d]]
|
|
216
|
+
elif abs(c) > 1e-9:
|
|
217
|
+
nulclina_y_pts = [[-f / c, y_min], [-f / c, y_max]]
|
|
218
|
+
|
|
219
|
+
# Despejar y(x) en las Nulclinas con SymPy
|
|
220
|
+
x_sym, y_sym_var = sp.symbols('x y', real=True)
|
|
221
|
+
|
|
222
|
+
nul_x_expr = sp.Eq(a_sym * x_sym + b_sym * y_sym_var + e_sym, 0)
|
|
223
|
+
try:
|
|
224
|
+
sol_x = sp.solve(nul_x_expr, y_sym_var)
|
|
225
|
+
nul_x_despejada = f"y = {str(sol_x[0]).replace('**', '^').replace('*', ' ')}" if sol_x else "No despejable como y(x)"
|
|
226
|
+
except Exception:
|
|
227
|
+
nul_x_despejada = "No despejable como y(x)"
|
|
228
|
+
|
|
229
|
+
nul_y_expr = sp.Eq(c_sym * x_sym + d_sym * y_sym_var + f_sym, 0)
|
|
230
|
+
try:
|
|
231
|
+
sol_y = sp.solve(nul_y_expr, y_sym_var)
|
|
232
|
+
nul_y_despejada = f"y = {str(sol_y[0]).replace('**', '^').replace('*', ' ')}" if sol_y else "No despejable como y(x)"
|
|
233
|
+
except Exception:
|
|
234
|
+
nul_y_despejada = "No despejable como y(x)"
|
|
235
|
+
|
|
236
|
+
real_eigenvectors_lines = []
|
|
237
|
+
if equilibrio_unico:
|
|
238
|
+
for i in range(len(autovalores)):
|
|
239
|
+
if abs(np.imag(autovalores[i])) < 1e-10:
|
|
240
|
+
vx, vy = float(np.real(autovectores[0, i])), float(np.real(autovectores[1, i]))
|
|
241
|
+
norm = math.sqrt(vx**2 + vy**2)
|
|
242
|
+
if norm > 1e-9:
|
|
243
|
+
vx, vy = vx / norm, vy / norm
|
|
244
|
+
real_eigenvectors_lines.append([
|
|
245
|
+
[equilibrio_pnto["x"] - 20 * vx, equilibrio_pnto["y"] - 20 * vy],
|
|
246
|
+
[equilibrio_pnto["x"] + 20 * vx, equilibrio_pnto["y"] + 20 * vy]
|
|
247
|
+
])
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
"matrix_a": [[a, b], [c, d]],
|
|
251
|
+
"vector_b": [e, f],
|
|
252
|
+
"traza": traza,
|
|
253
|
+
"determinante": det_A,
|
|
254
|
+
"discriminante": discriminante,
|
|
255
|
+
"clasificacion": clasificacion,
|
|
256
|
+
"comportamiento": comportamiento,
|
|
257
|
+
"equilibrio": equilibrio_pnto,
|
|
258
|
+
"equilibrio_unico": equilibrio_unico,
|
|
259
|
+
"autovalores": list_autovalores,
|
|
260
|
+
"autovectores": list_autovectores,
|
|
261
|
+
"solucion_homogenea_latex": sol_homogenea_latex,
|
|
262
|
+
"solucion_particular_latex": sol_particular_latex,
|
|
263
|
+
"solucion_general_latex": sol_general_latex,
|
|
264
|
+
"nulclina_x": {"puntos": nulclina_x_pts, "ecuacion_despejada": nul_x_despejada},
|
|
265
|
+
"nulclina_y": {"puntos": nulclina_y_pts, "ecuacion_despejada": nul_y_despejada},
|
|
266
|
+
"principal_trajectory": principal_trajectory,
|
|
267
|
+
"automatic_trajectories": automatic_trajectories,
|
|
268
|
+
"real_eigenvectors_lines": real_eigenvectors_lines
|
|
269
|
+
}
|