phyloK 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- phylok-1.0.0/PKG-INFO +11 -0
- phylok-1.0.0/README.md +5 -0
- phylok-1.0.0/phyloK.egg-info/PKG-INFO +11 -0
- phylok-1.0.0/phyloK.egg-info/SOURCES.txt +22 -0
- phylok-1.0.0/phyloK.egg-info/dependency_links.txt +1 -0
- phylok-1.0.0/phyloK.egg-info/not-zip-safe +1 -0
- phylok-1.0.0/phyloK.egg-info/requires.txt +3 -0
- phylok-1.0.0/phyloK.egg-info/top_level.txt +1 -0
- phylok-1.0.0/setup.cfg +4 -0
- phylok-1.0.0/setup.py +28 -0
- phylok-1.0.0/src/LRMF.py +171 -0
- phylok-1.0.0/src/RBF.py +203 -0
- phylok-1.0.0/src/__init__.py +0 -0
- phylok-1.0.0/src/agop.py +240 -0
- phylok-1.0.0/src/alpha_beta_weighting.py +297 -0
- phylok-1.0.0/src/block_cross_validation.py +315 -0
- phylok-1.0.0/src/opt.py +383 -0
- phylok-1.0.0/src/phyloK.py +1307 -0
- phylok-1.0.0/src/phyloKPQL.py +1944 -0
- phylok-1.0.0/src/phyloK_multiclass.py +1059 -0
- phylok-1.0.0/src/phyloK_v2.py +1572 -0
- phylok-1.0.0/src/tree_solve.py +370 -0
- phylok-1.0.0/src/utils.py +23 -0
- phylok-1.0.0/tests/test_phyloKPQL.py +31 -0
phylok-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: phyloK
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Classifier: Programming Language :: Python :: 3
|
|
5
|
+
Requires-Python: >=3.5
|
|
6
|
+
Requires-Dist: numpy
|
|
7
|
+
Requires-Dist: scipy
|
|
8
|
+
Requires-Dist: phylokrr
|
|
9
|
+
Dynamic: classifier
|
|
10
|
+
Dynamic: requires-dist
|
|
11
|
+
Dynamic: requires-python
|
phylok-1.0.0/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: phyloK
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Classifier: Programming Language :: Python :: 3
|
|
5
|
+
Requires-Python: >=3.5
|
|
6
|
+
Requires-Dist: numpy
|
|
7
|
+
Requires-Dist: scipy
|
|
8
|
+
Requires-Dist: phylokrr
|
|
9
|
+
Dynamic: classifier
|
|
10
|
+
Dynamic: requires-dist
|
|
11
|
+
Dynamic: requires-python
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
setup.py
|
|
3
|
+
phyloK.egg-info/PKG-INFO
|
|
4
|
+
phyloK.egg-info/SOURCES.txt
|
|
5
|
+
phyloK.egg-info/dependency_links.txt
|
|
6
|
+
phyloK.egg-info/not-zip-safe
|
|
7
|
+
phyloK.egg-info/requires.txt
|
|
8
|
+
phyloK.egg-info/top_level.txt
|
|
9
|
+
src/LRMF.py
|
|
10
|
+
src/RBF.py
|
|
11
|
+
src/__init__.py
|
|
12
|
+
src/agop.py
|
|
13
|
+
src/alpha_beta_weighting.py
|
|
14
|
+
src/block_cross_validation.py
|
|
15
|
+
src/opt.py
|
|
16
|
+
src/phyloK.py
|
|
17
|
+
src/phyloKPQL.py
|
|
18
|
+
src/phyloK_multiclass.py
|
|
19
|
+
src/phyloK_v2.py
|
|
20
|
+
src/tree_solve.py
|
|
21
|
+
src/utils.py
|
|
22
|
+
tests/test_phyloKPQL.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
phyloK
|
phylok-1.0.0/setup.cfg
ADDED
phylok-1.0.0/setup.py
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
import setuptools
|
|
4
|
+
from distutils.core import setup
|
|
5
|
+
|
|
6
|
+
dependencies = [
|
|
7
|
+
"numpy",
|
|
8
|
+
"scipy",
|
|
9
|
+
"phylokrr"
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
with open('README.md') as readme_file:
|
|
13
|
+
readme = readme_file.read()
|
|
14
|
+
|
|
15
|
+
setup(name = "phyloK",
|
|
16
|
+
version = '1.0.0',
|
|
17
|
+
# long_description = readme,
|
|
18
|
+
# long_description_content_type = 'text/markdown',
|
|
19
|
+
packages = ['phyloK'],
|
|
20
|
+
package_dir = {'phyloK': 'src'},
|
|
21
|
+
python_requires='>=3.5',
|
|
22
|
+
install_requires = dependencies,
|
|
23
|
+
zip_safe = False,
|
|
24
|
+
include_package_data=True,
|
|
25
|
+
classifiers = [
|
|
26
|
+
'Programming Language :: Python :: 3',
|
|
27
|
+
]
|
|
28
|
+
)
|
phylok-1.0.0/src/LRMF.py
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
def check_convergence(x_old, x_new, threshold=1e-6):
|
|
4
|
+
max_updt = np.linalg.norm(x_new - x_old, ord = np.inf)
|
|
5
|
+
w_max = np.linalg.norm(x_new, ord = np.inf)
|
|
6
|
+
if w_max == 0.0 or max_updt/w_max <= threshold:
|
|
7
|
+
return True
|
|
8
|
+
|
|
9
|
+
return False
|
|
10
|
+
|
|
11
|
+
def get_Options(X, nan_value = 0):
|
|
12
|
+
if np.isnan(nan_value):
|
|
13
|
+
Oi,Oj = np.where(~np.isnan(X))
|
|
14
|
+
else:
|
|
15
|
+
Oi,Oj = np.where(X != nan_value)
|
|
16
|
+
|
|
17
|
+
return np.c_[Oi, Oj]
|
|
18
|
+
|
|
19
|
+
def init_AB(k, shape, rng = np.random.default_rng(12038)):
|
|
20
|
+
a,b = shape
|
|
21
|
+
|
|
22
|
+
A = rng.normal(size = (a,k))
|
|
23
|
+
B = rng.normal(size = (k,b))
|
|
24
|
+
return A, B
|
|
25
|
+
|
|
26
|
+
def ALS_legacy(sample_train, sample_test,
|
|
27
|
+
k = 6, lam1 = 0.1, lam2 = 0.1,
|
|
28
|
+
get_errors = True, epsilon = 1e-6,
|
|
29
|
+
max_epochs = 1000,
|
|
30
|
+
rng = np.random.default_rng(12038)):
|
|
31
|
+
|
|
32
|
+
Oij_test = get_Options(sample_test)
|
|
33
|
+
Oij = get_Options(sample_train)
|
|
34
|
+
|
|
35
|
+
M_test = sample_test[Oij_test[:,0], Oij_test[:,1]]
|
|
36
|
+
|
|
37
|
+
Ik = np.eye(k)
|
|
38
|
+
A,B = init_AB(k, sample_train.shape, rng = rng)
|
|
39
|
+
|
|
40
|
+
all_errors = []
|
|
41
|
+
for _ in range(max_epochs):
|
|
42
|
+
Ac = np.copy(A)
|
|
43
|
+
Bc = np.copy(B)
|
|
44
|
+
# row and columns
|
|
45
|
+
# update
|
|
46
|
+
for i,j in Oij:
|
|
47
|
+
# i,j
|
|
48
|
+
Ok_ = Oij[ Oij[:,0] == i ]
|
|
49
|
+
Mkj = sample_train[ Ok_[:,0], Ok_[:,1] ]
|
|
50
|
+
Bj = B[:, Ok_[:,1] ]
|
|
51
|
+
|
|
52
|
+
# matrix form of
|
|
53
|
+
# update rule of A[i:]
|
|
54
|
+
A[i,:] = np.linalg.solve(Bj @ Bj.T + lam1*Ik, Bj @ Mkj.T).T
|
|
55
|
+
|
|
56
|
+
O_k = Oij[ Oij[:,1] == j ]
|
|
57
|
+
Mik = sample_train[ O_k[:,0], O_k[:,1] ]
|
|
58
|
+
Ai = A[ O_k[:,0],:]
|
|
59
|
+
|
|
60
|
+
# matrix form of
|
|
61
|
+
# update rule of B[:k]
|
|
62
|
+
B[:,j] = np.linalg.solve( Ai.T @ Ai + lam2*Ik, Ai.T @ Mik )
|
|
63
|
+
|
|
64
|
+
if get_errors:
|
|
65
|
+
# matrix form of
|
|
66
|
+
# error evaluation
|
|
67
|
+
AiBj = (A @ B)[Oij_test[:,0], Oij_test[:,1]]
|
|
68
|
+
# rmse's
|
|
69
|
+
myrmse = np.sqrt( np.mean( (M_test - AiBj)**2 ) )
|
|
70
|
+
# print("current RMSE: ", myrmse)
|
|
71
|
+
all_errors.append( myrmse)
|
|
72
|
+
|
|
73
|
+
if check_convergence(Ac, A, epsilon) and\
|
|
74
|
+
check_convergence(Bc, B, epsilon):
|
|
75
|
+
break
|
|
76
|
+
|
|
77
|
+
AiBj = (A @ B)[ Oij_test[:,0], Oij_test[:,1] ]
|
|
78
|
+
final_error = np.sqrt( np.mean( (M_test - AiBj)**2 ) )
|
|
79
|
+
|
|
80
|
+
return A, B, all_errors, final_error
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def ALS(M_train,
|
|
84
|
+
k = 6, lam1 = 0.1, lam2 = 0.1,
|
|
85
|
+
epsilon = 1e-6,
|
|
86
|
+
max_epochs = 1000,
|
|
87
|
+
rng = np.random.default_rng(12038),
|
|
88
|
+
nan_value = 0):
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
Oij = get_Options(M_train, nan_value = nan_value)
|
|
92
|
+
Ik = np.eye(k)
|
|
93
|
+
A,B = init_AB(k, M_train.shape, rng = rng)
|
|
94
|
+
|
|
95
|
+
for _ in range(max_epochs):
|
|
96
|
+
Ac = np.copy(A)
|
|
97
|
+
Bc = np.copy(B)
|
|
98
|
+
# row and columns
|
|
99
|
+
# update
|
|
100
|
+
for i,j in Oij:
|
|
101
|
+
# i,j
|
|
102
|
+
Ok_ = Oij[ Oij[:,0] == i ]
|
|
103
|
+
Mkj = M_train[ Ok_[:,0], Ok_[:,1] ]
|
|
104
|
+
Bj = B[:, Ok_[:,1] ]
|
|
105
|
+
|
|
106
|
+
# matrix form of
|
|
107
|
+
# update rule of A[i:]
|
|
108
|
+
A[i,:] = np.linalg.solve(Bj @ Bj.T + lam1*Ik, Bj @ Mkj.T).T
|
|
109
|
+
|
|
110
|
+
O_k = Oij[ Oij[:,1] == j ]
|
|
111
|
+
Mik = M_train[ O_k[:,0], O_k[:,1] ]
|
|
112
|
+
Ai = A[ O_k[:,0],:]
|
|
113
|
+
|
|
114
|
+
# matrix form of
|
|
115
|
+
# update rule of B[:k]
|
|
116
|
+
B[:,j] = np.linalg.solve( Ai.T @ Ai + lam2*Ik, Ai.T @ Mik )
|
|
117
|
+
|
|
118
|
+
if check_convergence(Ac, A, epsilon) and\
|
|
119
|
+
check_convergence(Bc, B, epsilon):
|
|
120
|
+
break
|
|
121
|
+
|
|
122
|
+
return A, B
|
|
123
|
+
|
|
124
|
+
def score(A, B, M_test, nan_value = 0):
|
|
125
|
+
Oij_test = get_Options(M_test, nan_value = nan_value)
|
|
126
|
+
M_test = M_test[Oij_test[:,0], Oij_test[:,1]]
|
|
127
|
+
AiBj = (A @ B)[Oij_test[:,0], Oij_test[:,1]]
|
|
128
|
+
final_error = np.sqrt( np.mean( (M_test - AiBj)**2 ) )
|
|
129
|
+
return final_error
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
if __name__ == "__main__":
|
|
133
|
+
|
|
134
|
+
train_file = "../data/rate_train.csv"
|
|
135
|
+
test_file = "../data/rate_test.csv"
|
|
136
|
+
|
|
137
|
+
rng = np.random.default_rng(12038)
|
|
138
|
+
|
|
139
|
+
sample_train = np.loadtxt(train_file, delimiter=',')
|
|
140
|
+
sample_test = np.loadtxt(test_file , delimiter=',')
|
|
141
|
+
|
|
142
|
+
lam1 = 4
|
|
143
|
+
lam2 = 4
|
|
144
|
+
k = 5
|
|
145
|
+
|
|
146
|
+
(A,B) = ALS(sample_train,
|
|
147
|
+
k = k, lam1 = lam1, lam2 = lam2,
|
|
148
|
+
max_epochs = 100, epsilon = 1e-3,
|
|
149
|
+
rng = rng)
|
|
150
|
+
|
|
151
|
+
final_error = score(A, B, sample_test)
|
|
152
|
+
print("Final RMSE: ", final_error) # Final RMSE: 0.8042507213383732
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# (A,B,_,_) = ALS_legacy(sample_train, sample_test,
|
|
157
|
+
# k = k, lam1 = lam1, lam2 = lam2,
|
|
158
|
+
# max_epochs = 100, epsilon = 1e-3,
|
|
159
|
+
# get_errors = False,rng = rng,)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# import matplotlib.pyplot as plt
|
|
163
|
+
# plt.plot(all_errors)
|
|
164
|
+
# plt.xlabel('Epoch')
|
|
165
|
+
# plt.ylabel('RMSE')
|
|
166
|
+
# plt.title('Convergence of ALS')
|
|
167
|
+
# plt.show()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# with open("./completed_matrix.csv", "w") as f:
|
|
171
|
+
# np.savetxt(f, A @ B, delimiter=",", fmt='%1.4f')
|
phylok-1.0.0/src/RBF.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def distance_matrix(a, b, M=None):
|
|
5
|
+
"""
|
|
6
|
+
l2 norm squared matrix
|
|
7
|
+
|
|
8
|
+
Time complexity:
|
|
9
|
+
If M is full matrix, then O(np^2 + n^2p). Assuming p < n, then O(n^2p).
|
|
10
|
+
If M is a diagonal or vector, then O(n^2p) regardless of the relationship between p and n.
|
|
11
|
+
If M is None, then O(n^2p) regardless of the relationship between p and n.
|
|
12
|
+
|
|
13
|
+
a: shape (m, p)
|
|
14
|
+
b: shape (n, p)
|
|
15
|
+
M: shape (p, p) or None
|
|
16
|
+
|
|
17
|
+
returns: shape (m, n)
|
|
18
|
+
"""
|
|
19
|
+
# a = X_test[:2, :]
|
|
20
|
+
# b = X_train[:3, :]
|
|
21
|
+
# # M = np.array([[2, 0],
|
|
22
|
+
# # [0, 1]])
|
|
23
|
+
# M = np.diag(M)
|
|
24
|
+
|
|
25
|
+
# a = X_train
|
|
26
|
+
# b = X_test
|
|
27
|
+
# M = np.eye(a.shape[1])
|
|
28
|
+
if M is None:
|
|
29
|
+
# O(pn^2) for the whole matrix
|
|
30
|
+
return np.linalg.norm(a[:, None, :] - b[None, :, :], axis=-1)**2
|
|
31
|
+
|
|
32
|
+
else:
|
|
33
|
+
# total time:
|
|
34
|
+
# O(np + np + mnp + mn) = O(mnp) = O(n^2p) if m = n
|
|
35
|
+
if len(M.shape) == 1:
|
|
36
|
+
# O(np)
|
|
37
|
+
A = ((a[ :, None, :] * M) @ a[:, :, None]).reshape(a.shape[0])
|
|
38
|
+
# O(mp)
|
|
39
|
+
B = ((b[ :, None, :] * M) @ b[:, :, None]).reshape(b.shape[0])
|
|
40
|
+
# O(mpn + pn) = O(mnp)
|
|
41
|
+
C = (a * M) @ b.T
|
|
42
|
+
# O(mn)
|
|
43
|
+
return np.add.outer(A,B) - 2*C
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
else:
|
|
47
|
+
# total time:
|
|
48
|
+
# O(mn + mp^2 + np^2 + mpn + p^2n)
|
|
49
|
+
# = O((m + n)p^2 + mpn)
|
|
50
|
+
# if m = n:
|
|
51
|
+
# O(np^2 + n^2p) = O(npp + nnp) <= O(n^2p) if p < n
|
|
52
|
+
|
|
53
|
+
# O(np^2)
|
|
54
|
+
A = (a[ :, None, :] @ M @ a[:, :, None]).reshape(a.shape[0])
|
|
55
|
+
# O(mp^2)
|
|
56
|
+
B = (b[ :, None, :] @ M @ b[:, :, None]).reshape(b.shape[0])
|
|
57
|
+
# O(mpn + p^2n)
|
|
58
|
+
C = a @ M @ b.T
|
|
59
|
+
# O(mn)
|
|
60
|
+
return np.add.outer(A,B) - 2*C
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def nan_sqdist(a, b, use_t=False, T=None, k = 3):
|
|
64
|
+
r"""
|
|
65
|
+
Assuming a and b have 0 where it is missing.
|
|
66
|
+
|
|
67
|
+
a**2 @ Mbf.T = [ a_1^2 [ | |
|
|
68
|
+
... 0/1 ... 0/1
|
|
69
|
+
a_m^2 ] | | ]
|
|
70
|
+
b1 bn <- presence/absence in b
|
|
71
|
+
= [ || a^{1}_1 ||^2 ... || a^{n}_1 ||^2
|
|
72
|
+
|
|
73
|
+
|| a^{1}_m ||^2 ... || a^{n}_m ||^2 ]
|
|
74
|
+
In the sum, this activates l2 norm for overlapped features.
|
|
75
|
+
The same applies to (b**2 @ Maf.T)
|
|
76
|
+
|
|
77
|
+
a @ b.T = [ a_1^T b_1 ... a_1^T b_n
|
|
78
|
+
... ... ...
|
|
79
|
+
a_m^T b_1 ... a_m^T b_n ]
|
|
80
|
+
|
|
81
|
+
thus a**2 @ Mbf.T + (b**2 @ Maf.T).T - 2 * (a @ b.T):
|
|
82
|
+
|
|
83
|
+
= [ ||a^{1}_1||^2 -2a_1^T b_1 +||b^{1}_1||^2 ... ||a^{n}_1||^2 -2a_1^T b_n +||b^{1}_n||^2
|
|
84
|
+
...
|
|
85
|
+
||a^{1}_m||^2 -2a_m^T b_1 +||b^{m}_1||^2 ... ||a^{n}_m||^2 -2a_m^T b_n +||b^{m}_n||^2 ]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
O(n^2p) time complexity.
|
|
89
|
+
"""
|
|
90
|
+
p = a.shape[1]
|
|
91
|
+
|
|
92
|
+
Ma = ~np.isnan(a) # observed masks, shape (m, p), O(mp)
|
|
93
|
+
Mb = ~np.isnan(b) # shape (n, p), O(np)
|
|
94
|
+
a0 = np.where(Ma, a, 0.0) # shape (m, p) zero-filled data, O(mp)
|
|
95
|
+
b0 = np.where(Mb, b, 0.0) # shape (n, p) zero-filled data, O(np)
|
|
96
|
+
|
|
97
|
+
Maf = Ma.astype(a.dtype)
|
|
98
|
+
Mbf = Mb.astype(b.dtype)
|
|
99
|
+
|
|
100
|
+
n = Maf @ Mbf.T # overlap counts, (m, n), O(mpn)
|
|
101
|
+
# R^{m,p} R^{p,n} + (R^{n,p} R^{p,m}).T - 2 R^{m,p} R^{p,n} = R^{m,n}
|
|
102
|
+
# O(mpn + mpn + mnp) = O(mnp) = O(n^2p) if m = n
|
|
103
|
+
S = (a0**2) @ Mbf.T + ((b0**2) @ Maf.T).T - 2 * (a0 @ b0.T)
|
|
104
|
+
|
|
105
|
+
with np.errstate(divide="ignore", invalid="ignore"):
|
|
106
|
+
D = S * (p / n) # n == 0 -> inf or nan
|
|
107
|
+
|
|
108
|
+
D[n == 0] = np.nan
|
|
109
|
+
|
|
110
|
+
if sum(n == 0) > 0:
|
|
111
|
+
k_linear_interpolation(D, k=k, T=T, use_t=use_t)
|
|
112
|
+
|
|
113
|
+
return D
|
|
114
|
+
|
|
115
|
+
def k_linear_interpolation(D, k = 3, T = None, use_t = False):
|
|
116
|
+
r"""
|
|
117
|
+
D: distance matrix with NaN values, shape (m, n)
|
|
118
|
+
k: number of nearest neighbors to use for interpolation
|
|
119
|
+
T: distance matrix with NaN values, shape (m, n)
|
|
120
|
+
use_t: if True, use T for finding nearest neighbors instead of D
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
O(n^2) algorithm
|
|
124
|
+
The algorithm iterates over each row of the distance matrix D,
|
|
125
|
+
and for each row, it finds the k nearest neighbors that have non-NaN values.
|
|
126
|
+
It then replaces the NaN values in that row with the mean of the k
|
|
127
|
+
nearest neighbors' distances.
|
|
128
|
+
"""
|
|
129
|
+
# k = 2
|
|
130
|
+
assert k > 0, "k must be a positive integer"
|
|
131
|
+
|
|
132
|
+
if use_t:
|
|
133
|
+
assert T is not None, "T must be provided when use_t is True"
|
|
134
|
+
|
|
135
|
+
m,n = D.shape
|
|
136
|
+
# O(n^2) for the whole matrix
|
|
137
|
+
for i in range(m):
|
|
138
|
+
# i = 0
|
|
139
|
+
|
|
140
|
+
full_idx = ~np.isnan(D[i,:]) # O(n)
|
|
141
|
+
if sum(full_idx) == n:
|
|
142
|
+
continue
|
|
143
|
+
|
|
144
|
+
# available neighbors
|
|
145
|
+
full_idx[i] = False
|
|
146
|
+
|
|
147
|
+
if sum(full_idx) == 0:
|
|
148
|
+
# this means that there exist a single x_ik value at the row i
|
|
149
|
+
# and there there no more values at column k.
|
|
150
|
+
raise ValueError("All neighbors are NaN for row {}".format(i))
|
|
151
|
+
|
|
152
|
+
if sum(full_idx) <= k:
|
|
153
|
+
k_neighs_idx = full_idx
|
|
154
|
+
else:
|
|
155
|
+
# Get the k nearest neighbors, O(n)
|
|
156
|
+
if use_t:
|
|
157
|
+
k_neighs_idx = np.argpartition(T[i, full_idx], k)[:k]
|
|
158
|
+
|
|
159
|
+
else:
|
|
160
|
+
k_neighs_idx = np.argpartition(D[i, full_idx], k)[:k]
|
|
161
|
+
|
|
162
|
+
# index with full values
|
|
163
|
+
full_idx[i] = True
|
|
164
|
+
|
|
165
|
+
D[i, ~full_idx] = np.mean(D[i, k_neighs_idx])
|
|
166
|
+
|
|
167
|
+
def RBF_kernel(a, b, gamma, M = None, nan_adjust = False, k = 3, T = None, use_t = False):
|
|
168
|
+
"""
|
|
169
|
+
Radial Basis Function
|
|
170
|
+
|
|
171
|
+
Time complexity:
|
|
172
|
+
If M is full matrix, then O(np^2 + n^2p). Assuming p < n, then O(n^2p).
|
|
173
|
+
If M is a diagonal or vector, then O(n^2p) regardless of the relationship between p and n.
|
|
174
|
+
If M is None, then O(n^2p) regardless of the relationship between p and n.
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
a: shape (m, p)\\
|
|
178
|
+
b: shape (n, p)\\
|
|
179
|
+
M: shape (p, p) or None\\
|
|
180
|
+
nan_adjust: if True, then use nan_sqdist to compute the distance matrix, otherwise use distance_matrix\\
|
|
181
|
+
k: number of nearest neighbors to use for interpolation if nan_adjust is True\\
|
|
182
|
+
T: distance matrix with NaN values, shape (m, n)\\
|
|
183
|
+
use_t: if True, use T for finding nearest neighbors instead of D\\
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
returns: shape (m, n)
|
|
187
|
+
"""
|
|
188
|
+
a = np.ascontiguousarray(a, dtype=np.float64)
|
|
189
|
+
b = np.ascontiguousarray(b, dtype=np.float64)
|
|
190
|
+
# data sim
|
|
191
|
+
if nan_adjust:
|
|
192
|
+
# O(n^2p) for the whole matrix and never uses M.
|
|
193
|
+
D = nan_sqdist(a, b, use_t=use_t, T=T, k=k)
|
|
194
|
+
|
|
195
|
+
else:
|
|
196
|
+
D = distance_matrix(a, b, M=M)
|
|
197
|
+
# print("D.shape", D.shape)
|
|
198
|
+
# print("D", D)
|
|
199
|
+
D = np.where(D < 1e-10, 0.0, D) # numerical stability
|
|
200
|
+
tmp_rbf = -gamma * D
|
|
201
|
+
|
|
202
|
+
np.exp(tmp_rbf, tmp_rbf) # RBF kernel. Inplace exponentiation
|
|
203
|
+
return tmp_rbf
|
|
File without changes
|