Sympy not finding a solution to equation, would numpy work? - python

I'm trying to build a system for simulating sliding done arbitrary slopes, but it requires that I find the intersection of half a circle and an arbitrary function. However, when I use sympy, it does find a solution despite one clearly existing.
Is there a numerical approach I could take to this in NumPy?
See code below, where x, y, and r and all constants:
z = symbols('z')
print(solve(z * 1, ((r**2 - (z - x)**2) ** 0.5) + y))
p1 = plot(z, show=False)
p2 = plot(((r**2 - (z - x)**2) ** 0.5) + y, show=False)
p1.append(p2[0])
p1.show()
This results in:
[]
Despite a solution clearly existing:
Note that in the case used for testing, x=0.4, y=0.4, r=1

You are passing two arguments to solve but it should be a single argument as an equation (Eq):
In [58]: eq = Eq(z * 1, ((r**2 - (z - x)**2) ** 0.5) + y)
In [59]: eq
Out[59]:
0.5
⎛ 2⎞
z = ⎝1 - (z - 0.4) ⎠ + 0.4
In [60]: solve(eq)
Out[60]: [1.10710678118655]
A second argument, if provided, should be the symbol to solve for or a list of symbols to solve for e.g.:
In [61]: solve(eq, z)
Out[61]: [1.10710678118655]

Related

Find min and max angle of an ellipse in Python

I have an equation, describing an ellipse. I want to use Python to find the minimum and maxium angle theta and length of the axis Lc of these ellipse for further calculations.
The parameters Lc1, theta1, etc. are the axis length for the respective angle.
I tried sympy.solve without success (the result didn´t make any sense) and are now trying sympy.solveset.
This is my current script. That is followed by further calculation which needs theta.
import math
import sympy as sym
from sympy import solve, Eq
from sympy import *
import matplotlib.pyplot as plt
#define parameters
#Lc modelled with matlab before
Lc1 = 0.67 / 1000
Lc2 = 0.36/ 1000
Lc3 = 0.7 / 1000
#orientation of the cut section
theta1 = 89
theta2 = -35
theta3 = 25
#calculation from Beaudoin et al. (2016) and Ebner et al. (2010)
dL = ((Lc2 - Lc1)/(Lc3 - Lc1))
c = math.atan(- (((dL * (math.sin(2 * theta3) - math.sin(2 * theta1))) - (math.sin(2 * theta2) - math.sin(2* theta1)))/ ((dL * (math.cos(2 * theta3) - math.cos(2 * theta1))) - (math.cos(2 * theta2) - math.cos(2 * theta1)))))
b = ((Lc2 - Lc1)/((math.sin(2 * theta2 + c)) - (math.sin(2 * theta1 + c))))
a = Lc1 - (b * math.sin(2 * theta1 + c))
print('Done calculating a, b and c')
##try to find theta at min and max Lc
#theta = sym.symbols('theta')
theta = var('theta')
#define eqation that give us our crossover length
Lc = Eq(a + b * sym.sin(2 * theta +c))
dLc = Eq(2 * b * sym.cos(2 * theta +c) + sym.sin(2 * theta + c))
print('Busy finding minimum and maximum.')
sol = solveset(dLc, theta)
sol
#now we have the derivation we can go find min and max of Lc
Using solveset I recieved no result so far, because python doesn´t stop running.
I am not sure, if I can get a reliable result with my current script or not. What is wrong? Is there a more efficient way? I´d be glad if anyone could help me!
Thx in advance!
Since this equation has no symbolic parameters and has floating point coefficients I guess that you just want a numeric solution so you can use nsolve:
In [4]: nsolve(dLc, theta, 1.2)
Out[4]: 1.22165851958244
The initial guess (1.2) comes from looking at a plot of the function (plot(dLc.lhs)).
Note that your equation looks like this:
In [26]: dLc
Out[26]: sin(2⋅θ + 0.697151824925716) + 0.0011237899722686⋅cos(2⋅θ + 0.697151824925716) = 0
We can solve this in terms of arbitrary symbols rather than particular numbers:
In [27]: a, b = symbols('a, b', real=True)
In [28]: eq = sin(2*theta + a) + b*cos(2*theta + a)
In [29]: solve(eq, theta)
Out[29]:
⎡ ⎛ ________ ⎞ ⎛ ________ ⎞⎤
⎢ ⎜ ╱ 2 ⎟ ⎜ ╱ 2 ⎟⎥
⎢ a ⎜╲╱ b + 1 - 1⎟ a ⎜╲╱ b + 1 + 1⎟⎥
⎢- ─ - atan⎜───────────────⎟, - ─ + atan⎜───────────────⎟⎥
⎣ 2 ⎝ b ⎠ 2 ⎝ b ⎠⎦
That gives two solutions one of which is negative and one positive. The other solutions come from adding multiples of pi.
The solution was much easier than I expected:
Having the ellipse-parameters a,b and c the maximum is simply L_maximum = a+b with the angle theta = math.raidans(45) - c/2
Plus: The imported angles theta1,... have to be converted to radians as well, with math.radians()

