Different sympy expression results - python

First I want to point out that I'm a total sympy noob.
I'm trying to create a Custom Formula-based Measurement class with this sympy expression:
from sympy import Symbol, S, floor, sympify, Float
SU = Symbol('millimeter')
exp = S(20.0) + floor((((SU-S(212.5)) / S(10.0))) / S(0.5)) * S(0.5)
The problem I face is that for the same SU I get different result based on the way the expression is evaluated. Here is what I mean:
>>> exp.subs(SU, 215)
20.0000000000000
>>> exp.evalf(subs={SU: 215})
0.e+1 #This is actually 16.0 when: float(exp.evalf(subs={SU: 215}))
More interestingly the problem exists only when SU is between [213:217] (when I expect the result to be 20.0)
For the rest of the values its fine (AFAIK)
>>> exp.subs(SU, 212)
19.5000000000000
>>> exp.evalf(subs={SU: 212})
19.50
>>> exp.subs(SU, 218)
20.5000000000000
>>> exp.evalf(subs={SU: 218})
20.50
Any Ideas for this strange behavior ?

This was due to incorrect precision values. The bug was reported and is already corrected in the current version of SymPy available on GitHub; the versions of SymPy > 1.1.1 will not have this bug.
Using srepr on the output of subs provides some explanation:
x = Symbol('x')
srepr((floor(x)+20).evalf(subs={x:0.5}))
The output is Float('16.0', precision=1). This is binary precision. SymPy thinks that the output of floor, when it happens to be zero, has only one bit of precision. So it subsequently truncates the added +20 accordingly, to nearest power of 2.
Of course, this is a bug. There are several open issues related to Float class and rounding, such as this one; they may be related.
The workaround is to avoid evalf(subs=dict) construction (is it even documented?). Using the methods in the natural order: substitute, then evaluate, gives correct results:
srepr((floor(x)+20).subs({x:0.5}).evalf())
"Float('20.0', precision=53)"

Related

Hexadecimal notation of square roots in Python - Sha-512

I am going over the description of Sha-512. It is mentioned that the initial hash value consists of the sequence of 64-bit words that are obtained by taking the fractional part of the first eight primes. I am trying to replicate these values in Python, but I am not getting the same results. To include more digits, I am using the mpmath library.
from mpmath import *
mp.dps = 50
sqrt(2)
# mpf('1.4142135623730950488016887242096980785696718753769468')
mpf(0.4142135623730950488016887242096980785696718753769468 * 2 ** 64)
# mpf('7640891576956012544.0')
hex(7640891576956012544)
# '0x6a09e667f3bcc800'
However, the description indicates this value must be 6a09e667f3bcc908. As it can be seen, the result I get differs in the last three digits from what I should be getting according to the description. I was wondering why that is the case, and what is the correct approach.
I have come across a similar question, but adjusting it for 64-bit word would yield:
import math
hex(int(math.modf(math.sqrt(2))[0]*(1<<64)))
# '0x6a09e667f3bcd000'
which actually differs in the last four digits.
As a comment already explained, you're actually only using 53 bits in your calculations (native CPython float precision).
Here's an easy way to reproduce the result you're apparently after:
>>> import decimal
>>> x = decimal.getcontext().sqrt(2) - 1
>>> x
Decimal('0.414213562373095048801688724')
>>> hex(int(x * 2**64))
'0x6a09e667f3bcc908'
Nothing really magical about decimal. It just happens to use enough precision by default. You could certainly do the same with mpmath.
For example,
>>> import mpmath
>>> mpmath.mp.prec = 80
>>> hex(int(mpmath.frac( mpmath.sqrt(2) ) * 2**64))
'0x6a09e667f3bcc908'

Wrong result of evalf in SymPy but only for some values of the precision

