Basic Python code works differently on new machine (Mac) - python

I just started my new M1 MacBook and since Python2.7 is preinstalled, I wanted to run a little harmonic series program in the terminal to compare to my old Mac (old Intel). A silly test, I know.
import time
t_1=time.time()
x=1
i=1
while x<20:
i+=1
x+=1/i
if i % 1e6 == 0: print(i,x);
print(i)
t_2=time.time()
print(str(t_2-t_1)+' s')
It turned out that only on the M1 Mac the program was not running as expected, since x did not increase. I had to change x+=1/i to x+=1/float(i) so x was understood as a float and not 'rounded' to 1 (staying an int). I thought that, while the latter is actual the more correct way to program, python is flexible with variables, and my most important question is of course: Why does this work differently on different machines?

In python2.7 the / is integer division. If you import
from __future__ import division
at the top of your file, then you can use / for float division.
If you still want to be able to use integer division, you can always use //.

Related

Running something in a method takes much more depending on the data types

Introduction
Today I found a weird behaviour in python while running some experiments with exponentiation and I was wondering if someone here knows what's happening. In my experiments, I was trying to check what is faster in python int**int or float**float. To check that I run some small snippets, and I found a really weird behaviour.
Weird results
My first approach was just to write some for loops and prints to check which one is faster. The snipper I used is this one
import time
# Run powers outside a method
ti = time.time()
for i in range(EXPERIMENTS):
x = 2**2
tf = time.time()
print(f"int**int took {tf-ti:.5f} seconds")
ti = time.time()
for i in range(EXPERIMENTS):
x = 2.**2.
tf = time.time()
print(f"float**float took {tf-ti:.5f} seconds")
After running it I got
int**int took 0.03004
float**float took 0.03070 seconds
Cool, it seems that data types do not affect the execution time. However, since I try to be a clean coder I refactored the repeated logic in a function power_time
import time
# Run powers in a method
def power_time(base, exponent):
ti = time.time()
for i in range(EXPERIMENTS):
x = base ** exponent
tf = time.time()
return tf-ti
print(f"int**int took {power_time(2, 2):.5f} seconds")
print(f"float**float took {power_time(2., 2.):5f} seconds")
And what a surprise of mine when I got these results
int**int took 0.20140 seconds
float**float took 0.05051 seconds
The refactor didn't affect a lot the float case, but it multiplied by ~7 the time required for the int case.
Conclusions and questions
Apparently, running something in a method can slow down your process depending on your data types, and that's really weird to me.
Also, if I run the same experiments but change ** by * or + the weird results disappear, and all the approaches give more or less the same results
Does someone know why is this happening? Am I missing something?
Apparently, running something in a method can slow down your process depending on your data types, and that's really weird to me.
It would be really weird if it was not like this! You can write your class that has it's own ** operator (through implementing the __pow__(self, other) method), and you could, for example, sleep 1s in there. Why should that take as long as taking a float to the power of another?
So, yeah, Python is a dynamically typed language. So, the operations done on data depend on the type of that data, and things can generally take different times.
In your first example, the difference never arises, because a) most probably the values get cached, because right after parsing it's clear that 2**2 is a constant and does not need to get evaluated every loop. Even if that's not the case, b) the time it costs to run a loop in python is hundreds of times that it takes to actually execute the math here – again, dynamically typed, dynamically named.
base**exponent is a whole different story. None about this is constant. So, there's actually going to be a calculation every iteration.
Now, the ** operator (__rpow__ in the Python data model) for Python's built-in float type is specified to do the float exponent (which is something implemented in highly optimized C and assembler), as exponentiation can elegantly be done on floating point numbers. Look for nb_power in cpython's floatobject.c. So, for the float case, the actual calculation is "free" for all that matters, again, because your loop is limited by how much effort it is to resolve all the names, types and functions to call in your loop. Not by doing the actual math, which is trivial.
The ** operator on Python's built-in int type is not as neatly optimized. It's a lot more complicated – it needs to do checks like "if the exponent is negative, return a float," and it does not do elementary math that your computer can do with a simple instruction, it handles arbitrary-length integers (remember, a python integer has as many bytes as it needs. You can save numbers that are larger than 64 bit in a Python integer!), which comes with allocation and deallocations. (I encourage you to read long_pow in CPython's longobject.c; it has 200 lines.)
All in all, integer exponentiation is expensive in python, because of python's type system.

Raise Integers to negative integer powers in Python 2.7

I'm having a really hard time translating this Matlab code to Python.
I'll show you my effort so far.
This is the matlab code
Sigma=BW1/(2*(2*(-log(10^(att_bw/10)))^(1/Order))^(1/2))
Now I tried to used Python power operator as I studied earlier this morning **
My code is
BW1 = np.array([100])
att_bw = np.array([-3])
Order = np.array([1])
Sigma = BW1/(2*(2*(-np.log(10**(att_bw[0]/10)))**(1/Order))**(1/2))
However it says that it cannot handle negative powers unfortunately
The result for sigma should be 42.539
EDIT: it seems my code runs perfectly fine in Python 3. However I'm stuck with Python 2.7. So is there any easy way to port it?
In python2 you need to make sure you use floating point numbers. To make them so, add . after each integer you now have in your formula.
Like this:
import numpy as np
BW1 = np.array([100])
att_bw = np.array([-3])
Order = np.array([1])
Sigma = BW1/(2.*(2.*(-np.log(10.**(att_bw[0]/10.)))**(1./Order))**(1./2.))
print Sigma
Output
[42.53892736]

Maximum value of kernel statistics - python

Question: What is the maximum value of kernel statistic counters and how can I handle it in python code?
Context: I calculate some statistics based on kernel statistics (e.g. /proc/partitions - it'll be customized python iostat version). But I have a problem with overflowed values - negative values. Original iostat code https://github.com/sysstat/sysstat/blob/master/iostat.c comments:
* Counters overflows are possible, but don't need to be handled in
* a special way: The difference is still properly calculated if the
* result is of the same type as the two values.
My language is python and I need to care about overflow in my case. Probably it depends also on architecture (32/64). I've tried 2^64-1 (64bit system), but no success.
The following function will work for 32-bit counters:
def calc_32bit_diff(old, new):
return (new - old + 0x100000000) % 0x100000000
print calc_32bit_diff(1, 42)
print calc_32bit_diff(2147483647, -2147483648)
print calc_32bit_diff(-2147483648, 2147483647)
This obviously won't work is the counter wraps around more than once between two consecutive reads (but then no other method would work either, since the information has been irrecoverably lost).
Writing a 64-bit version of this is left as an exercise for the reader. :)

Calculating pi in python - Leibnitz

I am starting my adventure with Python. My current program is very simple, it has to calculate pi using Leibnitz formula, and stop working when module from "a" varibale is less than x. So far, it looks like this:
from math import fabs
from math import pi
x=float(input('Enter accuracy of calculating:'))
sum=0
a=0
n=0
while True:
a=float(((-1)**n)/(2*n+1))
sum+=a
n+=1
result=sum*4
if fabs(a)<x:
break
print('Calculated pi:',result)
print('Imported pi:', pi)
It looks ok, but here's the problem:
In my version of Geanie it works great, but on my friend's Geanie - it calculates 0.0.
Also, on Ideone.com (without keyboard input, just e.g. x=0.0001) it also returns 0.0.
Does anyone knows where the problem is?
Try this
a=((-1)**n)/float(2*n+1)
instead of this
a=float(((-1)**n)/(2*n+1))
Reason: I don't see a point to setting a itself to be a float, but setting the divisor or dividend in the division that produces a will ensure that Python doesn't cut the remainder (which is the default for integer division before Python 3.0).
Side issue: Your style doesn't match the official Python style guidelines, so you might want to change that.

Floating point calculations debugging

So I recently decided to learn python and as a exercise (plus making something useful) I decided to make a Euler's Modified Method algorithm for solving higher-then-first order differential equations. An example input would be:
python script_name.py -y[0] [10,0]
where the first argument is the deferential equation (here: y''=-y), and the second one the initial conditions (here: y(0)=10, y'(0)=0). It is then meant to out put the resusts to two files (x-data.txt, and y-data.txt).
Heres the problem:
When in run the code with the specified the final line (at t=1) reads -0.0, but if you solve the ODE (y=10*cos(x)), it should read 5.4. Even if you go through the program with a pen and paper and execute the code, your (and the computers) results apart to diverge by the second iteration). Any idea what could have caused this?
NB: I'm using python 2.7 on a os x
Here's my code:
#! /usr/bin/python
# A higher order differential equation solver using Euler's Modified Method
import math
import sys
step_size = 0.01
x=0
x_max=1
def derivative(x, y):
d = eval(sys.argv[1])
return d
y=eval(sys.argv[2])
order = len(y)
y_derivative=y
xfile = open('x-data.txt','w+')
yfile = open('y-data.txt','w+')
while (x<x_max):
xfile.write(str(x)+"\n")
yfile.write(str(y[0])+"\n")
for i in range(order-1):
y_derivative[i]=y[(i+1)]
y_derivative[(order-1)] = derivative(x,y)
for i in range(order):
y[i]=y[i]+step_size*y_derivative[i]
x=x+step_size
xfile.close()
yfile.close()
print('done')
When you say y_derivative=y they are the SAME list with different names. I.e. when you change y_derivative[i]=y[i+1] both lists are changing. You want to use y_derivative=y[:] to make a copy of y to put in y_derivative
See How to clone or copy a list? for more info
Also see http://effbot.org/zone/python-list.htm
Note, I was able to debug this in IDLE by replacing sys.argv with your provided example. Then if you turn on the debugger and step through the code, you can see both lists change.

Categories

Resources