我有以下的python代码:

import matplotlib.pyplot as plt
import numpy as np


class bifurcation_diagram(object):
    def __init__(self):
        self.omega = []
        self.theta = []
        self.dt = (2 * np.pi / (2.0 / 3.0)) / 600
        self.time = []
        self.theta_in_bifurcation_diagram = []
        self.F_D = np.arange(1.35,1.5,0.001)
        self.theta_versus_F_D = []
    def calculate(self):
        l = 9.8
        g = 9.8
        q = 0.5
        Omega_D = 2.0 / 3.0
        for f_d in self.F_D:
            self.omega.append([0])
            self.theta.append([0.2])
            self.time.append([0])
            for i in range(600000):
                k1_theta = self.dt * self.omega[-1][-1]
                k1_omega = self.dt * ((-g / l) * np.sin(self.theta[-1][-1]) - q * self.omega[-1][-1] + f_d * np.sin(Omega_D * self.time[-1][-1]))
                k2_theta = self.dt * (self.omega[-1][-1] + 0.5 * k1_omega)
                k2_omega = self.dt * ((-g / l) * np.sin(self.theta[-1][-1] + 0.5 * k1_theta) - q * (self.omega[-1][-1] + 0.5 * k1_omega) + f_d * np.sin(Omega_D * (self.time[-1][-1] + 0.5 * self.dt)))
                k3_theta = self.dt * (self.omega[-1][-1] + 0.5 * k2_omega)
                k3_omega = self.dt * ((-g / l) * np.sin(self.theta[-1][-1] + 0.5 * k2_theta) - q * (self.omega[-1][-1] + 0.5 * k2_omega) + f_d * np.sin(Omega_D * (self.time[-1][-1] + 0.5 * self.dt)))
                k4_theta = self.dt * (self.omega[-1][-1] + k3_omega)
                k4_omega = self.dt * ((-g / l) * np.sin(self.theta[-1][-1] + k3_theta) - q * (self.omega[-1][-1] + k3_omega) + f_d * np.sin(Omega_D * (self.time[-1][-1] + self.dt)))
                temp_theta = self.theta[-1][-1] + (1.0 / 6.0) * (k1_theta + 2 * k2_theta + 2 * k3_theta + k4_theta)
                temp_omega = self.omega[-1][-1] + (1.0 / 6.0) * (k1_omega + 2 * k2_omega + 2 * k3_omega + k4_omega)
                while temp_theta > np.pi:
                    temp_theta -= 2 * np.pi
                while temp_theta < -np.pi:
                    temp_theta += 2 * np.pi
                self.omega[-1].append(temp_omega)
                self.theta[-1].append(temp_theta)
                self.time[-1].append(self.dt * i)
            for i in range(500,1000):
                n = i * 600
                self.theta_in_bifurcation_diagram.append(self.theta[-1][n])
                self.theta_versus_F_D.append(f_d)
    def show_results(self):
        plt.plot(self.theta_versus_F_D,self.theta_in_bifurcation_diagram,'.')
        plt.title('Bifurcation diagram' + '\n' + r'$\theta$ versus $F_D$')
        plt.xlabel(r'$F_D$')
        plt.ylabel(r'$\theta$ (radians)')
        plt.xlim(1.35,1.5)
        plt.ylim(1,3)
        plt.show()

        
bifurcation = bifurcation_diagram()
bifurcation.calculate()
bifurcation.show_results()

我希望把它做得更紧凑,做一个彩色的情节,同时提高它的效率.在这方面的任何帮助都将是真正有益的.

我原以为代码会运行得很快,但它需要15分钟以上才能运行.

推荐答案

不是为这样的代码创建的,因为使用CPython时,它通常是interpreted%(这是为了粘合代码和脚本而设计的),而且CPython(几乎)不执行代码优化,因此会重新计算重复的表达式.加速这类代码的通常方法是使用Numpy个在大型数组(而不是列表)上操作的函数.这被称为vectorization.这就是说,这个代码非常昂贵,使用Numpy可能还不够,在这里使用Numpy也是一项不可忽视的工作.另一种解决方案是使用NumbaSO编译这段代码(这要归功于JIT编译器).请注意,Numba主要用于加速主要使用Numpy和基本数值计算的代码(而不是像绘图这样的通用代码).

要做的第一件事是获取列表,并将其替换为Numpyarray.可以以合适的大小预先分配数组,以避免昂贵的append调用.然后,计算功能应该从类中移出,以便Numba易于使用.然后,可以将两个while循环替换为np.remainder.最后,您可以使用多线程计算parallel中的每一行array.

以下是生成的代码(几乎没有经过测试):


import matplotlib.pyplot as plt
import numpy as np
import numba as nb

