Dropping lower order terms of a polynomial with Sympy - python

Say we have this function,
f = poly(2*x**2 + 3*x - 1,x)
How would one go about dropping terms of degree n or lower.
For instance if n = 1 the result would be 2*x**2.

from sympy import poly
from sympy.abc import x
p = poly(x ** 5 + 2 * x ** 4 - x ** 3 - 2 * x ** 2 + x)
print(p)
n = 2
new_p = poly(sum(c * x ** i[0] for i, c in p.terms() if i[0] > n))
print(new_p)
Output:
Poly(x**5 + 2*x**4 - x**3 - 2*x**2 + x, x, domain='ZZ')
Poly(x**5 + 2*x**4 - x**3, x, domain='ZZ')

Related

Why does SymPy yield a "worse" solution than WolframAlpha?

I needed to solve $sin(x)+a*sin(3x)=0$ symbolically for $x$.
import sympy as sp
a,x = sp.symbols('a,x')
roots = sp.solve([sp.sin(x)+a*sp.sin(3*x)],[x])
print(roots)
produced
(I*(-log((-a - sqrt(-3*a**2 + 2*a + 1) - 1)/a) + log(2))/2,),
(I*(-log(-sqrt((-a + sqrt(-3*a**2 + 2*a + 1) - 1)/a)) + log(2)/2),),
(I*(-log((-a + sqrt(-3*a**2 + 2*a + 1) - 1)/a) + log(2))/2,)]
whereas WolframAlpha produced a much "better" solution:
solve sin(x) + a sin(3 x) = 0
yields
x = π n and n element Z
x = 2 π n and a = -1/3 and n element Z
x = 2 π n - 2 tan^(-1)(sqrt((5 a - 4 sqrt((a - 1) a) - 1)/(3 a + 1))) and 3 a + 1!=0 and sqrt((a - 1) a)!=2 a and n element Z
x = 2 (tan^(-1)(sqrt((5 a - 4 sqrt((a - 1) a) - 1)/(3 a + 1))) + π n) and 3 a + 1!=0 and sqrt((a - 1) a)!=2 a and n element Z
x = 2 π n - 2 tan^(-1)(sqrt((5 a + 4 sqrt((a - 1) a) - 1)/(3 a + 1))) and 3 a + 1!=0 and 2 a + sqrt((a - 1) a)!=0 and n element Z
Question:
can the SymPy solver be configured to produce solutions in the way WolframAlpha does?
Is anything known why SymPy expresses the solutions via (complex) logarithms instead of arc tangents?

python algorithm for solving systems of equations without

