Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
Let's say I have an equation:
2x + 6 = 12
With algebra we can see that x = 3. How can I make a program in Python that can solve for x? I'm new to programming, and I looked at eval() and exec() but I can't figure out how to make them do what I want. I do not want to use external libraries (e.g. SAGE), I want to do this in just plain Python.
How about SymPy? Their solver looks like what you need. Have a look at their source code if you want to build the library yourself…
There are two ways to approach this problem: numerically and symbolically.
To solve it numerically, you have to first encode it as a "runnable" function - stick a value in, get a value out. For example,
def my_function(x):
return 2*x + 6
It is quite possible to parse a string to automatically create such a function; say you parse 2x + 6 into a list, [6, 2] (where the list index corresponds to the power of x - so 6*x^0 + 2*x^1). Then:
def makePoly(arr):
def fn(x):
return sum(c*x**p for p,c in enumerate(arr))
return fn
my_func = makePoly([6, 2])
my_func(3) # returns 12
You then need another function which repeatedly plugs an x-value into your function, looks at the difference between the result and what it wants to find, and tweaks its x-value to (hopefully) minimize the difference.
def dx(fn, x, delta=0.001):
return (fn(x+delta) - fn(x))/delta
def solve(fn, value, x=0.5, maxtries=1000, maxerr=0.00001):
for tries in xrange(maxtries):
err = fn(x) - value
if abs(err) < maxerr:
return x
slope = dx(fn, x)
x -= err/slope
raise ValueError('no solution found')
There are lots of potential problems here - finding a good starting x-value, assuming that the function actually has a solution (ie there are no real-valued answers to x^2 + 2 = 0), hitting the limits of computational accuracy, etc. But in this case, the error minimization function is suitable and we get a good result:
solve(my_func, 16) # returns (x =) 5.000000000000496
Note that this solution is not absolutely, exactly correct. If you need it to be perfect, or if you want to try solving families of equations analytically, you have to turn to a more complicated beast: a symbolic solver.
A symbolic solver, like Mathematica or Maple, is an expert system with a lot of built-in rules ("knowledge") about algebra, calculus, etc; it "knows" that the derivative of sin is cos, that the derivative of kx^p is kpx^(p-1), and so on. When you give it an equation, it tries to find a path, a set of rule-applications, from where it is (the equation) to where you want to be (the simplest possible form of the equation, which is hopefully the solution).
Your example equation is quite simple; a symbolic solution might look like:
=> LHS([6, 2]) RHS([16])
# rule: pull all coefficients into LHS
LHS, RHS = [lh-rh for lh,rh in izip_longest(LHS, RHS, 0)], [0]
=> LHS([-10,2]) RHS([0])
# rule: solve first-degree poly
if RHS==[0] and len(LHS)==2:
LHS, RHS = [0,1], [-LHS[0]/LHS[1]]
=> LHS([0,1]) RHS([5])
and there is your solution: x = 5.
I hope this gives the flavor of the idea; the details of implementation (finding a good, complete set of rules and deciding when each rule should be applied) can easily consume many man-years of effort.
Python may be good, but it isn't God...
There are a few different ways to solve equations. SymPy has already been mentioned, if you're looking for analytic solutions.
If you're happy to just have a numerical solution, Numpy has a few routines that can help. If you're just interested in solutions to polynomials, numpy.roots will work. Specifically for the case you mentioned:
>>> import numpy
>>> numpy.roots([2,-6])
array([3.0])
For more complicated expressions, have a look at scipy.fsolve.
Either way, you can't escape using a library.
If you only want to solve the extremely limited set of equations mx + c = y for positive integer m, c, y, then this will do:
import re
def solve_linear_equation ( equ ):
"""
Given an input string of the format "3x+2=6", solves for x.
The format must be as shown - no whitespace, no decimal numbers,
no negative numbers.
"""
match = re.match(r"(\d+)x\+(\d+)=(\d+)", equ)
m, c, y = match.groups()
m, c, y = float(m), float(c), float(y) # Convert from strings to numbers
x = (y-c)/m
print ("x = %f" % x)
Some tests:
>>> solve_linear_equation("2x+4=12")
x = 4.000000
>>> solve_linear_equation("123x+456=789")
x = 2.707317
>>>
If you want to recognise and solve arbitrary equations, like sin(x) + e^(i*pi*x) = 1, then you will need to implement some kind of symbolic maths engine, similar to maxima, Mathematica, MATLAB's solve() or Symbolic Toolbox, etc. As a novice, this is beyond your ken.
Use a different tool. Something like Wolfram Alpha, Maple, R, Octave, Matlab or any other algebra software package.
As a beginner you should probably not attempt to solve such a non-trivial problem.
I am trying to solve simple algebraic parametrized equation sqrt(x)=sqrt(a)+1 with SymPy command solveset:
x, a = symbols("x a")
solveset(Eq(sqrt(x), sqrt(a)+1), x)
It returns as part of answer (conditional set) two solutions: 2*sqrt(a)+a+1 and -2*sqrt(a)+a+1. I can't understand how we can obtain the second one with minus sign from our equation!
Command solve in the same time gives us only solution (sqrt(a) + 1)**2, which is correct in my opinion.
What happens with solveset, any ideas?
The issue is to do with the sets you are solving over. Specifically, solveset defaults to a complex domain, resulting in extraneous solutions due to the complex square root. But by explicitly specifying a real domain and defining a and x to both be positive (as we're just dealing with reals, so can't have a or x negative), the desired solution can be extracted:
x, a = symbols('x a', positive=True)
solveset(Eq(sqrt(x), sqrt(a) + 1), x, domain=S.Reals).as_relational(x).expand()
# x = 2⋅√a + a + 1
The method that solveset uses gives two roots to consider but checksol is not able to show that the 2nd solution is not generally valid so to be safe, it returns both and shows you the condition which must be checked. If you substitute a value in for a you will obtain a set with the single concrete answer.
I am attempting to solve for a variable (T_p) in terms of another (T_i) using sympy. However all that is returned after running is empty square brackets i.e [].
My current code is as follows:
from sympy import *
T_i, T_p = symbols('T_i T_p')
eq = Eq(1.32*(T_i - T_p)**1.25 + 58.5*T_i, 3774)
sol = (solve(eq, T_p))
print(sol)
I am unsure what is wrong as it has worked for other equations. Any help would be appreciated.
Could it be that the solve function doesn't like fractional powers? If so, is there a similar function that does?
In Mathematica, you can use the function FullSimplify[expression,assumptions] to simplify expressions using assumptions. For instance, if I do this:
FullSimplify[x^2-y^2,x^2-y^2==1],
then the result will be 1 because that's the 'simplest expression that is equivalent to the function I gave the software.
Now I need to do the same with Python, but I don't know how to do that. I have seen the documentation about the command sympy.refine on this page:
https://docs.sympy.org/latest/modules/assumptions/refine.html
However, I haven't been able to use equalities as assumptions. It doesn't seem possible to assume things like that with the Q function.
I have tried to do something like this:
import sympy as sp
x,y=sp.symbols('x y')
sp.refine(x**2-y**2,x==y)
However, this gives me the following error: ValueError: Inconsistent assumptions
Does someone have any ideas about this? Thank you.
Without some scope for what kind of expressions and assumptions you want to work with this is probably an unsolvable problem so I'll make some assumptions.
If you want to simplify a polynomial expression based on some other polynomial expression(s) being zero then you can do this in sympy using ratsimpmodprime:
In [1]: x, y = symbols('x, y')
In [2]: assumptions = [x**2 + y**2 - 1]
In [3]: expression = x**2 + y**2
In [4]: ratsimpmodprime(expression, assumptions)
Out[4]: 1
https://docs.sympy.org/latest/modules/simplify/simplify.html#ratsimpmodprime
I tried two approaches to the problem.
Assume that x - y = 0. This was the only way I could find to express the assumption that you want to make. Unfortunately, it doesn't seem to be smart enough to notice that this assumption allows it to substitute x for y.
print("refine:", sp.refine(x**2-y**2,Q.zero(x-y)))
This just returns the original expression.
Ask sympy to substitute the expression. This isn't as automatic - you're asking it to do the substitution instead of giving it the option of doing the substitution, but it does work for this toy example.
expr = (x**2-y**2)
print("substitution:", expr.subs(x**2-y**2, 1))
prints 1.
Last Day I have been bothering with this problem. At first I made a question but after comment of needing a simplified example, I deleted the question and I finally found the "source" of the problem.
I fail to evaluate expressions AFTER substituting Functions by expressions: Following example will show you what I mean:
xx = sy.Symbol('x',real=True)
yy = sy.Symbol('y',real=True)
FuncT = sy.Function('F')
TestExp= sy.cos(sy.diff(FuncT(xx,yy),xx)+xx+yy*xx+yy)
print(TestExp.subs({FuncT(xx,yy):xx})).subs({xx:1,yy:0.1})
which results
How can it replace dx/dx = 1 ?
Just doit:
>>> TestExp.subs({FuncT(xx,yy):xx}).subs({xx:1,yy:0.1}).doit()
-0.588501117255346
How to know to use doit?
When I print (not pprint) the expressions I see
cos(Subs(Derivative(x, x), x, 1) + 1.2)
I don't want Subs there but I don't know much about Subs so I
ask for help and read the following:
>>> help(Subs)
...
There's no automatic expansion - use the method .doit() to effect all
possible substitutions of the object and also of objects inside the
expression.
...