Numba JIT在numpy.where和as.type

时间:2018-03-02 17:19:59

标签: python numpy numba

我尝试使用JIT来使用nopython模式加速代码中的某些功能。这主要起作用,但会在以下函数中引发错误:

@jit (nopython =True)
def calc_density(pos, ncells, L):
    dx = L/ncells
    pos = pos/dx
    #plower = pos.astype(np.int)
    plower = np.int_(pos)

    offset = pos - plower
    opp_offset = 1- offset
    density = zeros((ncells),dtype=np.float64)
    for i in range(0,ncells):
        ind = where((plower == i),dtype=np.float64)
        density[(i+1)%ncells] += sum(offset[ind])
        density[i] += sum(opp_offset[ind])

    density *= float(ncells) / float(len(pos))
    return (density)

@jit (nopython =True)
def periodic_interp(y, x):
    ny = len(y)
    if len(x) > 1:
        y = array(y) # Make sure it's a NumPy array for array indexing
    xl = floor(x).astype(int) # Left index
    dx = x - xl
    xl = ((xl % ny) + ny) % ny  # Ensures between 0 and ny-1 inclusive

    return y[xl]*(1. - dx) + y[(xl+1)%ny]*dx

在第一个函数中,我得到了零部分,但这对np.where部分不起作用。 第一个函数的错误很长,但说明: [1]期间:解析被调用者类型:Function()

对于第二个函数,错误是:

  File "C:\Users\Knowhow\Anaconda3\lib\site-packages\numba\typing\npydecl.py", line 433, in _parse_nested_sequence
    raise TypingError("%r not allowed in a homogenous sequence" % typ)

TypingError: array(float64, 1d, C) not allowed in a homogenous sequence

任何人都可以建议这些解决方案,即使这意味着在没有np.where的情况下重写函数。 非常感谢。

以下完整代码:     #!/ usr / bin / env python     #     #一维循环域中的静电PIC代码

from numpy import arange, concatenate, zeros, linspace, floor, array, pi
from numpy import sin, cos, sqrt, random, histogram, where, sum
import numpy as np
import time

import matplotlib.pyplot as plt # Matplotlib plotting library

try:
    import matplotlib.gridspec as gridspec  # For plot layout grid
    got_gridspec = True
except:
    got_gridspec = False

# Need an FFT routine, either from SciPy or NumPy
try:
    from scipy.fftpack import fft, ifft
except:
    # No SciPy FFT routine. Import NumPy routine instead
    from numpy.fft import fft, ifft
from numba import jit, int64


def rk4step(f, y0, dt, args=()):
    """ Takes a single step using RK4 method """


    k1 = f(y0, *args)



    k2 = f(y0 + 0.5*dt*k1, *args)
    k3 = f(y0 + 0.5*dt*k2, *args)
    k4 = f(y0 + dt*k3, *args)

    return y0 + (k1 + 2.*k2 + 2.*k3 + k4)*dt / 6.

#@jit("f8[:](f8[:],u1,f4)")
#@jit (nopython =True)
def calc_density(pos, ncells, L):


    dx = L/ncells
    pos = pos/dx
    #plower = pos.astype(np.int)
    plower = np.int_(pos)

    offset = pos - plower
    opp_offset = 1- offset
    density = zeros((ncells),dtype=np.float64)



    for i in range(0,ncells):
        ind = np.where((plower == i))

        density[(i+1)%ncells] += sum(offset[ind])
        density[i] += sum(opp_offset[ind])

    density *= float(ncells) / float(len(pos))


    return (density)
#@jit (nopython =True)
#jit("f8[:](f8[:], f8[:])")
def periodic_interp(y, x):


    ny = len(y)
    if len(x) > 1:
        y = array(y) # Make sure it's a NumPy array for array indexing
    #xl = floor(x).astype(int) # Left index
    #d=(np.int64(p))
    xl = np.int_(floor(x))
    dx = x - xl
    xl = ((xl % ny) + ny) % ny  # Ensures between 0 and ny-1 inclusive



    return y[xl]*(1. - dx) + y[(xl+1)%ny]*dx


def fft_integrate(y):
    """ Integrate a periodic function using FFTs
    """
    n = len(y) # Get the length of y

    f = fft(y) # Take FFT
    # Result is in standard layout with positive frequencies first then negative
    # n even: [ f(0), f(1), ... f(n/2), f(1-n/2) ... f(-1) ]
    # n odd:  [ f(0), f(1), ... f((n-1)/2), f(-(n-1)/2) ... f(-1) ]

    if n % 2 == 0: # If an even number of points
        k = concatenate( (arange(0, n/2+1), arange(1-n/2, 0)) )
    else:
        k = concatenate( (arange(0, (n-1)/2+1), arange( -(n-1)/2, 0)) )
    k = 2.*pi*k/n

    # Modify frequencies by dividing by ik
    f[1:] /= (1j * k[1:]) 
    f[0] = 0. # Set the arbitrary zero-frequency term to zero

    return ifft(f).real # Reverse Fourier Transform