I need algorithm, that solve systems like this:
Example 1:
5x - 6y = 0 <--- line
(10- x)**2 + (10- y)**2 = 2 <--- circle
Solution:
find y:
(10- 6/5*y)**2 + (10- y)**2 = 2
100 - 24y + 1.44y**2 + 100 - 20y + y**2 = 2
2.44y**2 - 44y + 198 = 0
D = b**2 - 4ac
D = 44*44 - 4*2.44*198 = 3.52
y[1,2] = (-b+-sqrt(D))/2a
y[1,2] = (44+-1.8761)/4.88 = 9.4008 , 8.6319
find x:
(10- x)**2 + (10- 5/6y)**2 = 2
100 - 20x + y**2 + 100 - 5/6*20y + (5/6*y)**2 = 2
1.6944x**2 - 36.6666x + 198 = 0
D = b**2 - 4ac
D = 36.6666*36.6666 - 4*1.6944*198 = 2.4747
x[1,2] = (-b+-sqrt(D))/2a
x[1,2] = (36.6666+-1.5731)/3.3888 = 11.2841 , 10.3557
my skills are not enough to write this algorithm please help
and another algorithm that solve this system.
5x - 6y = 0 <--- line
|-10 - x| + |-10 - y| = 2 <--- rhomb
as answer here i need two x and two y.
You can use sympy, Python's symbolic math library.
Solutions for fixed parameters
from sympy import symbols, Eq, solve
x, y = symbols('x y', real=True)
eq1 = Eq(5 * x - 6 * y, 0)
eq2 = Eq((10 - x) ** 2 + (10 - y) ** 2, 2)
solutions = solve([eq1, eq2], (x, y))
print(solutions)
for x, y in solutions:
print(f'{x.evalf()}, {y.evalf()}')
This leads to two solutions:
[(660/61 - 6*sqrt(22)/61, 550/61 - 5*sqrt(22)/61),
(6*sqrt(22)/61 + 660/61, 5*sqrt(22)/61 + 550/61)]
10.3583197613288, 8.63193313444070
11.2810245009662, 9.40085375080520
The other equations work very similar:
eq1 = Eq(5 * x - 6 * y, 0)
eq2 = Eq(Abs(-10 - x) + Abs(-10 - y), 2)
leading to :
[(-12, -10),
(-108/11, -90/11)]
-12.0000000000000, -10.0000000000000
-9.81818181818182, -8.18181818181818
Dealing with arbitrary parameters
For your new question, how to deal with arbitrary parameters, sympy can help to find formulas, at least when the structure of the equations is fixed:
from sympy import symbols, Eq, Abs, solve
x, y = symbols('x y', real=True)
a, b, xc, yc = symbols('a b xc yc', real=True)
r = symbols('r', real=True, positive=True)
eq1 = Eq(a * x - b * y, 0)
eq2 = Eq((xc - x) ** 2 + (yc - y) ** 2, r ** 2)
solutions = solve([eq1, eq2], (x, y))
Studying the generated solutions, some complicated expressions are repeated. Those could be substituted by auxiliary variables. Note that this step isn't necessary, but helps a lot in making sense of the solutions. Also note that substitution in sympy often only considers quite literal replacements. That's by the introduction of c below is done in two steps:
c, d = symbols('c d', real=True)
for xi, yi in solutions:
print(xi.subs(a ** 2 + b ** 2, c)
.subs(r ** 2 * a ** 2 + r ** 2 * b ** 2, c * r ** 2)
.subs(-a ** 2 * xc ** 2 + 2 * a * b * xc * yc - b ** 2 * yc ** 2 + c * r ** 2, d)
.simplify())
print(yi.subs(a ** 2 + b ** 2, c)
.subs(r ** 2 * a ** 2 + r ** 2 * b ** 2, c * r ** 2)
.subs(-a ** 2 * xc ** 2 + 2 * a * b * xc * yc - b ** 2 * yc ** 2 + c * r ** 2, d)
.simplify())
Which gives the formulas:
x1 = b*(a*yc + b*xc - sqrt(d))/c
y1 = a*(a*yc + b*xc - sqrt(d))/c
x2 = b*(a*yc + b*xc + sqrt(d))/c
y2 = a*(a*yc + b*xc + sqrt(d))/c
These formulas then can be converted to regular Python code without the need of sympy. That code will only work for an arbitrary line and circle. Some tests need to be added around, such as c == 0 (meaning the line is just a dot), and d either be zero, positive or negative.
The stand-alone code could look like:
import math
def give_solutions(a, b, xc, yc, r):
# intersection between a line a*x-b*y==0 and a circle with center (xc, yc) and radius r
c =a ** 2 + b ** 2
if c == 0:
print("degenerate line equation given")
else:
d = -a**2 * xc**2 + 2*a*b * xc*yc - b**2 * yc**2 + c * r**2
if d < 0:
print("no solutions")
elif d == 0:
print("1 solution:")
print(f" x1 = {b*(a*yc + b*xc)/c}")
print(f" y1 = {a*(a*yc + b*xc)/c}")
else: # d > 0
print("2 solutions:")
sqrt_d = math.sqrt(d)
print(f" x1 = {b*(a*yc + b*xc - sqrt_d)/c}")
print(f" y1 = {a*(a*yc + b*xc - sqrt_d)/c}")
print(f" x2 = {b*(a*yc + b*xc + sqrt_d)/c}")
print(f" y2 = {a*(a*yc + b*xc + sqrt_d)/c}")
For the rhombus, sympy doesn't seem to be able to work well with abs in the equations. However, you could use equations for the 4 sides, and test whether the obtained intersections are inside the range of the rhombus. (The four sides would be obtained by replacing abs with either + or -, giving four combinations.)
Working this out further, is far beyond the reach of a typical stackoverflow answer, especially as you seem to ask for an even more general solution.

