【MIMO】BD法のプリコーディング行列を求めるサンプルプログラム

# 0. はじめに
MIMO (Multiple Input and Multiple Output) は、複数の送受信アンテナを用いることで空間多重化や空間ダイバーシティを実現する通信技術である。4G以降の移動体通信システムにおいて中核となる技術であり、基地局（BS）とユーザ端末（UE）間の伝送容量を最大化する目的で主に利用される。
MIMOは、SU-MIMO（Single User MIMO）とMU-MIMO (Multi User MIMO) に大別される。
SU-MIMOでは、BSと特定のUE間の伝送容量を最大化するために、伝搬路行列に対するSVD(Singular Value Decomposition)によってプリコーディング行列及びポストコーディング行列を求め、空間多重伝送を行う。
MU-MIMO (Multi User MIMO) では、BSと複数のUEの間の同時通信を大容量伝送することを目指し、同じくSVDを用いたプリコーディング行列及びポストコーディング行列を用いた空間多重伝送を行う。
**MU-MIMOでは、BSと特定のUE間の伝送容量最大化とUE間の干渉抑圧を同時に目指す。それぞれの目的を達成するために2段階のSVDを適用する手法として、Block Diagonalization（BD）法がある**。
**本稿では、BD法のサンプルプログラムを示す**。
BD法の計算手順を詳しく知りたい方は、文献[1]を参照いただきたい。

# 1. サンプルプログラム

## 1.1 概要
文献[1]を参考に、サンプルプログラムを実装した。実装の概要を簡潔に述べる。先ずは、干渉抑圧を行うためのプリコーディング行列 (F0) を求める。BSからの通信対象とするUEを決め、そのUEを除いた他UEとBS間の伝送路行列に対するSVDを実施する。**SVD後の固有値が0となる固有値ベクトルを干渉抑圧目的のプリコーディング行列 (F0) として抽出する**。ここで、**固有値が0とは、干渉成分が0になることを意味する**。
次に、干渉抑圧後の条件で通常のSVDを実施する。BSからの通信対象としたUEとBS間の伝送路行列とF0の行列積に対してSVDを実施する。ここで、伝送路行列にF0をかける目的は、干渉抑圧後の伝送路行列を準備するためである。**SVD後の固有値が0でない固有値ベクトルを干渉抑圧状態で伝送容量最大化できるプリコーディング行列(F1)として抽出する**。
最後に、F0とF1の行列積をMU-MIMOを行うBS側の最終的なプリコーディング行列とする。