I think I have encountered a bug of Sympy's evalf() method with substitutions passed.
By accident, I found an expression that evaluates to a wrong value if I replace the variable x by an integer rather than a Float. The funny thing is that this happens only for some values of the precision.
The expression looks somewhat arbitrary, but if I attempt to simplify it further the bug disappears. This is a minimal working example
#!/usr/bin/env python
import sympy
from sympy.abc import x
# Some valid mathematical expression
expr = 1/((x - 9)*(x - 8)*(x - 7)*(x - 4)**2*(x - 3)**3*(x - 2))
def example(prec):
# This is the string 1.( prec-2 zeroes )1
almost1 = '1.'+(prec-2)*'0'+'1'
# We replace the integer 1
res1 = expr.evalf(prec, subs={x:1})
# We replace a Float veeery close to 1
res_almost1 = expr.evalf(prec, subs={x:sympy.Float(almost1,prec)})
return res1, res_almost1
The expected outcome is that the returned tuple should contain similar numbers since 1 and almost1 are very close. However, for some values of prec the result obtained by replacing 1 to expr is zero. (While the one obtained by replacing almost1 is close to the correct one.)
You may ask: "What are the values of prec for which the expression is wrong?". By running the code
wrong = [str(a) for a in range(10,1001) if example(a)[0] == 0]
print(','.join(wrong))
I obtain this seemingly completely random list
11,20,22,29,31,38,40,49,58,67,76,78,85,87,94,96,105,114,123,132,134,141,143,150,152,159,161,170,179,188,190,197,199,206,208,215,217,226,235,244,253,255,262,264,271,273,282,291,300,309,311,318,320,327,329,338,347,356,365,367,374,376,383,385,392,394,403,412,421,423,430,432,439,441,448,450,459,468,477,486,488,495,497,504,506,515,524,533,542,544,551,553,560,562,571,580,589,598,600,607,609,616,618,625,627,636,645,654,663,665,672,674,681,683,692,701,710,719,721,728,730,737,739,748,757,766,775,777,784,786,793,795,802,804,813,822,831,833,840,842,849,851,858,860,869,878,887,896,898,905,907,914,916,925,934,943,952,954,961,963,970,972,981,990,999
I posted it here so see whether I made some blunder in my code, otherwise I plan to post a bug issue on Sympy's github.

Sympy cannot evaluate an infinite sum involving gamma functions

I am using Sympy to evaluate some symbolic sums that involve manipulations of the gamma functions but I noticed that in this case it's not evaluating the sum and keeps it unevaluated.
import sympy as sp
a = sp.Symbol('a',real=True)
b = sp.Symbol('b',real=True)
d = sp.Symbol('d',real=True)
c = sp.Symbol('c',integer=True)
z = sp.Symbol('z',complex=True)
t = sp.Symbol('t',complex=True)
sp.simplify(t-sp.summation((sp.exp(-d)*(d**c)/sp.gamma(c+1))/(z-c-a*t),(c,0,sp.oo)))
I then need to lambdify this expression, and unfortunately this becomes impossible to do.
With Matlab symbolic toolbox however I get the following answer:
Matlab
>> a=sym('a')
>> b=sym('b');
>> c=sym('c')
>> d=sym('d');
>> z=sym('z');
>> t=sym('t');
>> symsum((exp(-d)*(d^c)/factorial(c))/(z-c-a*t),c,0,inf)
ans =
(-d)^(z - a*t)*exp(-d)*(gamma(a*t - z) - igamma(a*t - z, -d))
The formula involves lower incomplete gamma functions, as expected.
Any idea why of this behaviour? I thought sympy was able to do this summation symbolically.
Running your code with SymPy 1.2 results in
d**(-a*t + z)*exp(-I*pi*a*t - d + I*pi*z)*lowergamma(a*t - z, d*exp_polar(I*pi)) + t
By the way, summation already attempts to evaluate the sum (and succeeds in case of SymPy 1.2), subsequent simplification is cosmetic. (And can sometimes be harmful).
The presence of exp_polar means that SymPy found it necessary to consider the points on the Riemann surface of logarithmic function instead of regular complex numbers. (Related bit of docs). The function lower_gamma is branched and so we must distinguish between "the value at -1, if we arrive to -1 from 1 going clockwise" from "the value at -1, if we arrive to -1 from 1 going counterclockwise". The former is exp_polar(-I*pi), the latter is exp_polar(I*pi).
All this is very interesting but not really helpful when you need concrete evaluation of the expression. We have to unpolarify this expression, and from what Matlab shows, simply replacing exp_polar with exp is a correct way to do so here.
rv = sp.simplify(t-sp.summation((sp.exp(-d)*(d**c)/sp.gamma(c+1))/(z-c-a*t),(c,0,sp.oo)))
rv = rv.subs(sp.exp_polar, sp.exp)
Result: d**(-a*t + z)*exp(-I*pi*a*t - d + I*pi*z)*lowergamma(a*t - z, -d) + t
There is still something to think about here, with complex numbers and so on. Is d positive or negative? What does raising it to the power -a*t+z mean, what branch of multivalued power function do we take? The same issues are present in Matlab output, where -d is raised to a power.
I recommend testing this with floating point input (direct summation of series vs evaluation of the SymPy expression for it), and adding assumptions on the sign of d if possible.

