Sympy: Numerically find root of a symbolic function - python

I have a function whose roots I'd like to find. So far, even Mathematica was inable of finding the roots analytically, so numerically is fine (but please, I'd be happy to be surprised on this matter).
The examples in the documentation all refer to "real" functions, lambda functions, and don't address this issue sufficiently (or I'm just too slow to understand). Here's a simple use case:
from sympy import *
p, r, c, y, lam, f = symbols('p r c y lambda f')
priceCDF = (c*lam*p + c*r - lam*p*r - p*r + r*(c - p)*LambertW(-exp((-c*lam*p - c*r + lam*p*r + lam*r*(c - p) + p*r)/(r*(c - p))), -1))/(lam*r*(c - p))
priceCDFplot = priceCDF.subs(r, 2).subs(c, 0.5).subs(lam, 1)
mpmath.findroot(priceCDFplot, 0.8)
which gives me TypeError: 'Mul' object is not callable. What am I wrong, how do I numerically find the root -- and how could I find it analyitcally?

If you want to use mpmath.findroot, you'll need to convert the SymPy expression to a mpmath expression. The easiest way to do this is with lambdify(p, priceCDF, 'mpmath') (I'm assuming p is the variable you want to solve for).
Another solution would be to use sympy.nsolve, which works directly on SymPy expressions.

Related

Unexpected result for solving ordinary linear differential equation of second order with SymPy

I am trying to solve this ordinary linear differential equation of second order with SymPy and get an unexpected result.
import sympy as sym
k, t = sym.symbols('k, t')
s = sym.Function('s')
diff_eq = sym.Eq(s(t).diff(t, 2) + s(t) * k**2, 0) # everything fine here, when I print this I get what I expected.
solution_diff_eq = sym.dsolve(diff_eq, s(t))
print(solution_diff_eq)
Which prints
Eq(s(t), C1*exp(-I*k*t) + C2*exp(I*k*t))
However, the solution I expected is
Any ideas what I have done wrong?
The result prints as
Eq(s(t), C1*exp(-I*k*t) + C2*exp(I*k*t))
which is correct, as I is the imaginary unit. You might prefer the real form, but sympy was not notified of that and produced the most simple form as sum of exponential terms, especially as it is not clear if k is actually real.
If you make it explicit that k is a positive real number via
k = sym.Symbol('k', real=True, positive=True)
the solution is actually in real form, as you were expecting
Eq(s(t), C1*sin(k*t) + C2*cos(k*t))

Python modulo result differs from wolfram alpha?

When I run my python 3 program:
exp = 211
p = 199
q = 337
d = (exp ** (-1)) % ((p - 1)*(q - 1))
results in 211^(-1).
But when I run the calculation in wolfram alpha I get the result I was expecting.
I did some test outputs and the variables exp, p and q in the program are all the integer values I used in wolfram alpha.
My goal is to derive a private key from a (weakly) encrypted integer.
If I test my wolfram alpha result, I can decrypt the encrypted message correctly.
Wolfram Alpha is computing the modular inverse. That is, it's finding the integer x such that
exp*x == 1 mod (p - 1)*(q - 1)
This is not the same as the modulo operator %. Here, Python is simply calculating the remainder when 1/exp is divided by (p - 1)*(q - 1) when given the expression in your question.
Copying the Python code from this answer, you can compute the desired value with Python too:
>>> modinv(exp, (p - 1)*(q - 1))
45403
Wolfram Alpha does not have well-defined syntax. It takes arbitrary text you provide and attempts to figure out what you meant by that input. In this case, it decided you were probably looking for a modular inverse, and it gave you one.
Python has well-defined syntax. In Python, the parser does not take the ** and the % together and guess that that combination makes the two operators have a meaning other than their usual meaning. The ** is computed the usual way, and then % is the modulo operator. If you want a modular inverse, you'll have to write one yourself.
I think the idea here is that wolfram alpha and python define the modulo operation differently depending on the fact that you are dealing with integers or real numbers.
In this case, Wolfram Alpha is using the modulo inverse because it detects the first number is 0 < x < 1
More information about the definition on real numbers here
Python evaluates immediately (211^(-1) gets computed as 0.004739... and not ekpt as 1/211) and the modular Euclidan remainder for x and y is conventinally defined as x-floor(x/y)*y if any of x,y is a rational number. If you do your calculation with some dedicated number theoretic program like e.g.: GP/Pari
ep = 211;p = 199;q = 337;(ep ^ (-1)) % ((p - 1)*(q - 1))
you will get the result you expected to get because a) it keeps fractions as fractions as long as possible and b) knows about modular arithmetic.
Is you like Python you may take a look at the programms an libraries offered at SciPy. SymPy might be what you are looking for.

Getting a better answer from sympy inverse laplace transform

