フルランクな行列をランダムに生成する手法を教えて頂きました:
https://t.co/mp7HaadpwZ
— とみや🐉これならわかる機械学習 (@TomiyaAkio) August 8, 2021
ランダムなゼロでない固有値を対角にならべてから、ランダムなユニタリー行列で相似変換すれば良さそうです。ランダムなユニタリー行列はランダムなエルミート行列から作れます。
実装
手順:
- ランダムに対角行列 $D$ を生成する
- ランダムな直行行列 $Q$ を生成する
- 行列 $D$ を $Q$ によって相似変換する(i.e., $A = Q^\top D Q$)
from scipy.stats import ortho_group
def make_full_rank_matrix(d, seed=0):
"""
>>> make_full_rank_matrix(3)
array([[ 0.59537814, 0.03454805, -0.05188665],
[ 0.03454805, 0.6643958 , -0.04420196],
[-0.05188665, -0.04420196, 0.60699231]])
"""
np.random.seed(seed)
D = np.diag(np.random.rand(d))
Q = ortho_group.rvs(dim=d)
A = Q.T@D@Q
return A
# test for whether the matrix made by the above method is full rank or not
num_iter = 10
success = 0
for seed in range(num_iter):
d = 500
S = make_full_rank_matrix(d, seed)
if np.linalg.matrix_rank(S) == d:
success += 1
print(success)
別の手法
次の手法で生成される行列を考える:
- $n\times k$ 次元の行列 $W$ をランダムに生成する
- 対角成分が正であるような対角行列 $D$ をランダムに生成する
- 行列 $B$ を $B = WW^\top + D$ で生成する
- 行列 $B$ を相似変換して行列 $A$ を得る,i.e., $A = \text{diag}[1/\sqrt{B_{ii}}] B \text{diag}[1/\sqrt{B_{ii}}]$.
実装:
def make_full_rank_matrix(d, k, seed=0):
"""
>>> make_full_rank_matrix(3, 2)
array([[1. , 0.62140315, 0.74064102],
[0.62140315, 1. , 0.70247127],
[0.74064102, 0.70247127, 1. ]])
"""
np.random.seed(seed)
W = np.random.rand(d, k)
B = W@W.T + np.diag(np.random.rand(d))
A = np.diag(1/np.sqrt(np.diag(B))) @ B @ np.diag(1/np.sqrt(np.diag(B)))
return A
# test for whether the matrix made by the above method is full rank or not
num_iter = 10
success = 0
for seed in range(num_iter):
d = 500
S = make_full_rank_matrix(d, seed)
if np.linalg.matrix_rank(S) == d:
success += 1
print(success)
この手法で生成される行列 $A$ の固有値は,$k$ の値によって特徴付けられる.図を見た方が早いと思うので,以下に $k = 100, 50, 20, 10, 5, 2$ のそれぞれの場合における固有値を大きい順に並べて図示した 図を載せておく(行列のサイズは $100\times 100$ である):
図のように,上記の手法で生成した $n\times n$ の行列 $A$ の固有値を大きい順に並べたとき,$k$ 番目に大きい固有値までは大きい値をとり,それ以降の固有値は微小の値をとる($k=2$ のときはそうでもないように見えるが...).