Why doesn't sympy support units with 0 magnitude?

The title says it all... why does sympy have the following behavior?
import sympy.physics.units as u
print(0*u.meter)
# >>> 0
And Pint has this behavior:
import pint
u = pint.UnitRegistry()
print(0*u.meter)
# >>> 0 meter
I think I prefer pint's behavior, because it allows for dimensional consistency. 0 is a proper magnitude of some unit. For instance, 0 degrees kelvin has a definitive meaning... it's not just the absence of anything...
So I realize the contributors of sympy probably chose this implementation for some reason. Can you help me see the light?
The discussion of reasons for implementation belongs on GitHub (where I raised this issue), not here. A short answer is that units are a bolted-on structure; the core of SymPy is not really unit-aware.
You can create 0*meter expression by passing evaluate=False parameter to Mul:
>>> Mul(0, u.meter, evaluate=False)
0*meter
However, it will become 0 if combined with something else.
>>> 3*Mul(0, u.meter, evaluate=False)
0
Wrapping in UnevaluatedExpr prevents the above, but causes more problems than it solves.
>>> 3*UnevaluatedExpr(Mul(0, u.meter, evaluate=False))
3*(0*meter)

Python-How to do Algebraic Math with Exponents and Square Roots

so I am having issues trying to do basic math in Python. I can do basic math, but when I add in exponents, square roots, etc, I have errors with the IDE. How do I do this?
Here are a few of my problems that I am having issues with:
(n(n-1))/2)
(4)* pi * r ** 2=
(r(cos(a)**2) + r(sin(b))**2)**(.5)
((y**2) - (y**1))/((x**2) - (x**1))=
(n*(n-1))/2 should work if you have already given n a numeric value (e.g. n=2 or something). Your original expression has unbalanced parentheses (three closing parens and only two opening parens) and it is missing the multiplication sign (*) which is necessary in python, otherwise n(n-1) would be interpreted as the function n supplied with the input n-1, in which case you get a message like "TypeError: 'int' object is not callable", assuming you had previously defined n=2 or the like. It's telling you that the integer n cannot be called like a function, which is how it interprets n().
To get pi (3.14159...) in python, you should import the math package and then use math.pi like this:
import math
r = 2
x = 4*math.pi*r**2
You don't need parentheses around the 4. Parentheses are used for grouping, when you need operations to be done in a different order than the standard order of operations. You don't need the trailing equal sign (that's a syntax error).
In the third expression you are using implicit multiplication notation which is fine for pencil and paper but in python you need to use * every time you multiply. Also, you need to import the math package and then use math.sin and math.cos.
import math
a = 90
b = 45
x = (r*(math.cos(a)**2) + r*(math.sin(b))**2)**(.5)
There doesn't appear to be anything wrong with the last expression except the trailing = sign which should be removed. Store the result of this expression in a variable if you want to keep it for future use:
z = ((y**2) - (y**1))/((x**2) - (x**1))
If you just type the expression at the command line it will print the result immediately:
x = 3
y = 2
((y**2) - (y**1))/((x**2) - (x**1))
but if you are using this expression in a script you want to save the result in a variable so you can use it later or print it out or something:
z = ((y**2) - (y**1))/((x**2) - (x**1))
As was previously pointed out in the comments, x**1 is the same, mathematically, as x so it's not clear why you would want to write it this way, but it's not wrong.
You can use math.pow() or the simpler ** syntax:
There are two ways to complete basic maths equations in python either:
use the ** syntax. e.g:
>>> 2 ** 4
16
>>> 3 ** 3
27
or use math.pow(). e.g:
>>> import math
>>> math.pow(5, 2)
25.0
>>> math.pow(36, 0.5)
6.0
As you can see, with both of these functions you can use any real power so negative for inverse or decimals for roots.
In general, for these types of equations, you want to look into the math module. It has lost of useful functions and defined constants that you may find useful. In particular for your specific problems: math.pi and these trig. functions.
I hope these examples and the links I made are useful for you :)

Categories