@nb.njit('(float64[::1], float64)', parallel=True)
def compute_numba(F_D, dt):
    l = 9.8
    g = 9.8
    q = 0.5
    Omega_D = 2.0 / 3.0
    size = 600000
    loop2_start, loop2_end = 500, 1000
    loop2_stride = 600
    omega = np.empty((F_D.size, size+1), dtype=np.float64)
    theta = np.empty((F_D.size, size+1), dtype=np.float64)
    time = np.empty((F_D.size, size+1), dtype=np.float64)
    theta_in_bifurcation_diagram = np.empty((loop2_end-loop2_start) * F_D.size, dtype=np.float64)
    theta_versus_F_D = np.empty((loop2_end-loop2_start) * F_D.size, dtype=np.float64)
    for i in nb.prange(F_D.size):
        f_d = F_D[i]
        omega[i, 0] = 0.0
        theta[i, 0] = 0.2
        time[i, 0] = 0.0
        for j in range(size):
            cur_omega = omega[i,j]
            cur_theta = theta[i,j]
            cur_time = time[i,j]
            tmp = np.sin(Omega_D * (cur_time + 0.5 * dt))
            k1_theta = dt * cur_omega
            k1_omega = dt * ((-g / l) * np.sin(cur_theta) - q * cur_omega + f_d * np.sin(Omega_D * cur_time))
            k2_theta = dt * (cur_omega + 0.5 * k1_omega)
            k2_omega = dt * ((-g / l) * np.sin(cur_theta + 0.5 * k1_theta) - q * (cur_omega + 0.5 * k1_omega) + f_d * tmp)
            k3_theta = dt * (cur_omega + 0.5 * k2_omega)
            k3_omega = dt * ((-g / l) * np.sin(cur_theta + 0.5 * k2_theta) - q * (cur_omega + 0.5 * k2_omega) + f_d * tmp)
            k4_theta = dt * (cur_omega + k3_omega)
            k4_omega = dt * ((-g / l) * np.sin(cur_theta + k3_theta) - q * (cur_omega + k3_omega) + f_d * np.sin(Omega_D * (cur_time + dt)))
            temp_theta = cur_theta + (1.0 / 6.0) * (k1_theta + 2 * k2_theta + 2 * k3_theta + k4_theta)
            temp_omega = cur_omega + (1.0 / 6.0) * (k1_omega + 2 * k2_omega + 2 * k3_omega + k4_omega)
            temp_theta = np.remainder(temp_theta + np.pi, 2 * np.pi) - np.pi
            omega[i,j+1] = temp_omega
            theta[i,j+1] = temp_theta
            time[i,j+1] = dt * j
        for k, j in enumerate(range(loop2_start, loop2_end)):
            n = j * loop2_stride
            offset = (loop2_end - loop2_start) * i + k
            theta_in_bifurcation_diagram[offset] = theta[i,n]
            theta_versus_F_D[offset] = f_d
    return (omega, theta, time, theta_in_bifurcation_diagram, theta_versus_F_D)

class bifurcation_diagram(object):
    def __init__(self):
        self.omega = np.array([])
        self.theta = np.array([])
        self.dt = (2 * np.pi / (2.0 / 3.0)) / 600
        self.time = np.array([])
        self.theta_in_bifurcation_diagram = np.array([])
        self.F_D = np.arange(1.35,1.5,0.001)
        self.theta_versus_F_D = np.array([])
    def calculate(self):
        self.omega, self.theta, self.time, self.theta_in_bifurcation_diagram, self.theta_versus_F_D = compute_numba(self.F_D, self.dt)
    def show_results(self):
        plt.plot(self.theta_versus_F_D,self.theta_in_bifurcation_diagram,'.')
        plt.title('Bifurcation diagram' + '\n' + r'$\theta$ versus $F_D$')
        plt.xlabel(r'$F_D$')
        plt.ylabel(r'$\theta$ (radians)')
        plt.xlim(1.35,1.5)
        plt.ylim(1,3)
        plt.show()

        
bifurcation = bifurcation_diagram()
bifurcation.calculate()
bifurcation.show_results()

在我的6核i5-9600KF处理器上,这段代码只需要1.07秒,而不是最初的19分20秒!因此,这个新代码大约是1150 times faster

此外,我还发现生成的代码更易于阅读.

我建议你判断一下结果,尽管乍一看它们看起来很好. 事实上,以下是我到目前为止得到的结果图:

enter image description here

Python相关问答推荐

opencv Python稳定的图标识别

如何在具有重复数据的pandas中对groupby进行总和,同时保留其他列

对Numpy函数进行载体化

为什么tkinter框架没有被隐藏?

try 在树叶 map 上应用覆盖磁贴

为什么默认情况下所有Python类都是可调用的?

对所有子图应用相同的轴格式

driver. find_element无法通过class_name找到元素'""

Scrapy和Great Expectations(great_expectations)—不合作

在单个对象中解析多个Python数据帧

我的字符串搜索算法的平均时间复杂度和最坏时间复杂度是多少?

无论输入分辨率如何,稳定扩散管道始终输出512 * 512张图像

lityter不让我输入左边的方括号,'

python中csv. Dictreader. fieldname的类型是什么?'

OpenCV轮廓.很难找到给定图像的所需轮廓

如何在Python中使用Iscolc迭代器实现观察者模式?

简单 torch 模型测试:ModuleNotFoundError:没有名为';Ultralytics.yolo';

每次查询的流通股数量

如何在Python中从html页面中提取html链接?

如何在Polars中创建条件增量列?