boostrsa 0.0.1.dev0__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.
@@ -0,0 +1,22 @@
1
+
2
+ MIT License
3
+
4
+ Copyright (c) 2024 seojin yoon
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,27 @@
1
+ Metadata-Version: 2.1
2
+ Name: boostrsa
3
+ Version: 0.0.1.dev0
4
+ Summary: This is toolbox for boosting calculation speed using GPU
5
+ Author: seojin
6
+ Author-email: pures1@hanyang.ac.kr
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE.txt
13
+
14
+
15
+ # Boostrsa
16
+
17
+ blahblah
18
+
19
+ ## Setup
20
+
21
+ ### Dependencies
22
+
23
+ numba
24
+ cupy
25
+
26
+ ### PIP
27
+
@@ -0,0 +1,14 @@
1
+
2
+ # Boostrsa
3
+
4
+ blahblah
5
+
6
+ ## Setup
7
+
8
+ ### Dependencies
9
+
10
+ numba
11
+ cupy
12
+
13
+ ### PIP
14
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,30 @@
1
+
2
+ import subprocess
3
+ import re
4
+ from setuptools import setup, find_packages
5
+
6
+ with open("README.md", "r") as fh:
7
+ long_description = fh.read()
8
+
9
+ setup(
10
+ name = "boostrsa",
11
+ version = "0.0.1dev",
12
+ author = "seojin",
13
+ author_email = "pures1@hanyang.ac.kr",
14
+ description = "This is toolbox for boosting calculation speed using GPU",
15
+ long_description = long_description,
16
+ long_description_content_type="text/markdown",
17
+ packages = find_packages(where = "src"),
18
+ package_dir = {"": "src"},
19
+ classifiers = [
20
+ "Programming Language :: Python :: 3",
21
+ "License :: OSI Approved :: MIT License",
22
+ "Operating System :: OS Independent",
23
+ ],
24
+ python_requires='>=3.9',
25
+ install_requires = [
26
+ "numpy",
27
+ "pandas",
28
+ "tqdm",
29
+ ]
30
+ )
@@ -0,0 +1,27 @@
1
+ Metadata-Version: 2.1
2
+ Name: boostrsa
3
+ Version: 0.0.1.dev0
4
+ Summary: This is toolbox for boosting calculation speed using GPU
5
+ Author: seojin
6
+ Author-email: pures1@hanyang.ac.kr
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE.txt
13
+
14
+
15
+ # Boostrsa
16
+
17
+ blahblah
18
+
19
+ ## Setup
20
+
21
+ ### Dependencies
22
+
23
+ numba
24
+ cupy
25
+
26
+ ### PIP
27
+
@@ -0,0 +1,17 @@
1
+ LICENSE.txt
2
+ README.md
3
+ setup.py
4
+ src/boostrsa.egg-info/PKG-INFO
5
+ src/boostrsa.egg-info/SOURCES.txt
6
+ src/boostrsa.egg-info/dependency_links.txt
7
+ src/boostrsa.egg-info/requires.txt
8
+ src/boostrsa.egg-info/top_level.txt
9
+ src/cores/__init__.py
10
+ src/cores/cpgpu/__init__.py
11
+ src/cores/cpgpu/stats.py
12
+ src/cores/cpu/__init__.py
13
+ src/cores/cpu/matrix.py
14
+ src/cores/gpu/__init__.py
15
+ src/cores/gpu/basic_operations.py
16
+ src/cores/gpu/mask.py
17
+ src/cores/gpu/matrix.py
@@ -0,0 +1,3 @@
1
+ numpy
2
+ pandas
3
+ tqdm
File without changes
File without changes
@@ -0,0 +1,130 @@
1
+
2
+ import numpy as np
3
+ import cupy as cp
4
+ from numba import cuda, jit
5
+ from boostrsa.src.types import ShrinkageMethod
6
+ from boostrsa.src.gpu.basic_operations import outer_sum_square, outer_sum
7
+ from boostrsa.src.gpu.matrix import diag, eyes
8
+ from boostrsa.src.gpu.basic_operations import scaling
9
+
10
+ def _covariance_eye(residuals, threads_per_block = 1024):
11
+ """
12
+ Computes an optimal shrinkage estimate of a sample covariance matrix as described by the following publication:
13
+ **matrix should be demeaned before!
14
+
15
+ Ledoit and Wolfe (2004): "A well-conditioned estimator for large-dimensional covariance matrices"
16
+
17
+ :param residuals(np.ndarray): , shape: (#data, #n_point, #n_channel)
18
+ """
19
+ print("shrinakge method:", ShrinkageMethod.shrinkage_eye)
20
+
21
+ # Constant
22
+ data_len = len(residuals)
23
+ n_point = residuals.shape[1]
24
+ n_channel = residuals.shape[2]
25
+
26
+ n_block = int(np.ceil(data_len / threads_per_block))
27
+
28
+ # sum
29
+ out_sum_device = cuda.to_device(np.zeros((data_len, n_channel, n_channel)))
30
+
31
+ # sum square
32
+ out_sum_square_device = cuda.to_device(np.zeros((data_len, n_channel, n_channel)))
33
+
34
+ # Calc sum, sum square
35
+ outer_sum[n_block, threads_per_block](residuals, out_sum_device)
36
+ outer_sum_square[n_block, threads_per_block](residuals, out_sum_square_device)
37
+
38
+ # b2
39
+ s = out_sum_device.copy_to_host() / n_point
40
+ s2 = out_sum_square_device.copy_to_host() / n_point
41
+ b2 = np.sum(s2 - s * s, axis = (1, 2)) / n_point
42
+
43
+ # calculate the scalar estimators to find the optimal shrinkage:
44
+ # m, d^2, b^2 as in Ledoit & Wolfe paper
45
+ # m - shape: (data_len)
46
+ # d2 - shape: (data_len)
47
+ # b2 - shape: (data_len)
48
+ repeat_eyes = np.repeat(np.eye(n_channel)[:, :, np.newaxis], data_len, axis = 2).T
49
+
50
+ diag_s = np.diagonal(s, axis1 = 1, axis2 = 2)
51
+ m = (np.sum(diag_s, axis = 1) / n_channel)
52
+ d2 = np.sum((s - m[:, None, None] * repeat_eyes) ** 2, axis = (1, 2))
53
+
54
+ b2 = np.minimum(d2, b2)
55
+
56
+ # shrink covariance matrix
57
+ s_shrink = (b2 / d2 * m)[:, None, None] * repeat_eyes + ((d2-b2) / d2)[:, None, None] * s
58
+
59
+ # correction for degrees of freedom
60
+ dof = n_point - 1
61
+ s_shrink = s_shrink * n_point / dof
62
+
63
+ return s_shrink
64
+
65
+ def _covariance_diag(residuals, threads_per_block = 1024):
66
+ """
67
+ Calculate covariance
68
+ **matrix should be demeaned before!
69
+
70
+ Schäfer, J., & Strimmer, K. (2005). "A Shrinkage Approach to Large-Scale
71
+ Covariance Matrix Estimation and Implications for Functional Genomics.
72
+
73
+ :param residuals(np.ndarray): , shape: (#data, #n_point, #n_channel)
74
+ """
75
+ print("shrinakge method:", ShrinkageMethod.shrinkage_diag)
76
+
77
+ # Constant
78
+ data_len = len(residuals)
79
+ n_point = residuals.shape[1]
80
+ n_channel = residuals.shape[2]
81
+
82
+ n_block = int(np.ceil(data_len / threads_per_block))
83
+
84
+ # sum
85
+ out_sum_device = cuda.to_device(np.zeros((data_len, n_channel, n_channel)))
86
+
87
+ # sum square
88
+ out_sum_square_device = cuda.to_device(np.zeros((data_len, n_channel, n_channel)))
89
+
90
+ # Calc sum, sum square
91
+ outer_sum[n_block, threads_per_block](residuals, out_sum_device)
92
+ outer_sum_square[n_block, threads_per_block](residuals, out_sum_square_device)
93
+
94
+ # s
95
+ dof = n_point - 1
96
+ s = out_sum_device.copy_to_host() / dof
97
+
98
+ # var
99
+ stack_var_device = cuda.to_device(np.zeros((data_len, n_channel)))
100
+ diag[n_block, threads_per_block](s, stack_var_device)
101
+
102
+ # std
103
+ stack_std = np.sqrt(stack_var_device)
104
+
105
+ # sum mean
106
+ stack_s_mean = out_sum_device / np.expand_dims(stack_std, 1) / np.expand_dims(stack_std, 2) / (n_point - 1)
107
+
108
+ # s2 mean
109
+ stack_s2_mean = out_sum_square_device / np.expand_dims(stack_var_device, 1) / np.expand_dims(stack_var_device, 2) / (n_point - 1)
110
+
111
+ # var_hat
112
+ stack_var_hat = n_point / dof ** 2 * (stack_s2_mean - stack_s_mean ** 2)
113
+
114
+ # mask
115
+ mask = ~np.eye(n_channel, dtype=bool)
116
+
117
+ # lamb
118
+ stack_lamb_device = np.sum(stack_var_hat[:, mask], axis = 1) / np.sum(stack_s_mean[:, mask] ** 2, axis = 1)
119
+ stack_lamb_device = cp.maximum(cp.minimum(cp.array(stack_lamb_device), 1), 0)
120
+
121
+ # Scaling
122
+ stack_scaling_mats_device = cuda.to_device(np.zeros((data_len, n_channel, n_channel)))
123
+ eyes[n_block, threads_per_block](stack_scaling_mats_device)
124
+
125
+ scaling[n_block, threads_per_block](stack_scaling_mats_device, stack_lamb_device)
126
+ stack_s_shrink = s * stack_scaling_mats_device
127
+
128
+ return stack_s_shrink
129
+
130
+
File without changes
@@ -0,0 +1,44 @@
1
+
2
+ import numpy as np
3
+
4
+ def convert_1d_to_symmertic(a_1d, size, k = 0):
5
+ """
6
+ Convert 1d array to symmetric matrix
7
+
8
+ :param a_1d(1d array):
9
+ :param size: matrix size
10
+ :param k(int): offset
11
+
12
+ return (np.array)
13
+ """
14
+
15
+ # put it back into a 2D symmetric array
16
+
17
+ X = np.zeros((size,size))
18
+ X[np.triu_indices(size, k = 0)] = a_1d
19
+ X = X + X.T - np.diag(np.diag(X))
20
+
21
+ return X
22
+
23
+ def mean_fold_variance(variances, fold_info):
24
+ """
25
+ Calculate fold variacne from fold info
26
+
27
+ :param variances: variances (#data, #cov.shape)
28
+ :param fold_info(2d array): fold information - [[fold1, fold2], ...]
29
+
30
+ return (np.array) - (#data * fold_len, cov.shape)
31
+ """
32
+ n_d = len(variances)
33
+
34
+ result_variances = []
35
+ for i in range(n_d):
36
+ for fold1_i, fold2_i in fold_info:
37
+ cov1 = variances[i][fold1_i]
38
+ cov2 = variances[i][fold2_i]
39
+
40
+ result_variances.append((cov1 + cov2) / 2)
41
+
42
+ return np.array(result_variances)
43
+
44
+
File without changes
@@ -0,0 +1,61 @@
1
+
2
+ from numba import cuda, jit
3
+
4
+ @cuda.jit
5
+ def outer_sum(matrices, out):
6
+ i = cuda.grid(1)
7
+
8
+ if i < len(matrices):
9
+ matrix = matrices[i]
10
+
11
+ for m_line in matrix:
12
+ for j, e1 in enumerate(m_line):
13
+ for k, e2 in enumerate(m_line):
14
+ out[i][j][k] += e1 * e2
15
+
16
+ @cuda.jit
17
+ def outer_sum_square(matrices, out):
18
+ i = cuda.grid(1)
19
+
20
+ if i < len(matrices):
21
+ matrix = matrices[i]
22
+
23
+ for m_line in matrix:
24
+ for j, e1 in enumerate(m_line):
25
+ for k, e2 in enumerate(m_line):
26
+ out[i][j][k] += (e1 * e2) ** 2
27
+
28
+ @cuda.jit
29
+ def scaling(out, lambs):
30
+ i = cuda.grid(1)
31
+ lamb = lambs[i]
32
+
33
+ nd = out.shape[0]
34
+ nr = out.shape[1]
35
+ nc = out.shape[2]
36
+
37
+ if i < len(out):
38
+ for j in range(nr):
39
+ for k in range(nc):
40
+ if j != k:
41
+ out[i][j][k] = (1 - lamb)
42
+
43
+ @cuda.jit(device=True, inline=True)
44
+ def matmul(a,b, out):
45
+ """
46
+ Matrix multiplication a @ b
47
+
48
+ :param a(np.array): 2d matrix
49
+ :param b(np.array): 2d matrix
50
+ :param out(device array): output
51
+ """
52
+ ar,ac = a.shape
53
+ br,bc = b.shape
54
+
55
+ for i in range(ar):
56
+ for j in range(bc):
57
+ for k in range(ac): # or br
58
+ out[i,j] += a[i,k] * b[k,j]
59
+ return out
60
+
61
+
@@ -0,0 +1,23 @@
1
+
2
+ from numba import cuda, jit
3
+
4
+ @cuda.jit
5
+ def set_mask(neighbors, brain_1d_indexes, out):
6
+ """
7
+ Set neighbor mask(iterate over all neighbors)
8
+
9
+ :param neighbors(np.array): list of neighbor , shape: (#center, #neighbor)
10
+ :param brain_1d_indexes(np.array): , shape: #channel
11
+ :param out: masked_residual, output device memory , shape: (#center, #channel)
12
+ """
13
+ i = cuda.grid(1)
14
+
15
+ if i < len(neighbors):
16
+ neighbor_positions = neighbors[i]
17
+
18
+ for neighbor_pos in neighbor_positions:
19
+ for brain_i, brain_pos in enumerate(brain_1d_indexes):
20
+ if brain_pos == neighbor_pos:
21
+ out[i][brain_i] = 1
22
+
23
+
@@ -0,0 +1,125 @@
1
+
2
+ from numba import cuda, jit
3
+ from basic_operations import matmul
4
+
5
+ @jit(nopython=True)
6
+ def upper_tri_1d_index(i, j, n_col, k):
7
+ """
8
+ Get upper triangle 1d index
9
+
10
+ if k = 1)
11
+
12
+ (0,1), (0,2), (0,3), (0,4) -> 0, 1, 2, 3
13
+ (1,2), (1,3), (1,4) -> 4, 5, 6
14
+ (2,3), (2,4) -> 7, 8
15
+ (3,3) -> 9
16
+
17
+ :param i: row index
18
+ :param j: column index
19
+ :param n_col: column number
20
+ :param k: #padding
21
+ """
22
+ if i > j:
23
+ return None
24
+ else:
25
+ sum_val = 0
26
+ for loop_row_i in range(0, i):
27
+ sum_val += (n_col - k) # maximum filled count of row.
28
+ sum_val += (-1) * loop_row_i # non-filled element is increased as row value is increased.
29
+ return sum_val + (j - i - k)
30
+
31
+ @jit(nopython=True)
32
+ def lower_tri_1d_index(i, j):
33
+ """
34
+ Get lower triangle 1d index
35
+
36
+ :param i: row index
37
+ :param j: column index
38
+ """
39
+
40
+ if i < j:
41
+ return None
42
+ else:
43
+ total_fill = 0
44
+ for pr_row_i in range(1, i + 1):
45
+ total_fill += (pr_row_i - 1)
46
+ return total_fill + j
47
+
48
+ @cuda.jit
49
+ def diag(matrices, out):
50
+ i = cuda.grid(1)
51
+
52
+ if i < len(matrices):
53
+ matrix = matrices[i]
54
+
55
+ n_row = len(matrix)
56
+ for j in range(n_row):
57
+ out[i][j] = matrix[j][j]
58
+
59
+ @cuda.jit
60
+ def eyes(out):
61
+ i = cuda.grid(1)
62
+
63
+ nd = out.shape[0]
64
+ nr = out.shape[1]
65
+ nc = out.shape[2]
66
+
67
+ if i < len(out):
68
+ for j in range(nr):
69
+ out[i][j][j] = 1
70
+
71
+ @cuda.jit
72
+ def rdm_from_kernel(kernels, div, out):
73
+ """
74
+ Calculate rdm matrix
75
+
76
+ :param kernels(Device array): kernel, shape: (n_data, n_fold, n_cond, n_cond))
77
+ :param div(int): div value
78
+ :param out(Device array): rdm output, shape: (n_data, n_fold, n_dissim)
79
+ """
80
+ n_data = kernels.shape[0]
81
+ n_validation = kernels.shape[1]
82
+ n_cond = kernels.shape[-1]
83
+
84
+ i, j = cuda.grid(2)
85
+
86
+ if i < n_data:
87
+ if j < n_validation:
88
+ kernel = kernels[i][j]
89
+
90
+ for row_i in range(n_cond):
91
+ for column_i in range(n_cond):
92
+ if row_i < column_i:
93
+ dissim_i = int(upper_tri_1d_index(row_i, column_i, n_cond, 1))
94
+
95
+ # Assign dissim value
96
+ v1 = kernel[row_i][row_i] + kernel[column_i][column_i]
97
+ v2 = kernel[row_i][column_i] + kernel[column_i][row_i]
98
+ out[i][j][dissim_i] = (v1 - v2) / div
99
+
100
+ @cuda.jit
101
+ def calc_kernel(measurments, precisions, fold_info, out1, out2):
102
+ """
103
+ Calculate rdm kernel for calculating crossnobis
104
+
105
+ (2048, 4, 8, 93)
106
+
107
+ :param measurments(Device array): , shape: (n_data, n_run, n_cond, n_neighbor)
108
+ :param precisions(Device array): , shape: (n_data, n_fold, n_neighbor, n_neighbor)
109
+ :param fold_info(Device array): fold information - [[fold1, fold2], ...]
110
+ :param out1(Device array): intermediate matmul output , shape: (n_data, n_fold, n_cond, n_neighbor)
111
+ :param out2(Device array): kernel output , shape: (n_data, n_fold, n_cond, n_cond))
112
+ """
113
+ n_data = out1.shape[0]
114
+ n_validation = out1.shape[1]
115
+
116
+ i, j = cuda.grid(2)
117
+ if i < n_data:
118
+ if j < n_validation:
119
+ data1_i, data2_i = fold_info[j]
120
+
121
+ # measurements1 @ noise @ measurements2.T
122
+ matmul(measurments[i][data1_i], precisions[i][j], out1[i][j])
123
+ matmul(out1[i][j], measurments[i][data2_i].T, out2[i][j])
124
+
125
+