信号处理功能的时间优化

时间:2018-03-20 09:32:29

标签: python performance numpy time-complexity signal-processing

我有一个程序进行了大量的迭代(数千到数百万到数亿)。它开始花费相当多的时间(几分钟,几天),尽管我努力优化它,但我仍然有点卡住。

个人资料:通过控制台电话使用cProfile

ncalls     tottime  percall  cumtime  percall filename:lineno(function)
    500/1    0.018    0.000  119.860  119.860 {built-in method builtins.exec}
        1    0.006    0.006  119.860  119.860 Simulations_profiling.py:6(<module>)
      6/3    0.802    0.134  108.302   36.101 Simulations_profiling.py:702(optimization)
    38147    0.581    0.000  103.411    0.003 Simulations_profiling.py:270(overlap_duo_combination)
   107691   28.300    0.000  102.504    0.001 Simulations_profiling.py:225(duo_overlap)
 12615015   69.616    0.000   69.616    0.000 {built-in method builtins.round}

第一个问题,是2个第一行是什么?我以为这是被调用的程序。

我将在round()语句中通过容差比较替换if / else方法,从而避免这种时间消耗。我想进一步优化以下两个功能,但我找不到新的方法。

import itertools
import numpy as np

class Signal:
    def __init__(self, fq, t0, tf, width=0.3):
        self.fq = fq                                    # frequency in Hz
        self.width = float(width)                       # cathodic phase width in ms
        self.t0 = t0                                    # Instant of the first pulse in ms
        self.tf = tf                                    # End point in ms

        # List of instant at which a stim pulse is triggered in ms
        self.timeline = np.round(np.arange(t0, self.tf, 1/fq*1000), 3)

    def find_closest_t(self, t):
        val = min(self.timeline, key=lambda x:abs(x-t))
        id = np.where(self.timeline==val)[0][0]

        if val <= t or id == 0:
            return val, id
        else:
            return self.timeline[id-1], id-1

def duo_overlap(S1, S2, perc):

    pulse_id_t1, pulse_id_t2 = [], []

    start = max(S1.t0, S2.t0) - max(S1.width, S2.width)
    if start <= 0:
        start = 0
        start_id_S1 = 0
        start_id_S2 = 0
    else:
        start_id_S1 = S1.find_closest_t(start)[1]
        start_id_S2 = S2.find_closest_t(start)[1]

    stop = min(S1.tf, S2.tf) + max(S1.width, S2.width)

    for i in range(start_id_S1, len(S1.timeline)):
        if S1.timeline[i] > stop:
            break

        for j in range(start_id_S2, len(S2.timeline)):
            if S2.timeline[j] > stop:
                break

            d = round(abs(S2.timeline[j] - S1.timeline[i]), 3)  # Computation of the distance of the 2 point

            if S1.timeline[i] <= S2.timeline[j] and d < S1.width:
                pulse_id_t1.append(i)
                pulse_id_t2.append(j)
                continue

            elif S1.timeline[i] >= S2.timeline[j] and d < S2.width:
                pulse_id_t1.append(i)
                pulse_id_t2.append(j)
                continue

            else:
                continue

    return pulse_id_t1, pulse_id_t2

def overlap_duo_combination(signals, perc=0):

    overlap = dict()
    for i in range(len(signals)):
        overlap[i] = list()

    for subset in itertools.combinations(signals, 2):
        p1, p2 = duo_overlap(subset[0], subset[1], perc = perc)
        overlap[signals.index(subset[0])] += p1
        overlap[signals.index(subset[1])] += p2

    return overlap

该计划的说明:

我的宽度为Signal.width且频率为Signal.fq的方波信号从Signal.t0开始,到Signal.tf结束。在初始化Signal实例期间,计算timeline:它是一个浮点数的1D数组,其中each number corresponds to the instant at which a pulse is triggered

示例:

IN: Signal(50, 0, 250).timeline
OUT: array([  0.,  20.,  40.,  60.,  80., 100., 120., 140., 160., 180., 200., 220., 240.])

A pulse is triggered at t = 0, t = 20, t = 40, ... Each pulse has a width of 0.3.

duo_overlap()在输入中输入2个信号(在本例中我们将保持固定为0的百分比。此函数计算S1和S2(时间线数组中的ID)脉冲的id彼此重叠。

示例:

  

如果S1的脉冲从t = 0开始,脉冲从t = 0.2开始   S2,因为0.2-0 = 0.2 < 0.3(S1​​。宽度),2个脉冲重叠。

我尝试通过仅循环可能重叠的ID(start_idstop)来优化此功能。但正如您所看到的,此功能仍然是真正的时间 - 由于通话次数过多而消费。

最后一个函数overlap_duo_combination()将输入中的N个信号作为列表(或元组/可迭代)(实际上2 <= N <= 6)并创建dict(),其中key是输入列表中信号的ID,该值是重叠脉冲ID列表(输入列表中信号的比较2乘2)。

示例:

  

输入:信号=(S1,S2,S3)S1的脉冲n°2与脉冲n°3重叠   S2的脉冲n°3与S3的脉冲n°5重叠。

     

输出:dict [0] = [2] / dict [1] = [3,3] / dict [2] = [5]

3为S2弹出两次,因为它将添加第一个瓦片duo_overlap()在S1和S2上调用,第二次在S2和S3上调用它。 我不想避免重复,因为它是关于有多少不同脉冲重叠的信息(在这种情况下,2个脉冲与S2的脉冲n°3重叠)。

您是否有任何想法,建议,代码或其他任何重复代码的时间复杂性?

我目前正在研究PyCUDA的实现,因为我有一个Nvidia 1080 Ti可供使用,但我不知道C语言。 是否值得切换到GPU这个内部函数,在调用时执行并不需要很长时间,但被调用数千次?

感谢您阅读这么长的帖子,感谢您的帮助!

1 个答案:

答案 0 :(得分:1)

如果我正确理解了您的问题,您可以依靠numpy而不是执行所有循环来加快duo_overlap()功能。

您想从S2.timeline中减去S1.timeline的所有值,并将差异与信号宽度进行比较。以下函数重复S1.timeline(重复为列)并从每行中减去S2.timeline。因此,行索引对应于S1.timeline,列索引对应于S2.timeline

def duo_overlap_np(S1, S2):
    x = S1.timeline
    y = S2.timeline

    mat = np.repeat(x, y.shape[0]).reshape(x.shape[0],y.shape[0])
    mat = mat - y

    # End of S1 pulse overlaps with start of S2 pulse
    overlap_1 = np.where((0 >= mat) & (mat >= -S1.width))

    # End of S2 pulse overlaps with start of S1 pulse
    overlap_2 = np.where((0 <= mat) & (mat <= S2.width))

    # Flatten the overlap arrays. The first element returned by np.where
    # corresponds to S1, the second to S2
    S1_overlap = np.concatenate([overlap_1[0], overlap_2[0]])
    S2_overlap = np.concatenate([overlap_1[1], overlap_2[1]])

    return S1_overlap, S2_overlap

在我的机器上进行快速比较,

S1 = Signal(50, 0, 1000, width=0.3)
S2 = Signal(25.5, 20.2, 1000, width=0.6)

%timeit duo_overlap(S1, S2, 0)
# 7 ms ± 284 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit duo_overlap_np(S1, S2)
# 38.2 µs ± 2.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)