簡體   English   中英

在 python/numpy/scipy 中生成低差異准隨機序列?

[英]Generating low discrepancy quasi-random sequences in python/numpy/scipy?

已經有一個關於此的問題,但答案包含一個斷開的鏈接,並且已經兩年多了,我希望現在有更好的解決方案:)

低差異准隨機序列,例如 Sobol 序列,比均勻隨機序列更均勻地填充空間。 有沒有好的/簡單的方法在python中生成它們?

我認為 Python 中低差異序列的最佳替代方案是 Sensitivity Analysis Library (SALib):

https://github.com/SALib/SALib

我認為這是一個活躍的項目,您可以聯系作者檢查您需要的功能是否已經實現。 如果這不能解決您的問題,Corrado Chisari 將在 Matlab(由 John Burkardt)中制作的 SOBOL 版本移植到 Python,您可以在此處訪問它:

http://people.sc.fsu.edu/~jburkardt/py_src/sobol/sobol.html

有人清理了這些源中的注釋,並將它們放入文檔字符串的格式中。 它更具可讀性,您可以在此處訪問它:

https://github.com/naught101/sobol_seq

Chaospy也是一個有效的選擇。 可以選擇多種方法進行低差異采樣(包括“Sobol、拉丁超立方體等)-有關更多詳細信息,請參閱文檔

Scipy 現在有這個選項http://scipy.github.io/devdocs/generated/scipy.stats.qmc.Sobol.html

PyTorch 還證明了生成 sobol 隨機數的選項。 它允許高達 ~1k 的維度,並且可以選擇開啟加擾。 https://pytorch.org/docs/stable/generated/torch.quasirandom.SobolEngine.html

我認為現在最簡單的方法(從 SciPy 版本 >= 1.7.1 開始)就是我在這里的做法。 注意我是為期權定價寫的; 它適用於多達 21,201 個維度,因為他們實現了 Joe 和 Kuo 算法,這是您無需支付商業(非開源)版本即可獲得的最大維度數。 https://web.maths.unsw.edu.au/~fkuo/sobol/請注意,此例程可能非常緩慢(由於 ndtri,或點到沖擊的逆正態分布轉換),尤其是在高維度中。 無論如何,這至少可以讓您直接在 Python 代碼中生成點。 所以我正在做一些你的用例可能不需要的事情(我還沒有找到一種邏輯方法來從生成的 Owen 加擾 Sobol 點中刪除/替換 0 和 1,所以我只是用數字替換它們的序列這不會導致生成 -Inf 或 Inf,這會發生在許多高維度的 RQMC 點上 - 這些數字在我選擇它們的方式中是任意的;基本上我生成了一個具有 65,536 個模擬和“高”維度的 RQMC 序列,然后檢查了最小值和最大值的加擾點,這些點與這些值最接近,但略接近 0 或 1,因此它是“有點”邏輯選擇)。 另外,在 QMCgenerate 例程中,我跳過了第一點(即 0)——雖然這很常見,但有些論文建議不要這樣做(但我還沒有看到好的替代方案,如果你有的話,請隨意評論)。 我轉置它們只是為了稍后將它們粘貼到 Excel 中並檢查生成的沖擊。 不管怎樣,希望那些需要這個算法的人發現它有用。

from scipy.stats import qmc # needs SciPy >= 1.7.1
from scipy.special import ndtri
import numpy as np
import timeit

Total_periods = 252
N_assets = 12

# need a pow(2,m) loop to run until pow(2,m) >= sims IF using base2
sims = 8096 

dimensions = N_assets*Total_periods

def RQMCgenerate (dimensions, sims, seed):
    m=10 # start at 1024 sims
    #sims = pow(2,m) # RQMC Sobol needs a power of 2 for simulations
    while pow(2,m) < sims: #m = 17 # 131,072 sims; M = 16 # 65,536 sims
        m = m+1
    start_time = timeit.default_timer()
    RQMCgenerator = qmc.Sobol(dimensions, scramble=True, seed=seed)
    RQMCsamples = RQMCgenerator.random_base2(m)
    print('\n' + 'Time after sample generation RQMC:', (timeit.default_timer() - start_time), 'seconds'); 
    #clean up outliers for 0, 1 where normsinv(0) = -Inf and normsinv(1) = Inf
    RQMCsamples[RQMCsamples<=0]=0.000001 #arbitrary point change
    RQMCsamples[RQMCsamples>=1]=0.999999 #arbitrary point change
    #END clean up outliers for 0, 1 
    sobol = ndtri(RQMCsamples).T # get normsinv(points) and transpose to dimensions * sims 
    del RQMCsamples
    print('\n' + 'Time after ndtri (normsinv) of', sims,'sims x dimensions', dimensions, 'Randomized Sobol points): ', (timeit.default_timer() - start_time), 'seconds');
    return sobol

def QMCgenerate(dimensions, sims):
    start_time = timeit.default_timer()
    QMCgenerator = qmc.Sobol(dimensions, scramble=False)
    QMCgenerator.fast_forward(1) #skip first point where normsinv(0) = -Inf
    QMCsamples = QMCgenerator.random(sims) #this generates points not having to be powers of 2
    print('\n' + 'Time after sample generation QMC:', (timeit.default_timer() - start_time), 'seconds'); 
    sobol = ndtri(QMCsamples).T # get normsinv(points) and transpose to dimensions * sims
    del QMCsamples
    print('\n' + 'Time after ndtri (normsinv) of', sims,'sims x dimensions', dimensions, 'Sobol points):', (timeit.default_timer() - start_time), 'seconds');
    return sobol