## 1.2 ソース
概要で示した計算手順で、プリコーディング行列を求めるプログラムのソースを示す。
引数は、以下の4つである。
- NR : 各UEが備える受信アンテナの数 
- NUser : UEの数
- DEBUG : デバッグ用の変数
- seed : numpy random用のシード
```main.py
import numpy as np
import sys

def main(NR,NUser,DEBUG,seed):
        NT = NR * NUser
        print("Down Link Stream")
        print("BS with ",NT," antenna")
        print(NUser, "-UE with ",NR," antenna")
        np.random.seed(seed)

        Hic = np.zeros([(NUser-1)*NR,NT],dtype=complex)
        H = np.zeros([NUser*NR,NT],dtype=complex)
        F01 = np.zeros([NUser*NR,NT],dtype=complex)

        Hch = np.random.normal(size=[NUser,NR,NT])\
        +1j * np.random.normal(size=[NUser,NR,NT])

        for i in range(NUser):
                pm = NR
                c0 = 0
                for j in range(NUser):
                        if i != j:
                                for k0 in range(NR):
                                        for k1 in range(NT):
                                                Hic[c0*NR+k0][k1] = Hch[j][k0][k1]
                                c0 = c0 + 1
                Q0 = np.linalg.matrix_rank(Hic)
                u0,s0,v0 = np.linalg.svd(Hic)
                if DEBUG == 1:
                        print("Rank of H - Hm    : ",Q0)
                        print("Shape of sv mat   :", s0.shape)
                        print("Shpae of Precoder :",v0.shape)
                F0 = np.conjugate(v0[Q0:NT,:].T)
                if DEBUG == 2:
                        print("-- Cancelled Interferences by #",i," F0 --")
                        print(np.round(np.dot(Hic,F0),2))
                HF0 = np.dot(Hch[i],F0)
                Q1 = np.linalg.matrix_rank(HF0)
                u1,s1,v1 = np.linalg.svd(HF0)
                F1 = np.conjugate(v1[0:Q1,:].T)
                if DEBUG == 3:
                        print("Shape of F0 :",F0.shape)
                        print("Shape of F1 :",F1.shape)
                F01[:,i*NR:(i+1)*NR] = np.dot(F0,F1)
        if DEBUG == 3:
                print("Shape of F0 x F1 :",F01.shape)
        for i in range(NUser):
                for k0 in range(NR):
                        for k1 in range(NT):
                                H[i*NR+k0][k1] = Hch[i][k0][k1]
        A = np.dot(H,F01)
        print("-- Hch * F0 * F1 --")
        print(np.round(A,2))


if __name__ == '__main__':
                np.set_printoptions(suppress=True)
                args = sys.argv
                if len(args) == 5:
                                NUser = int(args[1])
                                NR_UE = int(args[2])
                                DEBUG = int(args[3])
                                seed  = int(args[4])
                else:
                                print("Set  Arguments as int")
                                print("#1 : the number of UE")
                                print("#2 : the number of Antenna of a UE")
                                print("#3 : the Debug number")
                                print("#4 : seed for numpy random")
                                print("example : pytheon main.py 4 2 0 123")
                                sys.exit(0)

                main(NUser,NR_UE,DEBUG,seed)
```
## 1.3 プログラム実行例
一例として、2アンテナのUEが4個、8アンテナのBSの条件における実行例を示す。
実行結果として、伝送路行列とプリコーディング行列の積（Hch * F0 * F1）が表示される。
2x2の対角ブロック行列の成分は値があるが、それ以外の干渉成分は全て0となっており、干渉抑圧しながら通信を可能にするプリコーディング行列（F0 * F1）となっていることが分かる。
```
# pytheon main.py 4 2 0 123
Down Link Stream
BS with  8  antenna
4 -UE with  2  antenna
-- Hch * F0 * F1 --
[[-2.17+0.33j -0.19+0.75j  0.  -0.j    0.  -0.j    0.  -0.j    0.  -0.j   0.  -0.j    0.  -0.j  ]
 [ 0.26+1.94j -0.84-0.23j -0.  +0.j    0.  +0.j    0.  +0.j    0.  -0.j   0.  -0.j   -0.  -0.j  ]
 [ 0.  -0.j   -0.  -0.j    2.37-1.34j  1.25+1.22j  0.  -0.j    0.  +0.j  -0.  +0.j   -0.  +0.j  ]
 [-0.  +0.j    0.  -0.j   -0.79+2.89j  1.59-0.02j  0.  -0.j   -0.  +0.j  -0.  -0.j    0.  +0.j  ]
 [ 0.  +0.j    0.  -0.j    0.  +0.j   -0.  +0.j   -1.7 -0.46j -0.77+0.49j  -0.  -0.j    0.  -0.j  ]
 [ 0.  -0.j   -0.  +0.j    0.  -0.j    0.  +0.j    1.45+0.23j -0.86+0.68j  -0.  -0.j    0.  +0.j  ]
 [ 0.  +0.j    0.  -0.j   -0.  +0.j    0.  +0.j    0.  -0.j    0.  +0.j  -0.38-3.5j  -0.17-0.21j]
 [ 0.  -0.j   -0.  +0.j    0.  +0.j    0.  +0.j   -0.  +0.j   -0.  -0.j   0.9 +0.53j -0.9 +0.04j]]
```

# 2. まとめ
本稿では、他UEへの干渉抑圧を行いながらのBSと個々のUE間の伝送容量を最大化するために、2段階でSVDを適用するBD法のサンプルプログラムを示した。

# 参考
[1] https://www.ieice-hbkb.org/files/04/04gun_01hen_07.pdf#page=13