# Systems of equations solver with sympy

So this is my first Python project and hoped for a bit of feedback.

I'm trying to make a little program, where I can insert n equations and run a numerical solve. I ended up using tkinter for the GUI as it seemed very approachable, and sympy for the math part.

I'm unsure if the overall structure of the program, is good or bad.

And some functions caused me a bit of trouble. Especially the define_eqsys_vars I feel like became needlessly complicated.

import tkinter as tk
import sympy as sp
from tkinter import Text

def clean_input(x: str) -> list:
temp = x.replace(' ', '').split('\n')
cleaned = list(filter(None, temp))
return cleaned

def create_res(x: str):
split = x.split('=')
res_eq = (sp.parse_expr(split[0], evaluate=False) - sp.parse_expr(split[1], evaluate=False))
return res_eq

def define_eqsys_vars(eqsys: list):
unique_vars = set()
for eq in eqsys:
unique_vars = unique_vars.union(eq.atoms(sp.Symbol))

# create a list with all symbols converted to text, and join - var() takes a string
var_string = ', '.join([repr(eq) for eq in unique_vars])
variables = sp.var(var_string)
return variables

def create_eqsys(x: list) -> tuple:
equation_system = [create_res(eq) for eq in x]
variables = define_eqsys_vars(equation_system)
return equation_system, variables

def create_guess(eqsys: list) -> tuple:
unique_vars = set()
for eq in eqsys:
unique_vars = unique_vars.union(eq.atoms(sp.Symbol))
guess = [1] * len(unique_vars)
return tuple(guess)

def solve_eqsys(eqsys, symbols, guess):
result = sp.nsolve(tuple(eqsys), symbols, guess)
return result

def main():
# input, from tkinter window
text_input = inputtxt.get("1.0", "end-1c")

# clean text
cleaned_text = clean_input(text_input)

# create system of equations and try to solve it
eqsys, eqsys_vars = create_eqsys(cleaned_text)
guess = create_guess(eqsys)
solution = solve_eqsys(eqsys, eqsys_vars, guess)

# text output
Output.insert(tk.END, f"Solution: {solution}")

return

# Build GUI
root = tk.Tk()
toplabel = tk.Label(text="Start variable name with a letter")
inputtxt = Text(root, height=30, width=50, bg="light yellow")
Output = Text(root, height=30, width=25, bg="light cyan")
Display = tk.Button(root, height=2,
width=20,
text="Solve system of equations",
command=lambda: main())
toplabel.pack()
inputtxt.pack()
Display.pack()
Output.pack()
tk.mainloop()


You shouldn't list() your filter. You can just return the iterable.

Rather than forming a subtraction from your user's equation, why not just... form an equation, and use the default solve instead of nsolve?

An Equality object can give you its free_symbols directly, which will be simpler than your use of atoms.

Don't sp.var and don't form a string to give it - just use free_symbols directly.

You don't need to make a (fairly unsafe, since it's uninformed) guess if you just call solve.

Your application doesn't render very well for me - it fills the entire height of the screen. I haven't bothered to try fixing this.

Don't leave state or setup code in the global namespace. The quickest fix in your case is to make main a closure.

## Suggested

import tkinter as tk
from typing import Iterable, Sequence

import sympy as sp
from tkinter import Text

def clean_input(x: str) -> Iterable[str]:
temp = x.replace(' ', '').split('\n')
return filter(None, temp)

def create_res(x: str) -> sp.Equality:
lhs, rhs = x.split('=')
res_eq = sp.Eq(
sp.parse_expr(lhs, evaluate=False),
sp.parse_expr(rhs, evaluate=False)
)
return res_eq

def define_eqsys_vars(eqsys: Iterable[sp.Equality]) -> set[sp.Symbol]:
unique_vars = set()
for eq in eqsys:
unique_vars |= eq.free_symbols

return unique_vars

def create_eqsys(x: list) -> tuple[
list[sp.Equality],
set[sp.Symbol],
]:
equation_system = [create_res(eq) for eq in x]
variables = define_eqsys_vars(equation_system)
return equation_system, variables

def solve_eqsys(eqsys: Sequence[sp.Equality], symbols: Sequence[sp.Symbol]) -> dict:
return sp.solve(eqsys, symbols)

def setup() -> None:
def main() -> None:
# input, from tkinter window
text_input = inputtxt.get("1.0", "end-1c")

# clean text
cleaned_text = clean_input(text_input)

# create system of equations and try to solve it
eqsys, eqsys_vars = create_eqsys(cleaned_text)
solution = solve_eqsys(eqsys, eqsys_vars)

# text output
Output.insert(tk.END, f"Solution: {solution}")

# Build GUI
root = tk.Tk()
toplabel = tk.Label(text="Start variable name with a letter")
inputtxt = Text(root, height=30, width=50, bg="light yellow")
Output = Text(root, height=30, width=25, bg="light cyan")
Display = tk.Button(
root, height=2, width=20,
text="Solve system of equations",
command=main,
)
toplabel.pack()
inputtxt.pack()
Display.pack()
Output.pack()
tk.mainloop()

if __name__ == '__main__':
setup()