Back in my early computer science studies, I had jot down notes on solving math problems. Most of them were scratch work, scattered examples of equations, and the occasional attempt to generalize a pattern. Those notes were never meant for anyone else, but looking back now, they captured the foundation of how I learned to connect mathematics with code.

Today, I’d like to revisit that memory lane and compare how those early approaches hold up against the current landscape of technology. SymPy is an excellent tool for symbolic math, we now also have the ability to bring in large language models (LLMs) like OpenAI’s GPT or Gemini to extend what’s possible: not just solving equations, but explaining them, generating new approaches, or even translating word problems into executable Python code.

Let’s experiment with the same ideas in the current timeline. The examples in this article are a refined version of those original notes. The focus remains on **math equation solver **. It is a journey of taking problems I once solved on paper, implementing them with SymPy, and then pushing further to see how modern technologies, especially large language models, change the way we approach mathematical problem solving.

Algebraic equations with SymPy

If I remember right, one of the first things I experimented with was solving simple algebraic equations. SymPy makes this almost effortless. The solve and solveset functions provide different ways to approach the same problem. solve works well for quick results and returns raw solutions, while solveset is more rigorous about domains and can express results as sets or intervals.

Let’s start with a quadratic equation:

from sympy import symbols, solve, solveset, Eq, S

x = symbols('x')

## Equation: x^2 - 5x + 6 = 0
equation = Eq(x**2 - 5*x + 6, 0)
roots_solve = solve(equation, x)
roots_solveset = solveset(equation, x, domain=S.Complexes)

print("solve:", roots_solve)
print("solveset:", roots_solveset)

Output:

solve: [2, 3]  
solveset: {2, 3}

The two functions agree in this case. Both return the exact roots of the quadratic. Fascinating! how SymPy handles symbolic math directly without needing numerical approximations.

Interestingly, If you change the equation to something less tidy, like x^2 - 2 = 0 SymPy returns exact forms involving square roots instead of floating-point numbers. This makes the results reliable for symbolic manipulations or further algebraic steps.

equation2 = Eq(x**2 - 2, 0)
print(solve(equation2, x))

Output:

[-sqrt(2), sqrt(2)]

Here, SymPy keeps the roots in symbolic form rather than approximating them as 1.414… This exactness is a recurring strength that sets SymPy apart from purely numeric solvers.

Systems of equations with SymPy

After experimenting with single equations, my notes moved on to systems of equations. Let’s attempt to solve multiple equations together with SymPy. Instead of trying to do substitution or elimination manually, I’ve described the system in code and let the solver handle the algebra.

Here is a simple linear system:

from sympy import symbols, Eq, solve

x, y = symbols('x y')
eq1 = Eq(x + y, 2)
eq2 = Eq(x - y, 1)

solution = solve((eq1, eq2), (x, y))
print(solution)

Output:

{x: 3/2, y: 1/2}

Great! it worked. SymPy can handle both linear and nonlinear systems with the same interface.

For example, consider a nonlinear system:

eq3 = Eq(x**2 + y, 2)
eq4 = Eq(y**2 + x, 2)

solution_nonlinear = solve((eq3, eq4), (x, y))
print(solution_nonlinear)

Output:

[(-2, -2), (1, 1), (2 - (1/2 - sqrt(5)/2)**2, 1/2 - sqrt(5)/2), (2 - (1/2 + sqrt(5)/2)**2, 1/2 + sqrt(5)/2)]

In this case, SymPy returns multiple valid solutions, something that would have been easy to miss if I were working these out by hand. For larger systems, the solver can produce exact symbolic expressions that reveal relationships between variables, although performance may degrade as complexity increases.

Inequalities and piecewise results

Once I had experimented with single equations and systems, I progressed to inequalities. Solving inequalities introduces a new dimension: instead of discrete solutions, we now deal with ranges or intervals of values. This was one of the first times I realized that symbolic computation could handle abstract concepts like “all x satisfying this condition” directly.

For a simple quadratic inequality: x^2 - 4 < 0

Let’s solve it in SymPy using solve_univariate_inequality:

from sympy import symbols
from sympy.solvers.inequalities import solve_univariate_inequality
from sympy import Lt

x = symbols('x')

## Define inequality x^2 - 4 < 0
inequality = Lt(x**2 - 4, 0)

solution = solve_univariate_inequality(inequality, x)
print(solution)

Output:

(-2, 2)

This result tells us that the inequality holds for all values of x between -2 and 2. SymPy abstracts the process that involves multiple steps of factoring, testing intervals, and considering signs.

For more complex cases, such as systems of inequalities or piecewise conditions, SymPy provides reduce_rational_inequalities which can combine multiple constraints into a single solution set. For example:

from sympy.solvers.inequalities import reduce_rational_inequalities
from sympy import Ge

inequalities = [[Ge((x - 1)*(x + 2), 0), Lt(x - 3, 0)]]
solution_complex = reduce_rational_inequalities(inequalities, x)
print(solution_complex)

Output:

(-∞, -2] ∪ [1, 3)

Back then, I remember this kind of symbolic interval arithmetic had been extremely tedious to do by hand. Revisiting it today, it is satisfying to see that modern tools make experimenting with inequalities seamless.

