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 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,5 @@
1
+ # KRGEE
2
+ Kernel Ridge GEE
3
+
4
+
5
+ Development repo
@@ -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,3 @@
1
+ numpy
2
+ scipy
3
+ phylokrr
@@ -0,0 +1 @@
1
+ phyloK
phylok-1.0.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
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
+ )
@@ -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')
@@ -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