From 53dd4eb801a99b2eb42623aa825230bb36227991 Mon Sep 17 00:00:00 2001 From: Miguel Date: Fri, 6 Jun 2025 15:05:56 +0200 Subject: [PATCH] mejorado de solve para equaciones con varias variables --- hybrid_calc_history.txt | 6 +- hybrid_calc_settings.json | 2 +- main_evaluation.py => main_evaluation_OLD.py | 0 main_evaluation_puro.py | 169 ++++++++++++++----- simple_debug.py | 2 +- test_final.py | 25 +++ 6 files changed, 158 insertions(+), 46 deletions(-) rename main_evaluation.py => main_evaluation_OLD.py (100%) create mode 100644 test_final.py diff --git a/hybrid_calc_history.txt b/hybrid_calc_history.txt index a52ced7..bead65a 100644 --- a/hybrid_calc_history.txt +++ b/hybrid_calc_history.txt @@ -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() diff --git a/hybrid_calc_settings.json b/hybrid_calc_settings.json index b491249..b85153f 100644 --- a/hybrid_calc_settings.json +++ b/hybrid_calc_settings.json @@ -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, diff --git a/main_evaluation.py b/main_evaluation_OLD.py similarity index 100% rename from main_evaluation.py rename to main_evaluation_OLD.py diff --git a/main_evaluation_puro.py b/main_evaluation_puro.py index 471e1d8..af33895 100644 --- a/main_evaluation_puro.py +++ b/main_evaluation_puro.py @@ -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 ===") diff --git a/simple_debug.py b/simple_debug.py index 0bac175..afcccd1 100644 --- a/simple_debug.py +++ b/simple_debug.py @@ -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): diff --git a/test_final.py b/test_final.py new file mode 100644 index 0000000..5ceae6f --- /dev/null +++ b/test_final.py @@ -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() \ No newline at end of file