How to check whether a symbolic expression is rational? - sympy

I'm using Sympy and I haven't found a simple way of testing for x ∈ Q.
Context: I have a set of solutions of a set of very simple, 2DoF eigenvalue problems (e.g.,
) and I want to check if one of these solutions is rational (or, in other words, if the solution doesn't contains a square root).
A direct way of checking is what I would like the best, but I could accept also an answer that deals with finding (not finding) a square root in the solution.

The function rational = lambda x: all(i.exp.is_Integer for i in x.atoms(Pow)) is a direct translation of your criteria to return True if all powers (if present) are integers.
>>> from sympy import Pow, S, sqrt
>>> rational = lambda x: all(i.exp.is_Integer for i in x.atoms(Pow))
>>> rational(S.Half)
True
>>> rational(sqrt(3))
False
>>> rational(3/(1+sqrt(3)))
False

Related

Sympy: Simplify small compound fraction with squares and roots

I have got the following situation (in Sympy 1.8):
from sympy import *
u = symbols('u') # not necessarily positive
term = sqrt(1/u**2)/sqrt(u**2)
The term renders as
How can I simplify this to 1/u**2, i.e. ?
I have tried many functions from https://docs.sympy.org/latest/tutorial/simplification.html, and some arguments listed in https://docs.sympy.org/latest/modules/simplify/simplify.html but could not get it to work.
The variable needs to be declared as real number:
u=symbols('u', real=True)
Then the term is auto-simplified.
(I suggested a corresponding Sympy documentation change.)

How to obtain only rational and not floating point results using sympy

I consider following matrices:
M1 = Matrix([[1/7,2/7],[3/7,4/7]])
M2 = Matrix([[1,2],[3,4]])/7
which are evidently identical, but when I determine their determinant I obtain different results:
print(M1.det())
print(M2.det())
giving the following results:
-0.0408163265306122
-2/49
I would like the first result to be expressed as a rational and not as a floating point.
This is an example of one of the gochas and pitfalls from SymPy's documentation. My answer will basically reiterate what is said there. I highly recommend going through it.
When you type 1/7, the Python interpreter changes it into a float before SymPy has a chance to identify it as a rational number. In order for SymPy to evaluate it before Python does, you need to use some other method. You have already shown one of those other methods with M2: divide a SymPy object by 7 instead of a Python int by 7. Here are a few other ways:
from sympy import *
M = Matrix([[Rational(1, 7),Rational(2, 7)],[Rational(3, 7),Rational(4, 7)]]) # create a Rational object
print(det(M))
M = Matrix([[S(1)/7,S(2)/7],[S(3)/7,S(4)/7]]) # divide a SymPy Integer by 7
print(det(M))
M = Matrix([[S("1/7"),S("2/7")],[S("3/7"),S("4/7")]]) # let SymPy interpret it
print(det(M))
M = Matrix([[1,2],[3,4]])/7 # divide a SymPy Matrix by 7
print(det(M))
M = S("Matrix([[1/7,2/7],[3/7,4/7]])") # throw the whole thing into SymPy
print(det(M))
All of the above will give rational determinants. There are probably many more ways to make SymPy identify a rational number.

Using sympy.integrate on a function that involves int()

I'm trying to integrate functions in Python. scipy.integrate.quad seems to work ok; but just be sure I'd like to check the results against other integration code. It was suggested that I try sympy.integrate. Now the code for the functions I want to integrate contains int(), which I use to convert floats into ints. This is ok for quad, but not for sympy.integrate.
Here's a simple example that reproduces the error:
import sympy
def f(x):
return sympy.exp(int(x))
sympy.symbols('x')
print(sympy.integrate(f(x),(x,0,2)))
This yields the error: TypeError: can't convert symbols to int
So is there a way to integrate functions that involve int() with scipy.integrate?
Thanks
To use integrate f must be a SymPy symbolic function which disallows your particular use of int. int(x) where x is a Symbol will always yield a type error however you could represent this symbolically using the floor function:
def f(x):
return sympy.exp(sympy.floor(x))
However, using floor may defeat some of the purpose of using SymPy in the first place because it will probably prevent discovery of an analytic solution as the following python session demonstrates:
>>> from sympy import *
>>> x = symbols("x")
>>> integrate(exp(floor(x)), (x, 0, 2)) # use of floor prevents evaluated result
Integral(exp(floor(x)), (x, 0, 2))
Though you can use the evalf method to compute a numeric result (which is ultimately performed by mpmath):
>>> integrate(exp(floor(x)), (x, 0, 2)).evalf()
3.7
(perhaps this result suggests sympy could better handle this integral? Wolfram Alpha computes this as 1 + e = 3.71828... so I suppose there is at least a floating point precision bug here too - see comment re ceiling)
In any case, I don't know if you consider that an appropriate result considering the version of f without floor:
>>> integrate(exp(x), (x, 0, 2))
-1 + exp(2)
>>> _.evalf()
6.38905609893065

Inequality validation checking based on assumption in sympy

I want to check whether inequality a<f hold based on the following assumptions
a<b
b=d
d=e
e<f
What is the best way to verify whether inequality a<f hold based on those assumption.
I can substitute equalizes and Simplify a<b as follows
>>> from sympy import *
>>> a=Symbol('a')
>>> b=Symbol('b')
>>> c=Symbol('c')
>>> d=Symbol('d')
>>> f=a<b
>>> f.subs({b:c,c:d}).simplify()
a < d
Looking forward for suggestion

How to obtain all solutions for cos(x)*cosh(x) == 1 in Sympy?

Following is the best I can get.
map(lambda n: nsolve(cos(x)*cosh(x)-1,x,3.14/2+3.14*n),range(9))
[mpf('0.0039941152964418809'),
mpf('4.730040744862704'),
mpf('7.8532046240958376'),
mpf('10.995607838001671'),
mpf('14.137165491257464'),
mpf('17.278759657399481'),
mpf('20.420352245626061'),
mpf('23.561944902040455'),
mpf('26.703537555508186')]
If you change range(9) to range(10), sympy will return an error.
ValueError: Could not find root within given tolerance. (1.59798e-17 > 2.1684e-1
9)
Try another starting point or tweak arguments.
I have asked this in the Mathematica site, Mathematica seems can provide the solutions quite accurate and fast. Check this out: how-to-obtain-all-solutions-for-cosx-coshx-1
This is a nice example of using an intelligent initial guess. If you just provide a tolerance you can find the additional solution:
>>> len([nsolve(cos(x)*cosh(x)-1,x,3.14/2+3.14*n,tol=1e-12) for n in range(10)])
10
Note, however, that the function is very steep in the region of the roots and it is unlikely that you will every end up with an x value that will make the function value small. If you know that your initial guesses leads to a root and not a discontinuity, you can safely use the verify=False to skip the verification of the solution (and verify it yourself, perhaps by taking the slope into account). I always feel safer using the bisect method, however, in these cases:
>>> f
cos(x)*cosh(x) - 1
>>> bounds = lambda i: (3.14*i, 3.14*(i+1))
>>> root = lambda i: nsolve(f, bounds(i), solver='bisect', verify=False)
>>> root(0)
mpf('0.0')
>>> root(99)
mpf('312.58846903218443')
>>> root(100)
mpf('315.73006168577422')
You can see that the function at this point is very large, but if we normalize by the derivative of the function the answer looks better:
>>> ans = _
>>> f.subs(x, ans).n(2)
2.3e+122
>>> (f/f.diff(x)).subs(x, ans).n(2)
-3.4e-15
Note: currently, it will not work to pass a normalized function to nsolve so it can be used in the solving process: nsolve only works with the numerator of the function you pass.