1
\$\begingroup\$

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()
\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

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()
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.