Trying to compute the following lines I'm getting a realy complex result.
from sympy import *
s = symbols("s")
t = symbols("t")
h = 1/(s**3 + s**2/5 + s)
inverse_laplace_transform(h,s,t)
The result is the following:
(-(I*exp(-t/10)*sin(3*sqrt(11)*t/10) - exp(-t/10)*cos(3*sqrt(11)*t/10))*gamma(-3*sqrt(11)*I/5)*gamma(-1/10 - 3*sqrt(11)*I/10)/(gamma(9/10 - 3*sqrt(11)*I/10)*gamma(1 - 3*sqrt(11)*I/5)) + (I*exp(-t/10)*sin(3*sqrt(11)*t/10) + exp(-t/10)*cos(3*sqrt(11)*t/10))*gamma(3*sqrt(11)*I/5)*gamma(-1/10 + 3*sqrt(11)*I/10)/(gamma(9/10 + 3*sqrt(11)*I/10)*gamma(1 + 3*sqrt(11)*I/5)) + gamma(1/10 - 3*sqrt(11)*I/10)*gamma(1/10 + 3*sqrt(11)*I/10)/(gamma(11/10 - 3*sqrt(11)*I/10)*gamma(11/10 + 3*sqrt(11)*I/10)))*Heaviside(t)
However the answer should be simpler, Wolframalpha proves it.
Is there any way to simplify this result?
I tried a bit with this one and the way I could find a simpler solution is using something like:
from sympy import *
s = symbols("s")
t = symbols("t", positive=True)
h = 1/(s**3 + s**2/5 + s)
inverse_laplace_transform(h,s,t).evalf().simplify()
Notice that I define t as a positive variable, otherwise the sympy function returns a large term followed by the Heaviaside function. The result still contains many gamma functions that I could not reduce to the expression returned by Wolfram. Using evalf() some of those are converted to their numeric value and then after simplification you get a expression similar like the one in Wolfram but with floating numbers.
Unfortunately this part of Sympy is not quite mature. I also tried with Maxima and the result is quite close to the one in Wolfram. So it seems that Wolfram is not doing anything really special there.

Can't get real solution with sympy.solve

I tried sympy.solve to solve an nonlinear equation system. It gave me a complex solution set.
Then I tried this equation system in matlab, and got a real solution set which I think is correct, because this is actually a geometry problem and I tested this solution in CAD software.
So...why sympy.solve gave me a complex solution set? where on earth did I make a mistake... or mistakes?
here is the code i wrote:
import sympy
x1=0
y1=-620
r1=920
zqua=126
yqua=276
x3=51
rm=205
r3=104
x0 = sympy.Symbol('x0')
y0 = sympy.Symbol('y0')
r0 = sympy.Symbol('r0')
f1=r0+((x0-x1)**2+(y0-y1)**2)**0.5-r1
f2=(zqua-x0)**2+(yqua-y0)**2-r0**2
f3=r0+((x0-x3)**2+(y0-rm)**2)**0.5-r3
A=sympy.solve((f1,f2,f3), (x0, y0, r0))
print A
and here is the solution it gave:
[(132.229058631742 - 3.4301208813066*I, 282.298802365236 + 1.7767794177989*I, -8.07109966646592 + 1.26065122532955*I), (132.229058631742 + 3.4301208813066*I, 282.298802365236 - 1.7767794177989*I, -8.07109966646592 - 1.26065122532955*I)]
Although you had the right sign of the difference of r in f1 and f3, if you write in terms of squares (where the sign no longer matters) as is already done in f2 you obtain 2 real answers:
>>> f1=(x0-x1)**2+(y0-y1)**2-(r0 - r1)**2
>>> f2=(zqua-x0)**2+(yqua-y0)**2-r0**2
>>> f3=(x0-x3)**2+(y0-rm)**2-(r3 - r0)**2
>>>
>>> A=sympy.solve((f1,f2,f3), (x0, y0, r0))
>>> [[i.n(2) for i in w] for w in A]
[[73., 2.2e+2, 79.], [88., 2.5e+2, 48.]]
>>>
SymPy should have found the roots with the other representation, it seems, but it selected -79 and -48 instead, and those results did not satisfy the original equations and were thus excluded from the reported solution.

Sympy won't evaluate 2x but will evaluate x*2

I'm using Sympy's sympify function to simplify 2 expressions so I can compare them for equality.
For example:
expr1 = sympify("(2 * x) + (x + 10)")
expr2 = sympify("(x + 10) + (x * 2)")
if expr1 == expr2:
print "Congrats those are essentially the same!"
However when using the form 2x as apposed to x*2 i get a parse exception eg:
expr1 = sympify("2x + (x + 10)")
Is there any way I can get sympy to understand the 2x form ?
If not, is there any other library that will allow this form ?
Well, you could modify the sympy lexer (or parser / grammar / whatever).
You could also wrap it with a function that transformed your input strings for you, using something like this:
>>> import re
>>> expr = '2x + 1'
>>> re.sub(r"(\d+)(\w+)", r"(\1 * \2)", expr)
'(2 * x) + 1'
But ask yourself why this notation isn't there to begin with.
For example, all of these are valid python, and though it's been a long while since I messed with sympy, I bet they mean something besides multiplication in sympy too:
0x32 # hex for 50
5e-3 # 0.005
2j # 2 * sqrt(-1) (so that one *is* multiplication, but by 1j, not j!)
15L # 15 (L used to represent long integers in python)
And what does x2 mean? Is it a variable named x2 or does it mean (x * 2). I purposely left this case out of the regular expression above because it's so ambiguous.
The development version of SymPy has the ability to parse such expressions. See http://docs.sympy.org/dev/modules/parsing#sympy.parsing.sympy_parser.implicit_multiplication_application. It is still not enabled by default in sympify, because sympify only does very basic extensions to the Python syntax (i.e., wrapping of numeric literals and undefined names, and converting ^ to **). But there is an example there that shows how to use it.
Note that this currently also applies implicit function application as well. Probably the two functionalities should be split up.
EDIT: Those functions are being split up in a pull request.

Categories

Resources