function does't return a list as indented, but returns only one listelement instead

For the sake of practice, I am trying to calculate the following mathematical expression:
The x-value in the parameter is supposed to be a number - an int or float
The y-value in the parameter is supposed to be a list of numbers.
The function is supposed to return a list of values in the same length as the list y in the parameter.
For some embarrassing reason, I only manage to make the function return only one list element, that comes from the first of the two equations in the function.
The code in question is:
def f(x ,y):
list = []
for i in y:
if y[i] <= 0:
list.append(4 * (x ** 3) * y[i] - 2 * x * y[i])
if y[i] > 0:
list.append(4 * (x ** 3) * y[i] + 2 * x * y[i])
return list
x_value = 2
y_values = [1,-2, 3, -7]
print(f(x_value, y_values))
#wanted output: [28, -56, 85, -252]
#actual output: [-56]
My question is:
How do you make this function return a list with all the calculations? Like for example
[28, -56, 85, -252] instead of the current output which is [-56]
This is probably an easy fix, but for some reason I am stuck.
Is there anybody kind enough to help me sort this out?
First, when you have for i in y, i is the actual value in y, not the index, so you don't use y[i], you use i directly. Second, you should return the final list after the for i in y loop is over, so it needs to be outside the loop. And finally, don't use built-in names (such as list) as your own variable/function/class names, because it will overwrite the built-in names.
def f(x ,y):
L = []
for i in y:
if i <= 0:
L.append(4 * (x ** 3) * i - 2 * x * i)
if i > 0:
L.append(4 * (x ** 3) * i + 2 * x * i)
return L
To use i as an index, you need to change the loop to for i in range(len(y)), and use y[i] as you did previously.
Also, if you use numpy, you can solve this as:
>>> import numpy as np
>>> x_value = 2
>>> y_values = np.array([1,-2, 3, -7])
>>> 4 * x_value**3 * y_values + 2 * x_value * np.where(y_values <= 0, -y_values, y_values)
array([ 36, -56, 108, -196])
You can do it briefly with a list comprehension:
def f(x,y):
return [4 * (x ** 3) * yy - 2 * x * yy if yy <= 0
else 4 * (x ** 3) * yy + 2 * x * yy for yy in y]
OR
def f(x, y)
return [4 * (x ** 3) * yy + (int(yy > 0) - 0.5) * 4 * x * yy for yy in y]
To articulate the last example given that is could be a little bit cryptic: in both formulas, the two elements are always the same:
4 * (x ** 3) * yy
2 * x * yy
in the one case you sum them, in the other case you take the difference:
Hence we could write f(x, y) as:
def f(x, y):
lst = []
for yy in y:
a, b = 4 * (x ** 3) * yy, 2 * x * yy
lst.append(a + (1 if yy > 0 else -1) * b
return lst
While there is a case to be esplicit with proper if close, there is also the case for not rewriting the same code/formulas.
Then can go even more compact knowing that bool(yy > 0) is 1 if yy > 0 and 0 otherwise, so 2 * (bool(yy > 0) - 0.5) is 1 and -1 respectively:

How to automate simplifying an expression by given sub-expressions in SymPy

I want to shorten a long equation with shorter expressions.
Here is a simple example:
from sympy.abc import z, h, m, G
y = 0.2 * m + 0.2 * z * m +z - h
y_1 = y.subs({m + z * m: G})
print(y_1)
Expected result is z - h + 0.2 * G but it doesn't replace the expression. I know the problem is 0.2. Is there any way to fix this automatically?
OR another solution can be :
Common subexpression elimination (cse(y)), which is not efficient as it works with default conditions to create sub-expressions.
Unfortunately, as far as I know, subs just isn't as powerful (yet?). You can try to automate splitting your substitution m + z * m: G into two separate substitutions z: G / m - 1 and m: G / (z + 1) and simplify in between.
from sympy.abc import z, h, m, G
y = 0.2 * m + z - h + 0.2 * z * m
y_1 = y.subs({m + z * m: G})
print(y_1)
y_2 = y.subs({z: G / m - 1, m: G / (z + 1)}).simplify()
print(y_2)
y_3 = y_2.subs({G / m - 1: z, G / (z + 1): m}).simplify()
print(y_3)
Which has the following output:
-h + 0.2*m*z + 0.2*m + z
0.2*G + G/m - h - 1
0.2*G - h + z
Update: When I said that you can try to automate the process, I meant something like the following code example. I do not know how much this is applicable to your situation.
from sympy import *
from sympy.abc import z, h, m, G
def elaborate_subs(expr, old, new):
subs = {}
for symbol in old.free_symbols:
solution = solve(Eq(old, new), symbol, simplify=False)
if solution:
subs[symbol] = solution[0]
expr = expr.subs(subs).simplify()
expr = expr.subs({v: k for k, v in subs.items()}).simplify()
return expr
y = 0.2 * m + z - h + 0.2 * z * m
print(elaborate_subs(y, m + z * m, G))
Which has the expected output:
0.2*G - h + z
I don't think the subs method works the way you think it does. It simply replaces a term with an input value that is passed. For example,
expr = cos(x)
expr.subs(x, 0)#here 0 is replaced with x
print(expr) # 1, sinces cos(0) is 1
#Or
expr = x**3 + 4*x*y - z
expr.subs([(x, 2), (y, 4), (z, 0)])
print(expr) #40
As you can see, in your subs method you are not telling exactly what should be replaced. Follow the examples above as a guideline and it should work. You can read more here

Finding the minimum of a function on a closed interval with Python

Updated: How do I find the minimum of a function on a closed interval [0,3.5] in Python? So far I found the max and min but am unsure how to filter out the minimum from here.
import sympy as sp
x = sp.symbols('x')
f = (x**3 / 3) - (2 * x**2) + (3 * x) + 1
fprime = f.diff(x)
all_solutions = [(xx, f.subs(x, xx)) for xx in sp.solve(fprime, x)]
print (all_solutions)
Since this PR you should be able to do the following:
from sympy.calculus.util import *
f = (x**3 / 3) - (2 * x**2) - 3 * x + 1
ivl = Interval(0,3)
print(minimum(f, x, ivl))
print(maximum(f, x, ivl))
print(stationary_points(f, x, ivl))
Perhaps something like this
from sympy import solveset, symbols, Interval, Min
x = symbols('x')
lower_bound = 0
upper_bound = 3.5
function = (x**3/3) - (2*x**2) - 3*x + 1
zeros = solveset(function, x, domain=Interval(lower_bound, upper_bound))
assert zeros.is_FiniteSet # If there are infinite solutions the next line will hang.
ans = Min(function.subs(x, lower_bound), function.subs(x, upper_bound), *[function.subs(x, i) for i in zeros])
Here's a possible solution using sympy:
import sympy as sp
x = sp.Symbol('x', real=True)
f = (x**3 / 3) - (2 * x**2) - 3 * x + 1
#f = 3 * x**4 - 4 * x**3 - 12 * x**2 + 3
fprime = f.diff(x)
all_solutions = [(xx, f.subs(x, xx)) for xx in sp.solve(fprime, x)]
interval = [0, 3.5]
interval_solutions = filter(
lambda x: x[0] >= interval[0] and x[0] <= interval[1], all_solutions)
print(all_solutions)
print(interval_solutions)
all_solutions is giving you all points where the first derivative is zero, interval_solutions is constraining those solutions to a closed interval. This should give you some good clues to find minimums and maximums :-)
The f.subs commands show two ways of displaying the value of the given function at x=3.5, the first as a rational approximation, the second as the exact fraction.

Categories

Resources