RQMCsobol = RQMCgenerate(dimensions, sims, seed=0)
sobol = QMCgenerate(dimensions, sims)

樣本生成 RQMC 后的時間:0.4269224999952712 秒

ndtri (normsinv) of 8096 sims x 維度 3024 Randomized Sobol 點之后的時間:1.0048970999996527 秒

樣本生成 QMC 后的時間:0.0630135999963386 秒

8096 sims x 尺寸 3024 Sobol 點的 ndtri (normsinv) 之后的時間:0.5444753999981913 秒

這在更高維度上變得更慢:

樣本生成后的時間 RQMC:2.1779929000040283 秒

ndtri (normsinv) of 131072 sims x 維度 3024 Randomized Sobol 點之后的時間:10.617904700004146 秒

樣本生成 QMC 后的時間:1.079756200000702 秒

131072 sims x 尺寸 3024 Sobol 點的 ndtri (normsinv) 之后的時間:9.545934699999634 秒

我認為現在最簡單的方法(從 SciPy 版本 >= 1.7.1 開始)就是我在這里的做法。 由於他們實現了 Joe 和 Kuo 算法,因此最多可支持 21,201 個維度,這是您可以獲得的最大維度數(開源)。 https://web.maths.unsw.edu.au/~fkuo/sobol/

在這里,我展示了如何使用 base2 方法(使用 Owen Scrambling)和隨機方法(從序列中生成任意數量的點),以及如何跳過第一個點。

請注意,此例程可能非常慢(由於 ndtri 或點到沖擊的逆正態分布轉換),尤其是在高維度 + 高模擬計數中。 Sobol 序列本身的點生成速度非常快,但對於大多數 Monte Carlo 模擬,您將它們轉換為沖擊(您可能使用標准正態分布以外的其他分布)。 這至少可以讓您直接在 Python 代碼中生成點。

另外,在 QMCgenerate 例程中,我跳過了第一點(即 0)——雖然這很常見,但有些論文建議不要這樣做(但我還沒有看到好的替代方案,如果你有的話,請隨意評論)。 我轉置它們只是為了稍后將它們粘貼到 Excel 中並檢查生成的沖擊。 不管怎樣,希望那些需要這個算法的人發現它有用。

from scipy.stats import qmc # needs SciPy >= 1.7.1
from scipy.special import ndtri
import numpy as np
import timeit

time_periods = 252
factors = 12

# IF using base2 generation, need a pow(2,m)
sims = 8092 

dimensions = factors*time_periods

def RQMCgenerate (dimensions, sims, seed):
    start_time = timeit.default_timer()
    m=10 # start at 1024 sims
    while pow(2,m) < sims: #m = 17 # 131,072 sims; M = 16 # 65,536 sims
        m = m+1
    RQMCgenerator = qmc.Sobol(dimensions, scramble=True, seed=seed)
    RQMCsamples = RQMCgenerator.random_base2(m)
    print('\n' + 'Time after sample generation RQMC:', (timeit.default_timer() - start_time), 'seconds'); 
    sobol = ndtri(RQMCsamples).T # get normsinv(points) and transpose to dimensions * sims 
    del RQMCsamples
    print('\n' + 'Time after ndtri (normsinv) of', sims,'sims x dimensions', dimensions, 'Randomized Sobol points): ', (timeit.default_timer() - start_time), 'seconds');
    return sobol

def QMCgenerate(dimensions, sims):
    start_time = timeit.default_timer()
    QMCgenerator = qmc.Sobol(dimensions, scramble=False)
    QMCgenerator.fast_forward(1) #skip first point where normsinv(0) = -Inf
    QMCsamples = QMCgenerator.random(sims) #this generates points not having to be powers of 2
    print('\n' + 'Time after sample generation QMC:', (timeit.default_timer() - start_time), 'seconds'); 
    sobol = ndtri(QMCsamples).T # get normsinv(points) and transpose to dimensions * sims
    del QMCsamples
    print('\n' + 'Time after ndtri (normsinv) of', sims,'sims x dimensions', dimensions, 'Sobol points):', (timeit.default_timer() - start_time), 'seconds');
    return sobol

RQMCsobol = RQMCgenerate(dimensions, sims, seed=0) #note sims changed with pow(2,m) if a power of 2 was not passed
sobol = QMCgenerate(dimensions, sims)

樣本生成 RQMC 后的時間:0.4269224999952712 秒

ndtri (normsinv) of 8092 sims x 維度 3024 Randomized Sobol 點之后的時間:1.0048970999996527 秒

樣本生成 QMC 后的時間:0.0630135999963386 秒

8092 sims x 尺寸 3024 Sobol 點的 ndtri (normsinv) 之后的時間:0.5444753999981913 秒

這在更高的 sims*dimensions 中變得更慢,盡管我沒有發現比 Python 中的 ndtri 更快的點到正態分布沖擊的轉換:

樣本生成后的時間 RQMC:2.1779929000040283 秒

ndtri (normsinv) of 131072 sims x 維度 3024 Randomized Sobol 點之后的時間:10.617904700004146 秒

樣本生成 QMC 后的時間:1.079756200000702 秒

131072 sims x 尺寸 3024 Sobol 點的 ndtri (normsinv) 之后的時間:9.545934699999634 秒

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM