<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 We don't need your loop
by serge_sans_paille
Quarkslab / Télécom Bretagne / Namek
PyConFR 2015
Une brève histoire de la boucle
Assembleur
.L3
Step1:  Fortran style
Step2:  APL style
Step3:  Une préférence ?
Question de performances
Boucles explicites
Step4:  We don't need your loops!
Un appel de fonction a plus de sens qu'une boucle pour un humain
Un appel de fonction a plus de sens qu'une boucle pour un compilateur
Exemples de boucles amenées à disparaitre
Step5:  Les scientifiquent codent en Numpy de haut niveau !
<ASSISTANT_TASK:>
Python Code:
import numpy
n = 10
A = numpy.random.random(n)
print(A)
Explanation: We don't need your loop
by serge_sans_paille
Quarkslab / Télécom Bretagne / Namek
PyConFR 2015
Une brève histoire de la boucle
Assembleur
.L3:
    addsd   (%rsi), %xmm0
    addq    $8, %rsi
    cmpq    %rax, %rsi
    jne .L3
FORTRAN
fortran
      do 10 i = 1, n
         sum = sum + A(i)
  10  continue
C
c
for(unsigned i = 0; i &lt; n ; ++i)
  sum += A[i];
APL
apl
+/ A
Python
End of explanation
s = 0
for i in range(n):
    s += A[i]
print(s)
Explanation: Fortran style
End of explanation
s = numpy.sum(A)
print(s)
Explanation: APL style
End of explanation
n = 1000000
A = numpy.random.random(n)
def explicit_sum(seq):
    s = 0
    for elem in seq:
        s += elem ** 2
    return s
%timeit explicit_sum(A)
%timeit numpy.sum(A**2)
### Cython
%load_ext Cython
%%cython
cimport numpy as np
cimport cython
@cython.boundscheck(False)
@cython.wraparound(False)
def cython_sum(np.ndarray[double, ndim=1] A):
    cdef double s = 0
    cdef int i, n
    n = len(A)
    for i in range(n):
        s += A[i] * A[i]
    return s
%timeit cython_sum(A)
%%file pythran_sum.py
#pythran export pythran_sum(float64[])
import numpy
def pythran_sum(A):
    return numpy.sum(A**2)
!python -m pythran.run pythran_sum.py -DUSE_BOOST_SIMD -O3 -march=native
!python -m timeit -s 'from pythran_sum import pythran_sum; import numpy; n = 1000000 ; A = numpy.random.random(n)' 'pythran_sum(A)'
Explanation: Une préférence ?
Question de performances
Boucles explicites
End of explanation
n, m = 100, 200
A = numpy.random.random((n,m))
s = 0.
for i in range(1, n-1):
    for j in range(1, m-1):
        s += A[i,j]
print(s)
print(numpy.sum(A[1:-1, 1:-1]))
s = 0
for i in range(n):
    for j in range(m):
        if A[i,j] < .5:
            s += 1
print(s)
print(numpy.sum(A < .5))
s = 0
B = numpy.empty_like(A)
for i in range(n):
    for j in range(m):
        if A[i,j] < .5:
            B[i,j] = A[i,j]
        else:
            B[i,j] = 0.
print(B)
print(numpy.where(A<.5,A,0.))
### Piège!
n = 100
B = numpy.arange(100)
shift = 3
for i in range(shift, n):
        B[i] = 1 + B[i - shift]
print(B)
B = numpy.arange(100)
B[shift:] = 1 + B[:-shift]
print(B)
Explanation: We don't need your loops!
Un appel de fonction a plus de sens qu'une boucle pour un humain
Un appel de fonction a plus de sens qu'une boucle pour un compilateur
Exemples de boucles amenées à disparaitre
End of explanation
%%file grayscott.py
#pythran export GrayScott(int, float, float, float, float)
import numpy as np
def GrayScott(counts, Du, Dv, F, k):
    n = 300
    U = np.zeros((n+2,n+2), dtype=np.float32)
    V = np.zeros((n+2,n+2), dtype=np.float32)
    u, v = U[1:-1,1:-1], V[1:-1,1:-1]
    r = 20
    u[:] = 1.0
    U[n/2-r:n/2+r,n/2-r:n/2+r] = 0.50
    V[n/2-r:n/2+r,n/2-r:n/2+r] = 0.25
    u += 0.15*np.random.random((n,n))
    v += 0.15*np.random.random((n,n))
    for i in range(counts):
        Lu = (                 U[0:-2,1:-1] +
              U[1:-1,0:-2] - 4*U[1:-1,1:-1] + U[1:-1,2:] +
                               U[2:  ,1:-1] )
        Lv = (                 V[0:-2,1:-1] +
              V[1:-1,0:-2] - 4*V[1:-1,1:-1] + V[1:-1,2:] +
                               V[2:  ,1:-1] )
        uvv = u*v*v
        u += Du*Lu - uvv + F*(1 - u)
        v += Dv*Lv + uvv - (F + k)*v
    return V
from grayscott import GrayScott
%timeit GrayScott(40, 0.16, 0.08, 0.04, 0.06)
%%cython
cimport cython
import numpy as np
cimport numpy as np
cpdef CythonGrayScott(int counts, double Du, double Dv, double F, double k):
    cdef int n = 300
    cdef np.ndarray U = np.zeros((n+2,n+2), dtype=np.float_)
    cdef np.ndarray V = np.zeros((n+2,n+2), dtype=np.float_)
    cdef np.ndarray u = U[1:-1,1:-1]
    cdef np.ndarray v = V[1:-1,1:-1]
    cdef int r = 20
    u[:] = 1.0
    U[n/2-r:n/2+r,n/2-r:n/2+r] = 0.50
    V[n/2-r:n/2+r,n/2-r:n/2+r] = 0.25
    u += 0.15*np.random.random((n,n))
    v += 0.15*np.random.random((n,n))
    cdef np.ndarray Lu = np.zeros_like(u)
    cdef np.ndarray Lv = np.zeros_like(v)
    cdef int i, c, r1, c1, r2, c2
    cdef double uvv
    cdef double[:, ::1] bU = U
    cdef double[:, ::1] bV = V
    cdef double[:, ::1] bLu = Lu
    cdef double[:, ::1] bLv = Lv
    for i in range(counts):
        for r in range(n):
            r1 = r + 1
            r2 = r + 2
            for c in range(n):
                c1 = c + 1
                c2 = c + 2
                bLu[r,c] = bU[r1,c2] + bU[r1,c] + bU[r2,c1] + bU[r,c1] - 4*bU[r1,c1]
                bLv[r,c] = bV[r1,c2] + bV[r1,c] + bV[r2,c1] + bV[r,c1] - 4*bV[r1,c1]
        for r in range(n):
            r1 = r + 1
            for c in range(n):
                c1 = c + 1
                uvv = bU[r1,c1]*bV[r1,c1]*bV[r1,c1]
                bU[r1,c1] += Du*bLu[r,c] - uvv + F*(1 - bU[r1,c1])
                bV[r1,c1] += Dv*bLv[r,c] + uvv - (F + k)*bV[r1,c1]
    return V
%timeit GrayScott(40, 0.16, 0.08, 0.04, 0.06)
%timeit CythonGrayScott(40, 0.16, 0.08, 0.04, 0.06)
!python -m pythran.run -O3 -march=native grayscott.py -o pythran_grayscott.so
! python -m timeit -s 'from pythran_grayscott import GrayScott' 'GrayScott(40, 0.16, 0.08, 0.04, 0.06)'
Explanation: Les scientifiquent codent en Numpy de haut niveau !
End of explanation
<END_TASK>