def pic(f, ncells, L):
    """ f contains the position and velocity of all particles
    """

    nparticles = len(f)//2     # Two values for each particle
    pos = f[0:nparticles] # Position of each particle
    vel = f[nparticles:]      # Velocity of each particle

    dx = L / float(ncells)    # Cell spacing

    # Ensure that pos is between 0 and L
    pos = ((pos % L) + L) % L

    # Calculate number density, normalised so 1 when uniform
    density = calc_density(pos, ncells, L)

    # Subtract ion density to get total charge density
    rho = density - 1.

    # Calculate electric field
    E = -fft_integrate(rho)*dx

    # Interpolate E field at particle locations
    accel = -periodic_interp(E, pos/dx)

    # Put back into a single array
    return concatenate( (vel, accel) )

####################################################################
#@jit("f8[:],f8[:](f8[:], f8[:],f8,u1,f8[:], f8[:], f4)")
def run(pos, vel, L, ncells=None, out=[], output_times=linspace(0,20,100), cfl=0.5):

    if ncells == None:
        ncells = int(sqrt(len(pos))) # A sensible default

    dx = L / float(ncells)

    f = concatenate( (pos, vel) )   # Starting state
    nparticles = len(pos)

    time = 0.0
    for tnext in output_times:
        # Advance to tnext
        stepping = True
        dt = cfl * dx / max(abs(vel))
        while stepping:
            # Maximum distance a particle can move is one cell

            if time + dt >= tnext:
                # Next time will hit or exceed required output time
                stepping = False
                dt = tnext - time
            #print "Time: ", time, dt
            f = rk4step(pic, f, dt, args=(ncells, L))
            time += dt

        # Extract position and velocities
        pos = ((f[0:nparticles] % L) + L) % L
        vel = f[nparticles:]

        # Send to output functions
        for func in out:
            func(pos, vel, ncells, L, time)

    return pos, vel

####################################################################
# 
# Output functions and classes
#

class Plot:
    """
    Displays three plots: phase space, charge density, and velocity distribution
    """
    def __init__(self, pos, vel, ncells, L):

        d = calc_density(pos, ncells, L)
        vhist, bins  = histogram(vel, int(sqrt(len(vel))))
        vbins = 0.5*(bins[1:]+bins[:-1])

        # Plot initial positions
        if got_gridspec:
            self.fig = plt.figure()
            self.gs = gridspec.GridSpec(4, 4)
            ax = self.fig.add_subplot(self.gs[0:3,0:3])
            self.phase_plot = ax.plot(pos, vel, '.')[0]
            ax.set_title("Phase space")

            ax = self.fig.add_subplot(self.gs[3,0:3])
            self.density_plot = ax.plot(linspace(0, L, ncells), d)[0]

            ax = self.fig.add_subplot(self.gs[0:3,3])
            self.vel_plot = ax.plot(vhist, vbins)[0]
        else:
            self.fig = plt.figure()
            self.phase_plot = plt.plot(pos, vel, '.')[0]

            self.fig = plt.figure()
            self.density_plot = plt.plot(linspace(0, L, ncells), d)[0]

            self.fig = plt.figure()
            self.vel_plot = plt.plot(vhist, vbins)[0]
        plt.ion()
        plt.show()

    def __call__(self, pos, vel, ncells, L, t):
        d = calc_density(pos, ncells, L)
        vhist, bins  = histogram(vel, int(sqrt(len(vel))))
        vbins = 0.5*(bins[1:]+bins[:-1])

        self.phase_plot.set_data(pos, vel) # Update the plot
        self.density_plot.set_data(linspace(0, L, ncells), d)
        self.vel_plot.set_data(vhist, vbins)
        plt.draw()
        plt.pause(0.05)

class Summary:
    def __init__(self):
        self.t = []
        self.firstharmonic = []

    def __call__(self, pos, vel, ncells, L, t):
        # Calculate the charge density
        d = calc_density(pos, ncells, L)

        # Amplitude of the first harmonic
        fh = 2.*abs(fft(d)[1]) / float(ncells)

        #print ("Time:", t, "First:", fh)

        self.t.append(t)
        self.firstharmonic.append(fh)

####################################################################
# 
# Functions to create the initial conditions
#

def landau(npart, L, alpha=0.2):
    """
    Creates the initial conditions for Landau damping

    """
    # Start with a uniform distribution of positions
    pos = random.uniform(0., L, npart)
    pos0 = pos.copy()
    k = 2.*pi / L
    for i in range(10): # Adjust distribution using Newton iterations
        pos -= ( pos + alpha*sin(k*pos)/k - pos0 ) / ( 1. + alpha*cos(k*pos) )

    # Normal velocity distribution
    vel = random.normal(0.0, 1.0, npart)

    return pos, vel

def twostream(npart, L, vbeam=2):
    # Start with a uniform distribution of positions
    pos = random.uniform(0., L, npart)
    # Normal velocity distribution
    vel = random.normal(0.0, 1.0, npart)

    np2 = int(npart / 2)
    vel[:np2] += vbeam  # Half the particles moving one way
    vel[np2:] -= vbeam  # and half the other

    return pos,vel

####################################################################

def main():
    average_time = []
    for i in range(1):
        start_time = time.perf_counter()
        L = 4.*pi
        pos, vel = landau(10000, L)


        s = Summary()

        run(pos, vel, L, 10, [s], linspace(0.,30,75))

        end_time = time.perf_counter()

        time_difference = end_time - start_time



        average_time.append(time_difference)


    print ("Average time is", np.round(np.mean(average_time),2), "+/-", np.round(np.std(average_time),2))

main()

0 个答案:

没有答案