Polynomial roots and numeric approximation

While quadratics and simple cubics are often solvable symbolically, higher-degree polynomials introduce a mix of exact and approximate solutions. It is interesting to see how symbolic computation remains powerful, yet numeric approximation is often necessary.

Let’s consider a fifth-degree polynomial: x^5 - x + 1 = 0

SymPy allows us to extract roots both symbolically and numerically. Using roots(), we can attempt an exact symbolic solution:

from sympy import symbols, roots

x = symbols('x')
polynomial = x**5 - x + 1

symbolic_roots = roots(polynomial, x)
print(symbolic_roots)

Output:

{}

Here, SymPy shows that there are no simple algebraic expressions for the roots, one of the first lessons in my early notes: not all polynomials are solvable symbolically, and understanding when to switch to numeric methods.

Numeric approximation is straightforward with nroots():

numeric_roots = polynomial.nroots()
print(numeric_roots)

Output:

[-1.1673, 0.8774, 0.1529 + 0.9888*I, 0.1529 - 0.9888*I, -0.0159 + 0.9988*I]

Now we obtain approximate real and complex roots. Revisiting this, it is striking how much time and effort symbolic and numeric solvers save. Back in the days, I would have had to rely on iterative methods manually, often without insight into complex solutions. SymPy provides a clean bridge between symbolic exactness and numeric practicality.

This is also where the journey of revisiting my old notes begins to intersect with modern tools. For example, one could feed these numeric roots into a large language model to generate human-readable explanations or even to suggest related polynomial identities. The combination of symbolic, numeric, and AI-augmented reasoning is something I could only imagine back when I first jotted down these polynomials.

Differential Equations (ODEs)

Solving ODEs by hand was also tedious, especially when dealing with initial conditions or more complex forms. Revisiting these exercises with SymPy today highlights how symbolic computation can handle general solutions and specific cases seamlessly.

Consider a simple second-order ODE: y’’ + y = 0

Using SymPy, we can solve it symbolically:

from sympy import symbols, Function, dsolve, Eq, Derivative

x = symbols('x')
y = Function('y')

## Define the differential equation y'' + y = 0
ode = Eq(Derivative(y(x), x, x) + y(x), 0)
solution = dsolve(ode, y(x))
print(solution)

Output:

Eq(y(x), C1*cos(x) + C2*sin(x))

This returns the general solution with arbitrary constants C1 and C2. We can also include initial conditions to find a particular solution:

## Initial conditions: y(0) = 2, y'(0) = 0
solution_ic = dsolve(ode, y(x), ics={y(0): 2, y(x).diff(x).subs(x, 0): 0})
print(solution_ic)

Output:

Eq(y(x), 2*cos(x))

This shows that SymPy handles both the general structure and concrete cases elegantly. It is clear that the combination of symbolic reasoning and computational automation transforms what once required careful manual integration into a quick, reliable process. I can also imagine that when ODEs become nonlinear, coupled, or involve complicated boundary conditions, SymPy may struggle. This is where modern large language models may complement the workflow, suggesting solution strategies, checking derivations, or even generating Python code to handle special numeric cases.

Thoughts on using LLMs where SymPy struggles

SymPy handles many problems efficiently, but there are some natural limits. Nonlinear systems with multiple variables, partial differential equations, or word problems that require interpretation can be difficult or impossible to solve symbolically. In the past, these cases often required manual sketches, numeric approximations, or trial-and-error. Nowadays, Large language models can be used as a complementary tool. From the top of my head, with this new tool we should be able to:

Check derivations and edge cases LLMs should be able to somewhat reason about solution validity, domain assumptions, or special conditions that SymPy might not highlight. For instance, feeding a nonlinear system with multiple solutions into an LLM can indicate which solutions are real, physically meaningful, or simplified forms for easier analysis. Explain symbolic solutions They should be able to generate human-readable, step-by-step explanations. For example, given the roots of a polynomial, an LLM can clarify why some roots are real or complex and explain the role of the discriminant in a quadratic.

Translate natural language into code They should also do conversion of word problems directly into SymPy-compatible equations. Example: “Find x if x squared minus five x plus six equals zero” -> Eq(x**2 - 5*x + 6, 0)

Suggest numeric approximations or alternative methods When symbolic solutions are unavailable, LLMs should be able to propose numeric solvers or even generate Python code for approximation (not sure if they would work on the first attempt without tweaking).

Conclusion

Reflecting on the early notes highlights both continuity and change. Symbolic computation still provides exact, reliable solutions for equations, systems, and differential equations. Large language models add a layer of interpretation, explanation, and guidance that was not available back then. There are also tools like Wolfram Alpha with advanced algorithms and knowledge base which can also be beneficial and aid in solving problems.

LLMs are useful tools, but they are nowhere near perfect. They will make mistakes, oversimplify, or hallucinate, and should be used alongside rigorous computation and critical judgment.

Combining SymPy with AI creates a hybrid workflow: the rigor of symbolic math complemented by intelligent reasoning. It shows that early lessons in computation remain relevant, even as new tools expand what is possible.