0

As part of a program, I am struggling to implement a function to solve a system of equations. First, I receive a set of equations from text input. Please note that the number of equations and thus variables are unknown, as well as the name of variables. For simplicity, I consider only two equations in a list of strings. Afterwards, equations are edited, variables are identified, and the guesses array is created.

from math import exp
import sympy
from sympy import sympify

eq_list = ["x + y**2 = 4", "exp(x) + x*y = 3"]
eq_list_altered = []
for eq in eq_list:
    start, end = eq.split('=')
    eq_list_altered.append(start + '-' + end)

guess = [0.1 for i in eq_list_altered]
vars_set = set()

for eq in eq_list_altered:
    vars_set.update((sympify(eq)).free_symbols)
vars_lst = list(vars_set) 

The second step is where I am struggling. The main issue is that the input can have n equations and n unknowns. WHilst the equations are easy to deal with, I am trying to find the simplest way to assign the variables to the guess values, i.e.:

def f(variables) :

    x, y = variables # How to this for any variable name and len ?
    res = []
    for eq in eq_list_edit:
        res.append(eval(eq))
    return res

solution = opt.root(f, guess)
print(solution.x)

I already tried using locals(), globals() and dictionaries without success. It seems a simple procedure to set a list of variables equal to a list of values; however, after two days of searching and reading, I could not find a simple solution. The only one working is:

    dict_tmp = {vars_lst[i]: variables[i] for i in range(len(vars_lst))}
    print(dict_tmp)
    for k in dict_tmp:
        globals()['{}'.format(k)] = dict_tmp[k]

That may result in issues as the input is external. Thank you.

djvg
  • 11,722
  • 5
  • 72
  • 103
  • Adapt code from [here](https://stackoverflow.com/questions/23294658/asking-the-user-for-input-until-they-give-a-valid-response) to keep asking the user for an equation and a list of variables until they enter "no" or something like that. Each iteration, store the equation and variables in a dictionary, then iterate through the dict to do the computations you need to. – MattDMo Jan 02 '22 at 22:45
  • Thanks for the tip, @MattDMo. However, I intend that the user only writes the equations. I want the program to interpret the equations and solve them; therefore, the code must create a list of variables based on the input. I already found some solutions with globals, locals and exec. Whilst they work, I prefer to use something less prone to security issues. Currently, I am exploring creating a specific dictionary (as locals()) for the function, which contains the variables guesses. Something "var.name = foo.var[0]". I am not sure if is possible. – João Soares Jan 03 '22 at 16:08
  • related: https://stackoverflow.com/q/3513292, https://stackoverflow.com/q/35804961, https://stackoverflow.com/q/2371436, and many more. – djvg Jan 04 '22 at 10:18

1 Answers1

0

I found a solution; thus, I am answering my question if anyone has the same problem.

First, other solutions exist for this problem; however, they imply risk issues if the user cannot control the input, e.g., eval(), exec(), locals() or globals(). The solution consists on:

1. Format the equation list

eq_list = ["x + y**2 = 4", "exp(x) + x*y = 3"]
new_eq_list = ['{x}+y**2-(4)', '{exp}({x})+{x}*y-(3)']

Like that, each variable is within brackets. I got this idea from: stackoverflow_question.

2. Create a dictionary where the variable is assigned to the guess:

def f(variables, new_eq_list, guess) :

        # dictionary with variables assigned to guess value
        var_values = {str(variables[i]): guess[i] for i in range(len(variables))}
        res = []
        for eqn in new_eq_list:
            eq_to_eval = eqn.format(**var_values) #replace eq var with guess value
            res.append(safe_eval(eq_to_eval))

Additionally, I removed the sympify, due to the warning about eval() use as I cannot control the input. That part is still messy to identify variables (symbols) using regex. Nothing against sympy, on the contrary; simply not adequate for this purpose.

Also, I found an alternative for the eval(). I explored AST functionalities, but in my opinion, it is not adequate for arithmetic operations. I found a good solution with ASTEVAL module. Still not sure how safe the module is, but the authors claim a safe alternative to eval(). I still need to read the module details. In the aforementioned code:

from asteval import Interpreter
safe_eval = Interpreter()

In any case, further suggestions or alternatives are welcome.