sympy square roots: best way to do operations on rational numbers? - python

Since i want square roots to be simplified, I've come across this workaround:
from sympy import sqrt, factor, simplify
_sqrt = sqrt
sqrt = lambda x: factor(simplify(_sqrt(x)))
# do maths operations with sqrt...
But it's too slow and I don't think it's the most suitable method one can use
So is there any other way to work with square roots and simplify them - automatically -

SymPy automatically simplifies rational arguments to sqrt, but it is possible to write rationals in a manner such that they are not explicitly rational (as in your previous post):
>>> eq
sqrt((-9/10 + 6*sqrt(3)/5)**2 + (6/5 + 9*sqrt(3)/10)**2)
sqrt will only simplify an explicit Rational argument. Expansion of the base of the argument reveals that it is a Rational and the sqrt will simplify it:
>>> eq.base.expand()
9
>>> sqrt(9) == 3
True
Better than expand in such cases where you need to flatten an expression involving powers is the _mexpand function:
>>> from sympy.core.function import _mexpand as flat
>>> flat(eq)
3

Related

Sympy simplification is broken (square roots)?

I tried this in python shell
>>> from sympy import sqrt
>>> sqrt((-9/10 + 6*sqrt(3)/5)**2 + (6/5 + 9*sqrt(3)/10)**2)
sqrt((-0.9 + 6*sqrt(3)/5)**2 + (1.2 + 9*sqrt(3)/10)**2)
and when type it in google:
So how do I get numpy to give me a more simpilified result (it won't always be an int so I cant use evalf or N)
There are 2 things missing here:
You need to explicitly tell sympy to simplify the expression if it's something complex.
You should use Rational whenever possible, to avoid numerical inaccuraties of floating point arithmetic
All in all:
>>> from sympy import Rational, simplify, sqrt
>>> simplify(sqrt((-Rational(9, 10) + Rational(6,5)*sqrt(3))**2 + (Rational(6,5) + Rational(9,10)*sqrt(3))**2))
3

Round arithmetic during evaluation

Question
When evaluating arithmetic there are multiple steps (PEMDAS) taken during evaluation. I know you can evaluate an operation then round it, but at times you need to round your data to never exceed a certain precision throughout the evaluation. This brings me to my question: How can you round at every step during the evaluation instead of just at the end?
Examples
For our first example, we will be using the simple operation 0.125/0.375 and rounding to 2 decimals.
# This operation evaluates to 1/3
>>> 0.125/0.375
0.3333333333333333
# If we wanted to round it we could just do
>>> round(0.125/0.375, 2)
0.33
# But if we wanted to round at every step of PEMDAS the following would be necessary
>>> round(round(0.125, 2)/round(0.375, 2), 2)
0.32
# Same equation as above but written as (1/8)/(3/8)
>>> round(round(round(1, 2)/round(8, 2), 2)/round(round(3, 2)/round(8, 2), 2), 2)
0.32
As you can see you get a different result if rounding is performed at every step rather than just at the end.
Although being a bit cumbersome this approach does get the job done. Problems arise though when the equation is not hardcoded but rather received from the user:
# Rounding cannot be applied here in the same way that we did above
>>> eval(input("Arithmetic: "))
Arithmetic: (1/8)/(3/8)
0.3333333333333333
Use cases
This may seem pretty useless at first but can actually be very valuable for many things.
Here is a simple example where rounding at each step would be necessary for finding the holes of a function:
# undefined.py
from math import *
import numpy as np
function = input("Function in terms of x: ")
def is_undefined(x):
x = round(x, 2) # To deal with minor Python inaccuracies (ex: 1.000000000000001)
try:
eval(function)
return False
except ZeroDivisionError:
return True
undefined = [x for x in np.linspace(-5, 5, 1001) if is_undefined(float(x))]
print(undefined)
# Works perfectly!
>>> python undefined.py
Function in terms of x: (x**2)*(x-2)/(x-2)
[2.0]
# Unable to find the hole at x=pi
>>> python undefined.py
Function in terms of x: (x**2)*(2*x - 2*pi)/(x - pi)
[]
The decimal module provides a Decimal type which can be configured so that all arithmetic operations are rounded to a certain number of decimal places:
>>> import decimal as d
>>> d.setcontext(d.Context(prec=2))
>>> x = d.Decimal(0.125)
>>> y = d.Decimal(0.375)
>>> x / y
Decimal('0.33')
You can force rounding of the numbers before the division by using the unary + operation, which normally does nothing, but in this case it applies the precision from the current context, changing the result (to be more inaccurate, of course):
>>> (+x) / (+y)
Decimal('0.32')
So a solution for an expression from user input could be to replace all number literals and instances of the variable x with Decimal objects of the same values: here I've used a regular expression to do that, and to use a unary + to also force rounding before operations.
import decimal as d
import re
d.setcontext(d.Context(prec=2))
function = input("Function in terms of x: ")
function = re.sub(r'([0-9]+(\.[0-9]+)?|x)', r'(+d.Decimal(\1))', function)
# ...
Note there is no longer a need to write x = round(x, 2), because the expression itself forces x to be rounded.
You may be looking for symbolic math instead, such as Sympy, which can probably do what you're really looking for-
Specifically, not aliasing transcendental numbers (like pi and e) or waiting to reduce irreducible fractions into decimal space until asked to evaluate to a decimal
>>> from sympy import *
>>> expr = "(1/8)/(3/8)"
>>> simplify(expr) # precise value
1/3
>>> simplify(expr).evalf() # decimal aliasing
0.333333333333333
>>> N("(1/8)/(3/8)") # using sympy.N()
0.333333333333333
This can also be used to solve equations
>>> x = symbols("x", real=True)
>>> solve(x**2 - 1) # simple solution
[-1, 1]
>>> solve(x**2 - pi) # more complex solution
[-sqrt(pi), sqrt(pi)]
>>> [N(expr) for expr in solve(x**2 - pi)] # decimal approximation
[-1.77245385090552, 1.77245385090552]
This can also be used (perhaps evilly) with Python constructs
>>> [N(x * pi) for x in range(10)] # lots of approximations!
[0, 3.14159265358979, 6.28318530717959, 9.42477796076938, 12.5663706143592, 15.7079632679490, 18.8495559215388, 21.9911485751286, 25.1327412287183, 28.2743338823081]

Finding coefficients using Sympy doesn't include square root (sqrt) as value

In SymPy, there is a useful feature to create a dictionary of coefficients for a given expression.
However, I'm encountering an irritating bug (/feature?) whereby square roots are considered to be part of the variable, and not part of the coefficient value.
Minimum example:
from sympy import sqrt, symbols
k, t = symbols('k, t')
d = 1.5*t + 2.5*sqrt(2)*k
d.as_coefficients_dict()
This returns:
{𝑡:1.5, sqrt(2)*𝑘:2.5}
When instead, the sqrt(2) should be considered as part of the coefficient. Thus, I expected to see the result:
{𝑡:1.5, 𝑘:2.5*sqrt(2)}
NB, I'm using the latest SymPy version 1.4
Is this a bug? Or an alternative way to use the function to get the expected value please?
EDIT: I amended the question to note that I am using the Sympy sqrt function. I also tried using NumPy's np.sqrt, which evaluates correctly, but gives the full numerical value rather than a nice neat sqrt(2) for the value of the k coefficient.
The documentation explicitly states:
Return a dictionary mapping terms to their Rational coefficient.
So to start with, this is the expected behavior. To see this, you can note that the sqrt is not the problem, rather it is the fact that the coefficient is irrational. If we take a rational coefficient, we get your expected behavior:
>>> from sympy import sqrt, symbols
>>> k, t = symbols('k, t')
>>> d = 1.5*t + 2.5*sqrt(4)*k
>>> d.as_coefficients_dict()
{k: 5.00000000000000, t: 1.50000000000000}
One way to solve your problem is to explicitly inquire about each coefficient with the given variables:
>>> from sympy import sqrt, symbols
>>> k, t = symbols('k, t')
>>> d = 1.5*t + 2.5*sqrt(2)*k
>>> {sym : d.coeff(sym) for sym in (k, t)}
{k: 2.5*sqrt(2), t: 1.50000000000000}

Why is (-27)**(1.0/3.0) not -3.0 in Python?

In math, you are allowed to take cubic roots of negative numbers, because a negative number multiplied by two other negative numbers results in a negative number. Raising something to a fractional power 1/n is the same as taking the nth root of it. Therefore, the cubic root of -27, or (-27)**(1.0/3.0) comes out to -3.
But in Python 2, when I type in (-27)**(1.0/3.0), it gives me an error:
Traceback (most recent call last):
File "python", line 1, in <module>
ValueError: negative number cannot be raised to a fractional power
Python 3 doesn't produce an exception, but it gives a complex number that doesn't look anything like -3:
>>> (-27)**(1.0/3.0)
(1.5000000000000004+2.598076211353316j)
Why don't I get the result that makes mathematical sense? And is there a workaround for this?
-27 has a real cube root (and two non-real cube roots), but (-27)**(1.0/3.0) does not mean "take the real cube root of -27".
First, 1.0/3.0 doesn't evaluate to exactly one third, due to the limits of floating-point representation. It evaluates to exactly
0.333333333333333314829616256247390992939472198486328125
though by default, Python won't print the exact value.
Second, ** is not a root-finding operation, whether real roots or principal roots or some other choice. It is the exponentiation operator. General exponentiation of negative numbers to arbitrary real powers is messy, and the usual definitions don't match with real nth roots; for example, the usual definition of (-27)^(1/3) would give you the principal root, a complex number, not -3.
Python 2 decides that it's probably better to raise an error for stuff like this unless you make your intentions explicit, for example by exponentiating the absolute value and then applying the sign:
def real_nth_root(x, n):
# approximate
# if n is even, x must be non-negative, and we'll pick the non-negative root.
if n % 2 == 0 and x < 0:
raise ValueError("No real root.")
return (abs(x) ** (1.0/n)) * (-1 if x < 0 else 1)
or by using complex exp and log to take the principal root:
import cmath
def principal_nth_root(x, n):
# still approximate
return cmath.exp(cmath.log(x)/n)
or by just casting to complex for complex exponentiation (equivalent to the exp-log thing up to rounding error):
>>> complex(-27)**(1.0/3.0)
(1.5000000000000004+2.598076211353316j)
Python 3 uses complex exponentiation for negative-number-to-noninteger, which gives the principal nth root for y == 1.0/n:
>>> (-27)**(1/3) # Python 3
(1.5000000000000004+2.598076211353316j)
The type coercion rules documented by builtin pow apply here, since you're using a float for the exponent.
Just make sure that either the base or the exponent is a complex instance and it works:
>>> (-27+0j)**(1.0/3.0)
(1.5000000000000004+2.598076211353316j)
>>> (-27)**(complex(1.0/3.0))
(1.5000000000000004+2.598076211353316j)
To find all three roots, consider numpy:
>>> import numpy as np
>>> np.roots([1, 0, 0, 27])
array([-3.0+0.j , 1.5+2.59807621j, 1.5-2.59807621j])
The list [1, 0, 0, 27] here refers to the coefficients of the equation 1x³ + 0x² + 0x + 27.
I do not think Python, or your version of it, supports this function. I pasted the same equation into my Python interpreter, (IDLE) and it solved it, with no errors. I am using Python 3.2.

get the program to recognize if its an integers or a real numbers (python)

thankyou for your help.
i am very new to programming, but have decided to learn Python. i am doing a program that can check if a number is a prime. this is mathematically done by checking if (x-1)^p -(x^p-1) is devisible by p (Capable of being divided, with no remainder) then p is a prime.
However i have run into trouble. this is my code so far:
from sympy import *
x=symbols('x')
p=11
f=(pow(x - 1, p)) - (pow(x, p) - 1) # (x-1)^p -(x^p-1)
f1=expand(f)
>>> -11*x**10 + 55*x**9 - 165*x**8 + 330*x**7 - 462*x**6 + 462*x**5 - 330*x**4 + 165*x**3 - 55*x**2 + 11*x
f2= f1/p
>>> -x**10 + 5*x**9 - 15*x**8 + 30*x**7 - 42*x**6 + 42*x**5 - 30*x**4 + 15*x**3 - 5*x**2 + x
to tell if the number p is a prime i need to check if the coefficients of the polynomium is divisible by p. so i have to check if the coefficients of f2 is whole numbers or real numbers.
this is what i would like to make a program that can check: https://www.youtube.com/watch?v=HvMSRWTE2mI
i have tried making it into int but it still shows fractions like 1/2 and 3/7. i wish that it will only show whole numbers.
how do i make it so?
What the method effective does is expand the polynomial and drop the first (x^p) and last coefficients (x^0). Then you have to iterate through the rest and check for divisibility. Since a polynomial expansion of power p produces p+1 terms (from 0 to p), we want to collect p-2 terms (from 1 to p-1). This is all summed up in the following code.
from sympy.abc import x
def is_prime_sympy(p):
poly = pow((x - 1), p).expand()
return not any(poly.coeff(x, i) % p for i in xrange(1, p))
This works, but the higher the number you input, e.g. 1013, the longer you'll notice it takes. Sympy is slow because internally it stores all expressions as some classes and all multiplications and additions take a long time. We can simply generate the coefficients using Pascal's triangle. For the polynomial (x - 1)^p, the coefficients are supposed to change sign, but we don't care about that. We just want the raw numbers. Credits to Copperfield for pointing out you only need half of the coefficients because of symmetry.
import math
def combination(n, r):
return math.factorial(n) // (math.factorial(r) * math.factorial(n - r))
def pascals_triangle(row):
# only generate half of the coefficients because of symmetry
return (combination(row, term) for term in xrange(1, (row+1)//2))
def is_prime_math(p):
return not any(c % p for c in pascals_triangle(p))
We can time both methods now to see which one is faster.
import time
def benchmark(p):
t0 = time.time()
is_prime_math(p)
t1 = time.time()
is_prime_sympy(p)
t2 = time.time()
print 'Math: %.3f, Sympy: %.3f' % (t1-t0, t2-t1)
And some tests.
>>> benchmark(512)
Math: 0.001, Sympy: 0.241
>>> benchmark(2003)
Math: 3.852, Sympy: 41.695
We know that 512 is not a prime. The very second term we have to check for divisibility fails the test, so most of the time is actually spent generating the coefficients. Python lazily computes them while sympy must expand the whole polynomial out before we can start collecting them. This shows as that a generator approach is preferable.
2003 is prime and here we notice sympy performs 10 times as slowly. In fact, all of the time is spent generating the coefficients, as iterating over 2000 elements for a modulo operation takes no time. So if there are any further optimisations, that's where one should focus.
numpy.poly1d()
Numpy has a class that can manipulate polynomial coefficients and it's exactly what we want. It even works relatively fast for powers up to 50k. However, in its original implementation it's useless to us. That is because the coefficients are stored as signed int32, which means very quickly they will overflow and our modulo operations will be thrown off. In fact, it'll fail for even 37.
But it's fast, though, right? Maybe if we can hack it so it accepts infite precision integers... Maybe it's possible, maybe it isn't. But even if it is, we have to consider that maybe the reason why it is so fast is exactly because it uses a fixed precision type under the hood.
For the sake of curiosity, this is what the implementation would look like if it were any useful.
import numpy as np
def is_prime_numpy(p):
poly = pow(np.poly1d([1, -1]), p)
return not any(c % p for c in poly.coeffs[1:-1])
And for the curious ones, the source code is located in ...\numpy\lib\polynomial.py.
I am not sure if I understood what you mean, but for checking if a number is an integer or float you can use isinstance:
>>> isinstance(1/2.0, float)
>>> True
>>> isinstance(1/2, float)
>>> False

Categories

Resources