在上一篇中我們非常簡要地介紹了 ScaLAPACK 軟件。雖然 ScaLAPACK 在設(shè)計(jì)上作了很多工作使其方法接口與 LAPACK 盡量保持一致,但是直接使用 Fortran 或 C 語言按照上一篇中介紹的步驟使用 ScaLAPACK 仍然是一件比較麻煩和容易出錯(cuò)的事情,就好比我們使用 numpy.linalg 或 scipy.linalg (在底層調(diào)用 BLAS 和 LAPACK)中的相關(guān)函數(shù)比直接調(diào)用 BLAS 和 LAPACK 中的相關(guān)例程要容易和方便的多,我們也希望使用一個(gè) Python 包裝之后的 ScaLAPACK,下面我們就將介紹這樣一個(gè)工具 scalapy。
為什么使用 scalapy
numpy.linalg 或 scipy.linalg 中提供了非常豐富且易用的線性代數(shù)計(jì)算函數(shù),能夠滿足我們大多數(shù)的線性代數(shù)運(yùn)算需求,為什么還要使用 ScaLAPACK 和 scalapy 呢?對(duì)規(guī)模不是很大的線性代數(shù)問題一般沒必要使用 ScaLAPACK 和 scalapy,但是對(duì)比較大規(guī)模甚至很大規(guī)模的線性代數(shù)問題,比如超過 5000 × 5000 的矩陣運(yùn)算,ScaLAPACK 和 scalapy 的優(yōu)勢(shì)就能顯現(xiàn)了。對(duì)如此大規(guī)模的線性代數(shù)問題,你會(huì)發(fā)現(xiàn) numpy.linalg 或 scipy.linalg 中的函數(shù)可能會(huì)計(jì)算的比較慢,因?yàn)?numpy.linalg 或 scipy.linalg 都不是分布式內(nèi)存的計(jì)算工具,因此其只能利用單臺(tái)機(jī)器的計(jì)算能力。同時(shí),受制于單臺(tái)機(jī)器可用內(nèi)存的大小,所能解決問題的規(guī)模也會(huì)受到限制,不然就會(huì)超過內(nèi)存的大小而導(dǎo)致程序崩潰。另外如果 numpy.linalg 或 scipy.linalg 使用的是 32 位的 LAPACK/BlAS 庫,即使單臺(tái)機(jī)器的內(nèi)存充足,所能計(jì)算的問題的規(guī)模也會(huì)受到限制,比如矩陣元素的數(shù)目不能超過一個(gè) 32 位整型數(shù)所能表示的最大整數(shù) 2147483647,因此可計(jì)算的最大方塊矩陣大小為 46340 × 46340,超過就會(huì)出錯(cuò)。對(duì)這些情況,ScaLAPACK 和 scalapy 就能幫上忙了,因?yàn)樗鼈兪欠植际絻?nèi)存的計(jì)算工具,因此可以把計(jì)算分布到多臺(tái)機(jī)器(如集群或超級(jí)計(jì)算機(jī)上),以充分利用多臺(tái)機(jī)器的強(qiáng)大計(jì)算能力和更大的總內(nèi)存空間來加快問題的求解或者解決某些在單臺(tái)機(jī)器上無法完成的計(jì)算任務(wù)。
下載與安裝 scalapy
軟件依賴
- MPI 實(shí)現(xiàn),目前支持 OpenMPI,IntelMPI 和 MPICH;
- ScaLAPACK 庫, Netlib 和 Intel MKL 的 ScaLAPACK 都支持;
- Python 2.7, 3.2 或更新版本;
- numpy;
- mpi4py;
- Cython;
- f2py (可單獨(dú)安裝,或使用較新版本的 numpy 中包含的 f2py)。
安裝
前往 https://github.com/jrs65/scalapy 下載 scalapy??梢灾苯酉螺d .zip 格式的壓縮包,并解壓,或者使用 git 將該軟件 clone 到本地:
$ git clone https://github.com/jrs65/scalapy.git
進(jìn)入軟件頂層目錄,使用以下命令進(jìn)行安裝:
$ python setup.py install [--user]
主要方法接口
scalapy 的核心成分 —— core 模塊
core 模塊提供了 1 個(gè)全局函數(shù) initmpi 和 4 個(gè)類 ProcessContext,DistributedMatrix,ScalapyException 和 ScalapackException,下面分別進(jìn)行介紹。
函數(shù) initmpi
全局的 scalapy 初始化函數(shù),一般會(huì)首先調(diào)用。
initmpi(gridshape, block_shape=[32, 32])
使用 scalapy 之前的初始化工作,會(huì)初始化一個(gè)全局的進(jìn)程網(wǎng)格上下文 _context (為我們下面將要介紹的 ProcessContext 對(duì)象),和一個(gè)全局的塊狀循環(huán)分布的塊大小 _block_shape。gridshape
是一個(gè)二元數(shù)組指定初始化的進(jìn)程網(wǎng)格的大小為 gridshape[0] × gridshape[1], block_shape
也是一個(gè)二元數(shù)組指定全局塊狀循環(huán)分布的塊大小,一般設(shè)置成 [16, 16],[32, 32] 或 [64, 64] 能獲得比較高的計(jì)算性能,塊的行和列也可以設(shè)置成不同大小。
注意:調(diào)用 initmpi 初始化的全局 ProcessContext 對(duì)象的通信子為 mpi4py.MPI.COMM_WORLD,如果需要使用其它的通信子,則必須手動(dòng)使用(下面將會(huì)介紹的) ProcessContext 的構(gòu)造函數(shù)來初始化一個(gè) ProcessContext 對(duì)象。
類 ProcessContext
ProcessContext 對(duì)象存儲(chǔ)有關(guān) MPI/BLACS 進(jìn)程的相關(guān)信息,這些信息會(huì)被(后面將會(huì)介紹的) DistributedMatrix 所使用。
方法
__init__(self, grid_shape, comm=None)
構(gòu)造函數(shù),以一個(gè)指定的進(jìn)程網(wǎng)格大小 grid_shape
和一個(gè) MPI 通信子 comm
創(chuàng)建一個(gè) ProcessContext 對(duì)象。grid_shape
是一個(gè)二元數(shù)組指定初始化的進(jìn)程網(wǎng)格的大小為 grid_shape[0] × grid_shape[1], comm
如果為 None,則會(huì)使用 mpi4py.MPI.COMM_WORLD。
屬性
grid_shape
所創(chuàng)建的進(jìn)程網(wǎng)格的 shape,為一個(gè)二元 tuple。
grid_position
本進(jìn)程在進(jìn)程網(wǎng)格中的位置,為一個(gè)二元 tuple。
mpi_comm
此 ProcessContext 對(duì)象所使用的 MPI 通信子。
blacs_context
BLACS context 句柄。
all_grid_positions
一個(gè) shape 為 (mpi_comm_size, 2) 的數(shù)組,其第 i 行是第 i 個(gè)進(jìn)程在進(jìn)程網(wǎng)格中的位置。
all_mpi_ranks
一個(gè) shape 為 (grid_shape[0], grid_shape[1]) 的整數(shù)數(shù)組,all_mpi_ranks[i, j] 給出進(jìn)程網(wǎng)格位置 (i, j) 上的進(jìn)程的 rank。
類 DistributedMatrix
一個(gè)以塊狀循環(huán)的方式分布在多個(gè) MPI 進(jìn)程組成的一個(gè)進(jìn)程網(wǎng)格上的分布式矩陣。塊狀循環(huán)分布矩陣是 ScaLAPACK 相關(guān)例程內(nèi)部所使用的數(shù)據(jù)分布形式。
DistributedMatrix 分布在各個(gè)進(jìn)程上的數(shù)據(jù)為基本的 numpy (2維)數(shù)組,下面是 DistributedMatrix 中使用的 ScaLAPACK 數(shù)據(jù)類型與 numpy 類型和 mpi4py 類型的對(duì)照表:
numpy 類型 | MPI 類型 | ScaLAPACK 類型 | 說明 |
---|---|---|---|
np.float32 | MPI.FLOAT | 'S' | 單精度浮點(diǎn)數(shù) |
np.float64 | MPI.DOUBLE | 'D' | 雙精度浮點(diǎn)數(shù) |
np.complex64 | MPI.COMPLEX | 'C' | 單精度復(fù)數(shù) |
np.complex128 | MPI.COMPLEX16 | 'Z' | 雙精度復(fù)數(shù) |
DistributedMatrix 提供了類似 numpy 數(shù)組的切片操作,但不同于 numpy 數(shù)組的是,DistributedMatrix 的一個(gè)切片會(huì)完全復(fù)制原分布矩陣對(duì)應(yīng)的數(shù)據(jù)而創(chuàng)建一個(gè)新的 DistributedMatrix,而不是返回原矩陣對(duì)應(yīng)數(shù)據(jù)的一個(gè)視圖。
主要方法
def __init__(self, global_shape, dtype=np.float64, block_shape=None, context=None)
構(gòu)造函數(shù),初始化一個(gè)空的 DistributedMatrix。global_shape
為所創(chuàng)建的 DistributedMatrix 的整體矩陣 shape,dtype
為其數(shù)據(jù)類型,目前只支持 np.float32、np.float64、np.complex64 和 np.complex128,block_shape
是塊狀循環(huán)分布的塊的大小,如果為 None,會(huì)使用由 initmpi 函數(shù)所初始化的全局 _block_shape,context
為一個(gè) ProcessContext 對(duì)象,如果為 None,則會(huì)使用由 initmpi 函數(shù)所初始化的全局 _context。
empty_like(cls, mat)
創(chuàng)建并返回一個(gè)與 DistributedMatrix mat
具有同樣屬性的空 DistributedMatrix。
empty_trans(cls, mat)
創(chuàng)建并返回一個(gè) global_shape 為 DistributedMatrix mat
的轉(zhuǎn)置(即交換其行和列)的 global_shape,但其它屬性相同的空 DistributedMatrix。
identity(cls, n, dtype=np.float64, block_shape=None, context=None)
創(chuàng)建并返回一個(gè) global_shape 為 (n
, n
) 的單位矩陣(即其對(duì)角元素全為 1.0,其它元素都為 0)。參數(shù) dtype
,block_shape
和 context
與 __init__ 中的對(duì)應(yīng)參數(shù)同。
copy(self)
返回當(dāng)前 DistributedMatrix 的一個(gè)(深)復(fù)制,即每個(gè)進(jìn)程的本地子數(shù)據(jù)都會(huì)進(jìn)行復(fù)制。
row_indices(self)
返回本進(jìn)程所持有的數(shù)據(jù)在當(dāng)前 DistributedMatrix 中整體的行指標(biāo)。
col_indices(self)
返回本進(jìn)程所持有的數(shù)據(jù)在當(dāng)前 DistributedMatrix 中整體的列指標(biāo)。
indices(self, full=True)
返回本進(jìn)程所持有的數(shù)據(jù)在當(dāng)前 DistributedMatrix 中整體的行和列指標(biāo)(一個(gè)由 numpy 數(shù)組組成的二元 tuple)。full
如果為 True,則會(huì)返回行和列廣播后的矩陣,如果為 False,則只返回行和列的指標(biāo)數(shù)組,其結(jié)果類似于 numpy.mgrid 和 numpy.ogrid 的區(qū)別。
可以使用其返回值非常容易地創(chuàng)建依賴于元素坐標(biāo)的 DistributedMatrix,比如創(chuàng)建一個(gè)整體為 Mij = i + j 的 DistributedMatrix,可以這樣操作:
...
dm = DistributedMatrix((100, 100))
rows, cols = dm.indices()
dm.local_array[:] = rows + cols
local_diagonal_indices(self, allow_non_square=False)
返回一個(gè)由 1d numpy 數(shù)組組成的三元 tuple (global_index, local_row_index, local_column_index) 以表明本進(jìn)程所持有的子數(shù)據(jù)位于 (local_row_index, local_column_index) 的元素是整體矩陣的第 global_index 行(或)列的對(duì)角元素。allow_non_square
指明是否必須是方塊矩陣。
可以使用其返回值非常容易地操作該 DistributedMatrix 的對(duì)角項(xiàng),比如要完成運(yùn)算 Mij += i2 * δij,可以這樣操作:
...
dm = DistributedMatrix((100, 100))
gi, lri, lci = dm.local_diagonal_indices()
dm.local_array[lri, lci] += gi**2
trace(self)
返回矩陣的跡(即對(duì)角元素的和),每個(gè)進(jìn)程都會(huì)返回同樣的結(jié)果。
from_global_array(cls, mat, rank=None, block_shape=None, context=None)
由一個(gè)整體的矩陣 mat
創(chuàng)建并返回一個(gè) DistributedMatrix。mat
為一個(gè) 2d numpy 數(shù)組,rank
如果為 None,假定每個(gè)進(jìn)程都擁有相同的 mat
,故只需從進(jìn)程本地的 mat
中提取對(duì)應(yīng)的數(shù)據(jù),否則只會(huì)從 rank
指定的進(jìn)程的 mat
中提取數(shù)據(jù)并分發(fā)給給個(gè)進(jìn)程,以創(chuàng)建一個(gè) DistributedMatrix。參數(shù) block_shape
和 contex
同 initmpi 中對(duì)應(yīng)的參數(shù)。
to_global_array(self, rank=None)
將該 DistributedMatrix 轉(zhuǎn)換為一個(gè)整體的 numpy 數(shù)組并返回。如果 rank
為 None,則所以進(jìn)程都會(huì)返回相同的整體數(shù)組,否則只有 rank
對(duì)應(yīng)的進(jìn)程會(huì)返回整體數(shù)組,其它進(jìn)程返回 None。
from_file(cls, filename, global_shape, dtype, block_shape=None, context=None, order='F', displacement=0)
從指定文件 filename
中讀取數(shù)據(jù)創(chuàng)建并返回一個(gè) DistributedMatrix。注意文件中存放的是一個(gè)整體的數(shù)據(jù)矩陣而不是塊狀循環(huán)分布后的數(shù)據(jù),該函數(shù)會(huì)在內(nèi)部完成數(shù)據(jù)分布任務(wù)。global_shape
是一個(gè)二元數(shù)組指定整體矩陣的 shape,dtype
,block_shape
he contex
同 initmpi 中對(duì)應(yīng)的參數(shù),order
指明文件中數(shù)據(jù)的排列方式,可以為 'F'(默認(rèn)值) 或 'C',前者是 Fortran 的排列方式,即按照列優(yōu)先的方式排列,后者是 C 的排列方式,解按照行優(yōu)先的方式排列,displacement
指明從文件中開始讀取數(shù)據(jù)的位置。
to_file(self, filename, order='F', displacement=0)
將該 DistributedMatrix 中的數(shù)據(jù)寫入到文件 filename
中。注意寫入到文件中的數(shù)據(jù)是按照整體矩陣中元素的順序排列的,而不是按照塊狀循環(huán)分布后的元素順序。order
可選 'F' 或 'C',指明寫入文件中的數(shù)據(jù)矩陣的排列方式(列優(yōu)先或行優(yōu)先),displacement
指明在文件中的起始寫入位置。
redistribute(self, block_shape=None, context=None)
將該 DistributedMatrix 按照新指定的 block_shape
和 context
重新分布,返回新創(chuàng)建的 DistributedMatrix。block_shape
如果為 None,會(huì)使用由 initmpi 函數(shù)所初始化的全局 _block_shape,context
如果為 None,則會(huì)使用由 initmpi 函數(shù)所初始化的全局 _context。注意:新指定的 context
必須和該 DistributedMatrix 的 ProcessContext 具有相同的 MPI 通信子。
transpose(self):
返回該 DistributedMatrix 的轉(zhuǎn)置(一個(gè)新的 DistributedMatrix,其整體矩陣為原整體矩陣行和列交換后的結(jié)果)。也可以通過屬性 T 獲取。
conj(self)
返回該 DistributedMatrix 的復(fù)共軛矩陣(元素的實(shí)部相同,虛部反號(hào)),如果該 DistributedMatrix 的元素為實(shí)數(shù)(dtype 為 np.float32 或 np.float64),則返回的矩陣為原矩陣自身,如果其元素為復(fù)數(shù)(dtype 為 np.complex64 或 np.complex128),則會(huì)返回一個(gè)新的 DistributedMatrix。也可以通過屬性 C 獲取。
hconj(self)
返回該 DistributedMatrix 的厄密共軛(即轉(zhuǎn)置后的復(fù)共軛),為一個(gè)新的 DistributedMatrix。也可以通過屬性 H 獲取。
屬性
local_array
該 DistributedMatrix 分布在本進(jìn)程上的子數(shù)組,為一個(gè) numpy 數(shù)組。
desc
ScaLAPACK array descriptor,為一個(gè)整數(shù) numpy 數(shù)組。
context
該 DistributedMatrix 的 ProcessContext 對(duì)象。
dtype
該 DistributedMatrix 數(shù)據(jù)的 numpy 類型,見前面的類型對(duì)照表。
mpi_dtype
該 DistributedMatrix 數(shù)據(jù)的 MPI 類型,見前面的類型對(duì)照表。
sc_dtype
該 DistributedMatrix 數(shù)據(jù)的 ScaLAPACK 類型,見前面的類型對(duì)照表。
global_shape
該 DistributedMatrix 的整體矩陣 shape。
local_shape
該 DistributedMatrix 分布在本進(jìn)程上的子數(shù)組的 shape。
block_shape
該 DistributedMatrix 塊狀循環(huán)分布的塊大小。
T
該 DistributedMatrix 的轉(zhuǎn)置。
C
該 DistributedMatrix 的復(fù)共軛。
H
該 DistributedMatrix 的厄密共軛。
類 ScalapyException
scalapy 軟件包自身產(chǎn)生的異常。
類 ScalapackException
調(diào)用 ScaLAPACK 過程中產(chǎn)生的異常。
scalapy 的計(jì)算例程 —— routines 模塊
routines 模塊提供了若干用于分布式內(nèi)存線性代數(shù)運(yùn)算的函數(shù),這些函數(shù)在底層調(diào)用 ScaLAPACK 的相關(guān)例程完成其工作,但是其提供了與 numpy.linalg 和 scipy.linalg 一致的函數(shù)名和使用接口,因此非常方便易用。目前其提供的函數(shù)非常有限,但是常用的都已提供,下面分別對(duì)其進(jìn)行介紹。
dot(A, B, transA='N', transB='N')
矩陣相乘,返回一個(gè)新的 DistributedMatrix。A
和 B
都是 DistributedMatrix,transA
和 transB
可以取值 'N','T' 或者 'C':'N' 表示用矩陣本身做運(yùn)算,'T' 表示用矩陣的轉(zhuǎn)置(行和列交換后的結(jié)果)做運(yùn)算,'C' 表示用矩陣的厄密共軛(轉(zhuǎn)置后復(fù)共軛)做運(yùn)算。
inv(A, overwrite_a=True)
計(jì)算 A
的逆矩陣,A
是一個(gè)方塊(行數(shù)和列數(shù)相同) DistributedMatrix,如果 overwrite_a
為 True,則 A
中的數(shù)據(jù)會(huì)在計(jì)算過程中被破壞掉,如果為 False,則運(yùn)算中會(huì)使用 A
的一個(gè)復(fù)制,因此 A
中的數(shù)據(jù)會(huì)得到保持。返回由 A
的逆矩陣(也是一個(gè) DistributedMatrix)和一個(gè) numpy 數(shù)組組成的二元 tuple,該數(shù)組提供一些額外信息,一般不用關(guān)注。
triinv(A, lower=False, unit_triangular=False, overwrite_a=True)
計(jì)算并返回一個(gè)三角矩陣 A
的逆矩陣,返回結(jié)果為一個(gè) DistributedMatrix。三角矩陣分為上三角矩陣和下三角矩陣,上三角矩陣是指對(duì)角線和對(duì)角線上方的元素非零,而對(duì)角線下方的元素都為零的矩陣;下三角矩陣是指對(duì)角線和對(duì)角線下方的元素非零,而對(duì)角線上方的元素都為零的矩陣。參數(shù) A
是一個(gè) DistributedMatrix,lower
如果為 True,會(huì)使用 A
的下三角部分做計(jì)算,其對(duì)角線以上部分會(huì)被忽略,如果為 False,則會(huì)使用 A
的上三角部分做計(jì)算,其對(duì)角線以下部分會(huì)被忽略,unit_triangular
如果為 True,表明 A
的對(duì)角元素全為 1,否則表明 A
的對(duì)角元素不全為 1,overwrite_a
如果為 True,則 A
中的數(shù)據(jù)會(huì)在計(jì)算過程中被破壞掉,如果為 False,則運(yùn)算中會(huì)使用 A
的一個(gè)復(fù)制,因此 A
中的數(shù)據(jù)會(huì)得到保持。
pinv(A, overwrite_a=True)
計(jì)算并返回 A
的(Moore-Penrose)廣義逆矩陣,返回結(jié)果為一個(gè) DistributedMatrix。A
是一個(gè) DistributedMatrix,overwrite_a
如果為 True,則 A
中的數(shù)據(jù)會(huì)在計(jì)算過程中被破壞掉,如果為 False,則運(yùn)算中會(huì)使用 A
的一個(gè)復(fù)制,因此 A
中的數(shù)據(jù)會(huì)得到保持。注意:此方法要求 A
是一個(gè)滿秩矩陣。
pinv2(A, overwrite_a=True, cond=None, rcond=None, return_rank=False, check_finite=True)
計(jì)算并返回 A
的(Moore-Penrose)廣義逆矩陣。此方法不同于 pinv 在于它是調(diào)用(下面為介紹的) svd 函數(shù)來計(jì)算 A
的廣義逆的,因此不要求 A
必須是一個(gè)滿秩矩陣。A
是一個(gè) DistributedMatrix,overwrite_a
如果為 True,則 A
中的數(shù)據(jù)會(huì)在計(jì)算過程中被破壞掉,如果為 False,則運(yùn)算中會(huì)使用 A
的一個(gè)復(fù)制,因此 A
中的數(shù)據(jù)會(huì)得到保持。cond
he rcond
如果非 None 設(shè)置奇異值的截?cái)嚅撝担∮?rcond*largest_singular_value 或者 cond*largest_singular_value 的奇異值會(huì)被截?cái)酁?0,否則會(huì)使用合適的機(jī)器精度對(duì)奇異值進(jìn)行截?cái)唷?code>return_rank 如果為 True,該函數(shù)會(huì)返回由 A
的廣義逆矩陣和 A
的有效秩(即經(jīng)過截?cái)嗪蠓橇愕钠娈愔档膫€(gè)數(shù))組成的二元 tuple,否則只返回 A
的廣義逆矩陣。check_finite
指明是否首先檢查 A
中所有元素都是有限數(shù)值然后再進(jìn)行計(jì)算。
cholesky(A, lower=False, overwrite_a=False, zero_triangle=True)
計(jì)算對(duì)稱或厄密矩陣 A
的 Cholesky 分解。參數(shù) A
是一個(gè) DistributedMatrix,lower
如果為 True,會(huì)計(jì)算分解 A = U*U,其中 U 是一個(gè)上三角矩陣,最后返回 U,如果為 False,則會(huì)計(jì)算分解 A = LL*,其中 L 是一個(gè)下三角矩陣,最后返回 L,overwrite_a
如果為 True,則 A
中的數(shù)據(jù)會(huì)在計(jì)算過程中被破壞掉,如果為 False,則運(yùn)算中會(huì)使用 A
的一個(gè)復(fù)制,因此 A
中的數(shù)據(jù)會(huì)得到保持,zero_triangle
如果為 True,則返回的 U 或者 L 中應(yīng)該為 0 的元素會(huì)被顯式填充為 0,因?yàn)樵谟?jì)算過程中 ScaLAPACK 的相關(guān)例程會(huì)忽略對(duì)那些元素的操作,因此返回的結(jié)果那些元素可能并不為 0。
lu(A, overwrite_a=True)
計(jì)算方塊矩陣 A
的 LU 分解。此方法計(jì)算的分解形式為 A = P L U,其中 P 是一個(gè)置換矩陣,L 是一個(gè)對(duì)角元素都為 1 的下三角矩陣,U是一個(gè)上三角矩陣。A
是一個(gè) DistributedMatrix,overwrite_a
如果為 True,則 A
中的數(shù)據(jù)會(huì)在計(jì)算過程中被破壞掉,如果為 False,則運(yùn)算中會(huì)使用 A
的一個(gè)復(fù)制,因此 A
中的數(shù)據(jù)會(huì)得到保持。返回由一個(gè) DistributedMatrix 和一個(gè) numpy 數(shù)組組成的二元 tuple,分解因子 L 和 U 分布存儲(chǔ)在該 DistributedMatrix 的下三角和上三角部分,但是 L 的對(duì)角元素(全為 1)沒有存儲(chǔ),在返回的 numpy 數(shù)組中包含轉(zhuǎn)置的相關(guān)信息。
eigh(A, B=None, lower=True, eigvals_only=False, overwrite_a=True, overwrite_b=True, type_=1, eigbounds=None, eigvals=None)
求解對(duì)稱或厄密矩陣的本征值(或廣義本征值)問題。A
為一個(gè)實(shí)對(duì)稱或者復(fù)厄密 DistributedMatrix,B
如果非 None,應(yīng)該是一個(gè)實(shí)對(duì)稱或者復(fù)厄密的正定 DistributedMatrix,否則會(huì)使用單位矩陣,lower
如果為 True,會(huì)使用 A
的下三角部分做計(jì)算,其對(duì)角線以上部分會(huì)被忽略,如果為 False,則會(huì)使用 A
的上三角部分做計(jì)算,其對(duì)角線以下部分會(huì)被忽略,eigvals_only
如果為 False,會(huì)計(jì)算和返回本征值(一個(gè) numpy 數(shù)組,每個(gè)進(jìn)程都會(huì)是同樣都結(jié)果,包含所有本征值)及本征向量矩陣(一個(gè) DistributedMatrix),如果為 True,則只會(huì)計(jì)算和返回本征值數(shù)組,overwrite_a
(overwrite_b
)指明是否允許在計(jì)算過程中破壞原矩陣 A
(B
),如果為 False,會(huì)使用原矩陣的一個(gè)復(fù)制,因此會(huì)使用更多的內(nèi)存,type_
指明計(jì)算類型(見下面的說明),eigbounds
如果非 None,應(yīng)該是一個(gè)二元 tuple (vl, vu),指定只計(jì)算處于下限 vl 和上限 vu 之間的本征值和本征向量,eigvals
如果非 None,應(yīng)該是一個(gè)二元 tuple (lo, hi),指定只計(jì)算第 lo 到第 hi (包含第 hi)個(gè)本征值及本征向量。
type_
可取值 1, 2 或 3,分別對(duì)應(yīng)求解下面的問題:
- type_ = 1: A v[:,i] = w[i] B v[:,i]
- type_ = 2: A B v[:,i] = w[i] v[:,i]
- type_ = 3: B A v[:,i] = w[i] v[:,i]
svd(A, overwrite_a=True, compute_u=True, compute_v=True)
計(jì)算 A
的奇異值分解。分解形式為 A = U * np.diag(s) * V*,U 和 V 都是幺正(或?qū)嵳唬┚仃?,s 是一個(gè) 1d numpy 數(shù)組包含從大到小排列的奇異值。A
是一個(gè) DistributedMatrix,overwrite_a
如果為 True,則 A
中的數(shù)據(jù)會(huì)在計(jì)算過程中被破壞掉,如果為 False,則運(yùn)算中會(huì)使用 A
的一個(gè)復(fù)制,因此 A
中的數(shù)據(jù)會(huì)得到保持,compute_u
(compute_v
)指明是否返回 U (V*),如果都返回,結(jié)果為一個(gè)三元 tuple (U, s, V*),其中 U 和 V* 都是 DistributedMatrix,s 是一個(gè) 1d numpy 數(shù)組(所有進(jìn)程都會(huì)返回同樣的結(jié)果),如果不返回 U 或者 V*,結(jié)果為一個(gè)二元 tuple (U, s) 或者 (s, V*),如果 U 和 V* 都不返回,則結(jié)果為 s。
transpose(A)
計(jì)算并返回 A
的轉(zhuǎn)置。A
是一個(gè) DistributedMatrix。
conj(A)
計(jì)算并返回 A
的復(fù)共軛。A
是一個(gè) DistributedMatrix。
hconj(A)
計(jì)算并返回 A
的厄密共軛。A
是一個(gè) DistributedMatrix。
使用步驟
使用 scalapy 的步驟和上一篇中介紹的使用 ScaLAPACK 的步驟基本一致:
初始化進(jìn)程網(wǎng)格;
可以調(diào)用 initmpi 函數(shù)初始化一個(gè)全局的 ProcessContext 對(duì)象或者由 ProcessContext 的構(gòu)造函數(shù)手動(dòng)初始化一個(gè) ProcessContext 對(duì)象。將數(shù)據(jù)創(chuàng)建為一個(gè) DistributedMatrix,這會(huì)將數(shù)據(jù)(矩陣或向量)按照塊狀循環(huán)方式分布到進(jìn)程網(wǎng)格上;
可以采用多種方式將數(shù)據(jù)創(chuàng)建為 DistributedMatrix,如可以調(diào)用 DistributedMatrix.from_global_array 由一個(gè)整體的 numpy 數(shù)組創(chuàng)建,可以調(diào)用 DistributedMatrix.from_file 從文件中讀取數(shù)據(jù)創(chuàng)建,或者手動(dòng)創(chuàng)建(對(duì)于一些比較特殊的容易創(chuàng)建的 DistributedMatrix)。調(diào)用 DistributedMatrix 自身的方法或 scalapy.routines 模塊的求解例程以完成計(jì)算。
計(jì)算的結(jié)果一般都是 DistributedMatrix,可以通過調(diào)用 DistributedMatrix.to_global_array 將其轉(zhuǎn)化為一個(gè)整體的 numpy 數(shù)組,或調(diào)用 DistributedMatrix.to_file 將其對(duì)應(yīng)的整體矩陣存入到文件。
例程
下面給出簡單的使用例程。
# scalapy_demo.py
"""
Demonstrate the use of scalapy.
Run this with 4 process like:
$ mpiexec -n 4 python scalapy_demo.py
"""
import os
import numpy as np
import scipy.linalg as la
from mpi4py import MPI
from scalapy import core
import scalapy.routines as rt
comm = MPI.COMM_WORLD
rank = comm.rank
size = comm.size
if size != 4:
raise Exception("Must run with 4 processes")
# define a function to compare whether two arrays are equal
allclose = lambda a, b: np.allclose(a, b, rtol=1e-4, atol=1e-6)
# initialize a global ProcessContext object,
# which includes the initialization of a 2 x 2 process grid
core.initmpi([2, 2], block_shape=[16, 16])
N = 300
# create a N x N numpy array with random numbers
gA = np.random.standard_normal((N, N)).astype(np.float64)
gA = np.asfortranarray(gA)
# create a DistributedMatrix from gA
dA = core.DistributedMatrix.from_global_array(gA, rank=0)
print 'rank %d has global_shape of dA = %s' % (rank, dA.global_shape)
print 'rank %d has local_shape of dA = %s' % (rank, dA.local_shape)
print 'rank %d has block_shape of dA = %s' % (rank, dA.block_shape)
# compute the inverse of dA
invA, ipiv = rt.inv(dA)
# convert to a global numpy array hold by rank 0 only
ginvA = invA.to_global_array(rank=0)
if rank == 0:
# compare the result with that of scipy.linalg.inv
print 'result equals that of scipy: ', allclose(ginvA, la.inv(gA))
# write dA to file
file_name = 'dA.dat'
dA.to_file(file_name)
# now read it from file and check it equals the original DistributedMatrix
dA1 = core.DistributedMatrix.from_file(file_name, dA.global_shape, dA.dtype, dA.block_shape, dA.context)
print 'rank %d has dA.local_array == dA1.local_array: %s' % (rank, allclose(dA.local_array, dA1.local_array))
# remove the file
if rank == 0:
os.remove(file_name)
運(yùn)行結(jié)果如下:
$ mpiexec -n 4 python scalapy_demo.py
rank 0 has global_shape of dA = (300, 300)
rank 0 has local_shape of dA = (156, 156)
rank 0 has block_shape of dA = (16, 16)
rank 1 has global_shape of dA = (300, 300)
rank 1 has local_shape of dA = (156, 144)
rank 1 has block_shape of dA = (16, 16)
rank 2 has global_shape of dA = (300, 300)
rank 2 has local_shape of dA = (144, 156)
rank 2 has block_shape of dA = (16, 16)
rank 3 has global_shape of dA = (300, 300)
rank 3 has local_shape of dA = (144, 144)
rank 3 has block_shape of dA = (16, 16)
result equals that of scipy: True
rank 3 has dA.local_array == dA1.local_array: True
rank 1 has dA.local_array == dA1.local_array: True
rank 2 has dA.local_array == dA1.local_array: True
rank 0 has dA.local_array == dA1.local_array: True
以上介紹了使用 scalapy 調(diào)用 ScaLAPACK 進(jìn)行分布式內(nèi)存的線性代數(shù)運(yùn)算。Python 作為一種膠水語言,可以非常容易地包裝和調(diào)用其它計(jì)算機(jī)語言已有的程序代碼和工具庫,如果我們有用 C,C++,F(xiàn)ortran 或其它計(jì)算機(jī)語言編寫的 MPI 計(jì)算程序,也能很容易地將其包裝后在 mpi4py 中進(jìn)行調(diào)用。另外我們也可以用這些計(jì)算機(jī)語言編寫一些運(yùn)算速度更快的 MPI 擴(kuò)展模塊供 mpi4py 程序調(diào)用。在下面的章節(jié)中我們將介紹怎么在 mpi4py 程序中包裝和調(diào)用 C,C++,F(xiàn)ortran 的 MPI 程序。在下一篇中我們首先介紹直接使用 Python C API 進(jìn)行 C 語言 MPI 程序包裝的方法。