mejorado de solve para equaciones con varias variables

This commit is contained in:
Miguel 2025-06-06 15:05:56 +02:00
parent c0e638ec79
commit 53dd4eb801
6 changed files with 158 additions and 46 deletions

View File

@ -1,9 +1,11 @@
# x = 5
y = x + 3
z = y + x
solve(x)
solve(z)
x=?
solve()
# Instanciación via sympify
ip = IP4(120.11.255.2,30)
ip.Nodes()

View File

@ -1,6 +1,6 @@
{
"window_geometry": "1020x700+356+1216",
"sash_pos_x": 327,
"sash_pos_x": 343,
"symbolic_mode": true,
"show_numeric_approximation": true,
"keep_symbolic_fractions": true,

View File

@ -138,6 +138,15 @@ class PureAlgebraicEngine:
def _apply_tokenization(self, line: str) -> str:
"""Aplica tokenización dinámica a la línea de entrada"""
# 1. TOKENIZACIÓN ESPECIAL: _x=? → solve(_x)
variable_solve_pattern = r'([a-zA-Z_][a-zA-Z0-9_]*)\s*=\s*\?'
if re.match(variable_solve_pattern, line.strip()):
var_name = re.match(variable_solve_pattern, line.strip()).group(1)
tokenized_line = f"solve({var_name})"
self.logger.debug(f"Tokenización solve: '{line}''{tokenized_line}'")
line = tokenized_line
if not self.tokenization_patterns:
return line
@ -204,8 +213,8 @@ class PureAlgebraicEngine:
return EvaluationResult(line, error_msg, "error", False, str(e))
def _is_solve_shortcut(self, line: str) -> bool:
"""Detecta atajos de resolución como x=? o solve(x)"""
return line.endswith('=?') or line.startswith('solve(')
"""Detecta atajos de resolución como solve(x)"""
return line.startswith('solve(')
def _is_comparison(self, line: str) -> bool:
"""Detecta comparaciones como ==, <=, >=, !="""
@ -215,25 +224,25 @@ class PureAlgebraicEngine:
def _evaluate_solve_shortcut(self, line: str) -> EvaluationResult:
"""Evalúa atajos de resolución"""
try:
if line.endswith('=?'):
# Atajo x=?
var_name = line[:-2].strip()
var_symbol = sp.Symbol(var_name)
solution = self._solve_for_variable(var_symbol)
if line.startswith('solve('):
# Función solve() - manejar casos especiales primero
# Extraer el contenido dentro de solve()
import re
match = re.match(r'solve\(([^)]+)\)', line)
if match:
var_content = match.group(1).strip()
# Si es una variable simple, usar nuestra lógica mejorada
if re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', var_content):
# Crear símbolo directamente sin usar context para evitar sustitución
var_symbol = sp.Symbol(var_content)
solution_result = self._smart_solve(var_symbol)
output = str(solution_result)
numeric = self._get_numeric_approximation(solution_result)
if numeric and str(solution_result) != str(numeric):
output += f"{numeric}"
return EvaluationResult(line, output, "symbolic", True, is_solve_query=True)
# Output conciso
if solution != var_symbol:
output = str(solution)
numeric = self._get_numeric_approximation(solution)
if numeric and str(solution) != str(numeric):
output += f"{numeric}"
else:
output = str(var_symbol)
return EvaluationResult(line, output, "symbolic", True, is_solve_query=True)
elif line.startswith('solve('):
# Función solve() - usar sympify unificado
# Para casos más complejos, usar sympify
result = self._evaluate_expression(line)
result.is_solve_query = True
return result
@ -360,8 +369,23 @@ class PureAlgebraicEngine:
return "Sin solución"
except Exception as e:
return f"Error resolviendo sistema: {e}"
elif len(args) == 1 and hasattr(args[0], 'is_Symbol') and args[0].is_Symbol:
# solve(variable) - resolver para una variable específica y devolver ecuación
var_symbol = args[0]
solution_value = self._solve_for_variable(var_symbol)
# Si encontramos una solución, devolver como ecuación
if solution_value != var_symbol:
return Eq(var_symbol, solution_value)
else:
# Si no hay solución en las ecuaciones, verificar en symbol_table
var_name = str(var_symbol)
if var_name in self.symbol_table:
return Eq(var_symbol, self.symbol_table[var_name])
else:
return var_symbol
else:
# solve() con argumentos específicos
# solve() con argumentos específicos (múltiples variables, ecuaciones, etc.)
return solve(*args, **kwargs)
def _solve_for_variable(self, var_symbol):
@ -370,20 +394,82 @@ class PureAlgebraicEngine:
return var_symbol
try:
# Intentar resolver la variable en el contexto del sistema
solution = solve(self.equations, var_symbol, dict=True)
# 1. Buscar si la variable tiene asignación directa en symbol_table
var_name = str(var_symbol)
if var_name in self.symbol_table:
# Devolver el valor de la asignación directa
return self.symbol_table[var_name]
if solution and var_symbol in solution[0]:
return solution[0][var_symbol]
else:
# Intentar resolver solo las ecuaciones que contienen esta variable
relevant_eqs = [eq for eq in self.equations if var_symbol in eq.free_symbols]
if relevant_eqs:
solution = solve(relevant_eqs, var_symbol)
if solution:
return solution[0] if isinstance(solution, list) else solution
# 2. Buscar ecuaciones que contengan esta variable
relevant_eqs = [eq for eq in self.equations if var_symbol in eq.free_symbols]
if relevant_eqs:
# Estrategia 1: Buscar asignación directa
for eq in relevant_eqs:
left_expr = eq.lhs
right_expr = eq.rhs
# Caso directo: variable = expresión
if left_expr == var_symbol:
return right_expr
elif right_expr == var_symbol:
return left_expr
return var_symbol
# Estrategia 2: Resolver algebraicamente ecuación por ecuación
for eq in relevant_eqs:
try:
single_solution = solve(eq, var_symbol)
if single_solution and isinstance(single_solution, list) and single_solution:
result = single_solution[0]
if result != var_symbol:
return result
except:
continue
# Estrategia 3: Resolver el sistema completo para obtener expresiones
# en términos de otras variables
try:
# Obtener todas las variables del sistema excepto la que queremos resolver
all_vars = list(self.variables)
other_vars = [v for v in all_vars if v != var_symbol]
if other_vars:
# Intentar resolver el sistema para todas las variables
# esto nos dará expresiones en términos de variables libres
solution = solve(self.equations, all_vars, dict=True)
if solution and var_symbol in solution[0]:
return solution[0][var_symbol]
# Alternativa: resolver en términos de una variable específica
for other_var in other_vars:
try:
# Resolver el sistema dejando other_var como variable libre
vars_to_solve = [v for v in all_vars if v != other_var]
if var_symbol in vars_to_solve:
partial_solution = solve(self.equations, vars_to_solve, dict=True)
if partial_solution and var_symbol in partial_solution[0]:
result = partial_solution[0][var_symbol]
# Verificar que la solución contenga la otra variable
if other_var in result.free_symbols:
return result
except:
continue
except:
pass
# Estrategia 4: Si todo falla, usar la primera ecuación relevante
eq = relevant_eqs[0]
try:
expr_to_solve = eq.lhs - eq.rhs
solution = solve(expr_to_solve, var_symbol)
if solution:
result = solution[0] if isinstance(solution, list) else solution
if result != var_symbol:
return result
except:
pass
# 5. Si nada funciona, devolver la variable tal como está
return var_symbol
except Exception as e:
self.logger.debug(f"Error resolviendo {var_symbol}: {e}")
@ -467,18 +553,17 @@ if __name__ == "__main__":
engine = PureAlgebraicEngine()
test_lines = [
"x = 5",
"y = x + 3",
"solve()",
"a = b + 5", # Ecuación con variables
"b=?", # ✅ Tokenización: b=? → solve(b)
"solve(b)", # ✅ Debería dar: Eq(b, a - 5)
"x = 10", # Asignación directa
"y = x + 3", # Asignación usando variable
"x=?", # ✅ Tokenización: x=? → solve(x)
"solve(x)", # ✅ Debería dar: Eq(x, 10)
"solve()", # Resolver todo el sistema
"ip = IP4(10.1.1.1)",
"ip + 1", # ✅ Aritmética IP con _op_priority
"ip.to_hex()",
"10.1.1.1", # ✅ Tokenización automática
"16#FF + 1", # ✅ Aritmética con IntBase y _op_priority
"16#FF", # IntBase directo
"16#FF.to_hex()", # Método directo
"n = 16#FF", # Asignación IntBase
"n.to_hex()" # Método del objeto asignado
]
print("=== DEMO MOTOR ALGEBRAICO UNIFICADO ===")

View File

@ -22,7 +22,7 @@ from datetime import datetime
from pathlib import Path
# Importar motores de evaluación
from main_evaluation import HybridEvaluationEngine
from main_evaluation_OLD import HybridEvaluationEngine
def run_debug(input_file: str, output_file: str = None, verbose: bool = False):

25
test_final.py Normal file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env python3
"""
Test final con el ejemplo exacto del usuario
"""
import main_evaluation_puro
def test_ejemplo_usuario():
print("=== EJEMPLO ORIGINAL DEL USUARIO ===")
engine = main_evaluation_puro.PureAlgebraicEngine()
casos = [
"y = x + 3",
"z = y + x",
"solve(x)",
"solve(z)"
]
for caso in casos:
result = engine.evaluate_line(caso)
print(f"{caso}{result.output}")
if __name__ == "__main__":
test_ejemplo_usuario()