Full factorization of polynomials over complexes with SymPy

I want to fully factorize a polynom, thus factorize it over complexes.
SymPy provide factor to do it, but I’m very surprised that factorization is done only over integer roots, e.g. :
>>> from sympy import *
>>> z = symbols('z')
>>> factor(z**2 - 1, z)
(z - 1)*(z + 1)
>>> factor(z**2 + 1, z)
z**2 + 1
or
>>> factor(2*z - 1, z)
2*z - 1
>>> factor(2*z - 1, z, extension=[Integer(1)/2])
2*(z - 1/2)
An answered question already exists : Factor to complex roots using sympy, and the solution given by asmeurer works :
>>> factor(z**2 + 1, z, extension=[I])
(z - I)*(z + I)
but you need to specify every divisor of non-integer roots, e.g. :
>>> factor(z**2 + 2, z, extension=[I])
z**2 + 2
>>> factor(z**2 + 2, z, extension=[I, sqrt(2)])
(z - sqrt(2)*I)*(z + sqrt(2)*I)
My question is : how to fully factorize a polynom (thus over complexes), without needing to give every divisor to extension ?
asmeurer gives a solution to do this :
>>> poly = z**2 + 2
>>> r = roots(poly, z)
>>> LC(poly, z)*Mul(*[(z - a)**r[a] for a in r])
/ ___ \ / ___ \
\z - \/ 2 *I/*\z + \/ 2 *I/
But it should exists a native way to do it, no ?
Someting like factor(poly, z, complex=True).
I looked for in the documentation of factor, but I did not find anything.
Futhermore, factor can take domain as optional argument that I believed allows to specified the set on which the factorization is made, but not
>>> factor(z**2 + 2, z, domain='Z')
2
z + 2
>>> factor(z**2 + 2, z, domain='R')
/ 2 \
2.0*\0.5*z + 1.0/
>>> factor(z**2 + 2, z, domain='C')
2
1.0*z + 2.0
The domain argument should work and in the case of Gaussian rationals you can also use gaussian=True which is equivalent to extension=I:
In [24]: factor(z**2 + 1, gaussian=True)
Out[24]: (z - ⅈ)⋅(z + ⅈ)
That doesn't work in your case though because the factorisation needs to be over QQ(I, sqrt(2)) rather than QQ(I). The reason that domains 'R' and 'C' don't work as expected is because they are inexact floating point domains rather than domains representing the real or complex numbers in the pure mathematical sense and factorisation is
The approaches above can be combined though with
In [28]: e = z**2 + 2
In [29]: factor(e, extension=roots(e))
Out[29]: (z - √2⋅ⅈ)⋅(z + √2⋅ⅈ)

Python SymPy factoring polynomial over complex numbers

I am using SymPy to factor polynomials over the complex numbers.
import sympy as sp
x, y = sp.symbols('x y')
z = sp.expand( (x - 1) * (y - 1) )
factorization_1 = sp.factor(z)
factorization_2 = sp.factor(z, extension = [sp.I])
Here, z has contains the expression xy - x - y + 1, and factorization_1 indeed has the expected value (x - 1)(y - 1), but for some reason factorization_2 has the value x - 1.
Where did the factor (y - 1) go?

Equilibria of complex non linear system

When i run the following code i get
TypeError: can't multiply sequence by non-int of type "Add'
Can anyone explain why I get this error?
from sympy.core.symbol import symbols
from sympy.solvers.solveset import nonlinsolve
x, y, z, r, R, a, m, n, b, k1, k2 = symbols('x,y,z,r,R,a,m,n,b,k1,k2', positive=True)
f1 = r * x * (1 - x / k1) - (a * z * x ** (n + 1)) / (x ** n + y ** n)
f2 = R * y * (1 - y / k2) - (b * z * y ** (n + 1)) / (x ** n + y ** n)
f3 = z * (a * x ** (n + 1) + b * y ** (n + 1)) / (x ** n + y ** n) - m * z
f = [f1, f2, f3]
nonlinsolve(f, [x, y, z])
The error message is not really descriptive but the full stack trace indicates where the problem was: SymPy tries to work with the expression as if it was a polynomial, and finds that impossible because the exponent n is a symbol rather than a concrete integer.
Simply put, SymPy does not have an algorithm for solving systems like that one (and I'm not sure if any CAS has).
When written in polynomial form, the system has monomials of total degree n+2. So, already for n = 1 this is utterly hopeless: a system of three cubic equations with three unknowns. SymPy can solve the case n = 0, and I wouldn't expect anything more than that.

Factoring polys in sympy

I'm doing a very simple probability calculations of getting subset of X, Y, Z from set of A-Z (with corresponding probabilities x, y, z).
And because of very heavy formulas, in order to handle them, I'm trying to simplify (or collect or factor - I dont know the exact definition) these polynomial expressions using sympy.
So.. having this (a very simple probability calculation expression of getting subset of X,Y,Z from set of A-Z with corresponding probabilities x, y, z)
import sympy as sp
x, y, z = sp.symbols('x y z')
expression = (
x * (1 - x) * y * (1 - x - y) * z +
x * (1 - x) * z * (1 - x - z) * y +
y * (1 - y) * x * (1 - y - x) * z +
y * (1 - y) * z * (1 - y - z) * x +
z * (1 - z) * y * (1 - z - y) * x +
z * (1 - z) * x * (1 - z - x) * y
)
I want to get something like this
x * y * z * (6 * (1 - x - y - z) + (x + y) ** 2 + (y + z) ** 2 + (x + z) ** 2)
a poly, rewritten in way to have as few operations (+, -, *, **, ...) as possible
I tried factor(), collect(), simplify(). But result differs from my expectations. Mostly I get
2*x*y*z*(x**2 + x*y + x*z - 3*x + y**2 + y*z - 3*y + z**2 - 3*z + 3)
I know that sympy can combine polynomials into simple forms:
sp.factor(x**2 + 2*x*y + y**2) # gives (x + y)**2
But how to make sympy to combine polynomials from expressions above?
If this is impossible task in sympy, may be there are any other options?
Putting together some of the methods happens to give a nice answer this time. It would be interesting to see if this strategy works more often than not on the equations you generate or if, as the name implies, this is just a lucky result this time.
def iflfactor(eq):
"""Return the "I'm feeling lucky" factored form of eq."""
e = Mul(*[horner(e) if e.is_Add else e for e in
Mul.make_args(factor_terms(expand(eq)))])
r, e = cse(e)
s = [ri[0] for ri in r]
e = Mul(*[collect(ei.expand(), s) if ei.is_Add else ei for ei in
Mul.make_args(e[0])]).subs(r)
return e
>>> iflfactor(eq) # using your equation as eq
2*x*y*z*(x**2 + x*y + y**2 + (z - 3)*(x + y + z) + 3)
>>> _.count_ops()
15
BTW, a difference between factor_terms and gcd_terms is that factor_terms will work harder to pull out common terms while retaining the original structure of the expression, very much like you would do by hand (i.e. looking for common terms in Adds that can be pulled out).
>>> factor_terms(x/(z+z*y)+x/z)
x*(1 + 1/(y + 1))/z
>>> gcd_terms(x/(z+z*y)+x/z)
x*(y*z + 2*z)/(z*(y*z + z))
For what it's worth,
Chris
As far as I know, there is no function that does exactly that. I believe it is actually a very hard problem. See Reduce the number of operations on a simple expression for some discussion on it.
There are however, quite a few simplification functions in SymPy that you can try. One that you haven't mentioned that gives a different result is gcd_terms, which factorizes out a symbolic gcd without doing an expansions. It gives
>>> gcd_terms(expression)
x*y*z*((-x + 1)*(-x - y + 1) + (-x + 1)*(-x - z + 1) + (-y + 1)*(-x - y + 1) + (-y + 1)*(-y - z + 1) + (-z + 1)*(-x - z + 1) + (-z + 1)*(-y - z + 1))
Another useful function is .count_ops, which counts the number of operations in an expression. For example
>>> expression.count_ops()
47
>>> factor(expression).count_ops()
22
>>> e = x * y * z * (6 * (1 - x - y - z) + (x + y) ** 2 + (y + z) ** 2 + (x + z) ** 2)
>>> e.count_ops()
18
(note that e.count_ops() is not the same as you counted yourself, because SymPy automatically distributes the 6*(1 - x - y - z) to 6 - 6*x - 6*y - 6*z).
Other useful functions:
cse: Performs a common subexpression elimination on the expression. Sometimes you can simplify the individual parts and then put it back together. This also helps in general to avoid duplicate computations.
horner: Applies the Horner scheme to a polynomial. This minimizes the number of operations if the polynomial is in one variable.
factor_terms: Similar to gcd_terms. I'm actually not entirely clear what the difference is.
Note that by default, simplify will try several simplifications, and return the one that is minimized by count_ops.
I have had a similar problem, and ended up implementing my own solution before I stumbled across this one. Mine seems to do a much better job reducing the number of operations. However, mine also does a brute-force style set of collections over all combinations of variables. Thus, it's runtime grows super-exponentially in the number of variables. OTOH, I've managed to run it on equations with 7 variables in a not-unreasonable (but far from real-time) amount of time.
It is possible that there are some ways to prune some of the search branches here, but I haven't bothered with it. Further optimizations are welcome.
def collect_best(expr, measure=sympy.count_ops):
# This method performs sympy.collect over all permutations of the free variables, and returns the best collection
best = expr
best_score = measure(expr)
perms = itertools.permutations(expr.free_symbols)
permlen = np.math.factorial(len(expr.free_symbols))
print(permlen)
for i, perm in enumerate(perms):
if (permlen > 1000) and not (i%int(permlen/100)):
print(i)
collected = sympy.collect(expr, perm)
if measure(collected) < best_score:
best_score = measure(collected)
best = collected
return best
def product(args):
arg = next(args)
try:
return arg*product(args)
except:
return arg
def rcollect_best(expr, measure=sympy.count_ops):
# This method performs collect_best recursively on the collected terms
best = collect_best(expr, measure)
best_score = measure(best)
if expr == best:
return best
if isinstance(best, sympy.Mul):
return product(map(rcollect_best, best.args))
if isinstance(best, sympy.Add):
return sum(map(rcollect_best, best.args))
To illustrate the performance, this paper(paywalled, sorry) has 7 formulae that are 5th degree polynomials in 7 variables with up to 29 terms and 158 operations in the expanded forms. After applying both rcollect_best and #smichr's iflfactor, the number of operations in the 7 formulae are:
[6, 15, 100, 68, 39, 13, 2]
and
[32, 37, 113, 73, 40, 15, 2]
respectively. iflfactor has 433% more operations than rcollect_best for one of the formulae. Also, the number of operations in the expanded formulae are:
[39, 49, 158, 136, 79, 27, 2]

Categories

Resources