nystrom-ncut 0.0.1__py3-none-any.whl → 0.0.3__py3-none-any.whl

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.
@@ -1,241 +0,0 @@
1
- import logging
2
- from typing import Literal, Tuple
3
-
4
- import torch
5
-
6
- from .nystrom import (
7
- EigSolverOptions,
8
- OnlineKernel,
9
- OnlineNystrom,
10
- solve_eig,
11
- )
12
- from .propagation_utils import (
13
- affinity_from_features,
14
- run_subgraph_sampling,
15
- )
16
-
17
-
18
- DistanceOptions = Literal["cosine", "euclidean", "rbf"]
19
-
20
-
21
- class LaplacianKernel(OnlineKernel):
22
- def __init__(
23
- self,
24
- affinity_focal_gamma: float,
25
- distance: DistanceOptions,
26
- eig_solver: EigSolverOptions,
27
- ):
28
- self.affinity_focal_gamma = affinity_focal_gamma
29
- self.distance: DistanceOptions = distance
30
- self.eig_solver: EigSolverOptions = eig_solver
31
-
32
- # Anchor matrices
33
- self.anchor_features: torch.Tensor = None # [n x d]
34
- self.A: torch.Tensor = None # [n x n]
35
- self.Ainv: torch.Tensor = None # [n x n]
36
-
37
- # Updated matrices
38
- self.a_r: torch.Tensor = None # [n]
39
- self.b_r: torch.Tensor = None # [n]
40
-
41
- def fit(self, features: torch.Tensor) -> None:
42
- self.anchor_features = features # [n x d]
43
- self.A = affinity_from_features(
44
- self.anchor_features, # [n x d]
45
- affinity_focal_gamma=self.affinity_focal_gamma,
46
- distance=self.distance,
47
- fill_diagonal=False,
48
- ) # [n x n]
49
- U, L = solve_eig(
50
- self.A,
51
- num_eig=features.shape[-1] + 1,
52
- eig_solver=self.eig_solver,
53
- ) # [n x (d + 1)], [d + 1]
54
- self.Ainv = U @ torch.diag(1 / L) @ U.mT # [n x n]
55
- self.a_r = torch.sum(self.A, dim=-1) # [n]
56
- self.b_r = torch.zeros_like(self.a_r) # [n]
57
-
58
- def update(self, features: torch.Tensor) -> torch.Tensor:
59
- B = affinity_from_features(
60
- self.anchor_features, # [n x d]
61
- features, # [m x d]
62
- affinity_focal_gamma=self.affinity_focal_gamma,
63
- distance=self.distance,
64
- fill_diagonal=False,
65
- ) # [n x m]
66
- b_r = torch.sum(B, dim=-1) # [n]
67
- b_c = torch.sum(B, dim=-2) # [m]
68
- self.b_r = self.b_r + b_r # [n]
69
-
70
- rowscale = self.a_r + self.b_r # [n]
71
- colscale = b_c + B.mT @ self.Ainv @ self.b_r # [m]
72
- scale = (rowscale[:, None] * colscale) ** -0.5 # [n x m]
73
- return (B * scale).mT # [m x n]
74
-
75
- def transform(self, features: torch.Tensor = None) -> torch.Tensor:
76
- rowscale = self.a_r + self.b_r # [n]
77
- if features is None:
78
- B = self.A # [n x n]
79
- colscale = rowscale # [n]
80
- else:
81
- B = affinity_from_features(
82
- self.anchor_features, # [n x d]
83
- features, # [m x d]
84
- affinity_focal_gamma=self.affinity_focal_gamma,
85
- distance=self.distance,
86
- fill_diagonal=False,
87
- ) # [n x m]
88
- b_c = torch.sum(B, dim=-2) # [m]
89
- colscale = b_c + B.mT @ self.Ainv @ self.b_r # [m]
90
- scale = (rowscale[:, None] * colscale) ** -0.5 # [n x m]
91
- return (B * scale).mT # [m x n]
92
-
93
-
94
- class NewNCUT(OnlineNystrom):
95
- """Nystrom Normalized Cut for large scale graph."""
96
-
97
- def __init__(
98
- self,
99
- num_eig: int = 100,
100
- affinity_focal_gamma: float = 1.0,
101
- num_sample: int = 10000,
102
- sample_method: Literal["farthest", "random"] = "farthest",
103
- distance: DistanceOptions = "cosine",
104
- eig_solver: EigSolverOptions = "svd_lowrank",
105
- normalize_features: bool = None,
106
- device: str = None,
107
- move_output_to_cpu: bool = False,
108
- matmul_chunk_size: int = 8096,
109
- ):
110
- """
111
- Args:
112
- num_eig (int): number of top eigenvectors to return
113
- affinity_focal_gamma (float): affinity matrix temperature, lower t reduce the not-so-connected edge weights,
114
- smaller t result in more sharp eigenvectors.
115
- num_sample (int): number of samples for Nystrom-like approximation,
116
- reduce only if memory is not enough, increase for better approximation
117
- sample_method (str): subgraph sampling, ['farthest', 'random'].
118
- farthest point sampling is recommended for better Nystrom-approximation accuracy
119
- distance (str): distance metric for affinity matrix, ['cosine', 'euclidean', 'rbf'].
120
- eig_solver (str): eigen decompose solver, ['svd_lowrank', 'lobpcg', 'svd', 'eigh'].
121
- normalize_features (bool): normalize input features before computing affinity matrix,
122
- default 'None' is True for cosine distance, False for euclidean distance and rbf
123
- device (str): device to use for eigen computation,
124
- move to GPU to speeds up a bit (~5x faster)
125
- move_output_to_cpu (bool): move output to CPU, set to True if you have memory issue
126
- matmul_chunk_size (int): chunk size for large-scale matrix multiplication
127
- """
128
- OnlineNystrom.__init__(
129
- self,
130
- n_components=num_eig,
131
- kernel=LaplacianKernel(affinity_focal_gamma, distance, eig_solver),
132
- eig_solver=eig_solver,
133
- chunk_size=matmul_chunk_size,
134
- )
135
- self.num_sample = num_sample
136
- self.sample_method = sample_method
137
- self.distance = distance
138
- self.normalize_features = normalize_features
139
- if self.normalize_features is None:
140
- if distance in ["cosine"]:
141
- self.normalize_features = True
142
- if distance in ["euclidean", "rbf"]:
143
- self.normalize_features = False
144
-
145
- self.device = device
146
- self.move_output_to_cpu = move_output_to_cpu
147
- self.matmul_chunk_size = matmul_chunk_size
148
-
149
- def _fit_helper(
150
- self,
151
- features: torch.Tensor,
152
- precomputed_sampled_indices: torch.Tensor,
153
- ) -> Tuple[torch.Tensor, torch.Tensor]:
154
- # move subgraph gpu to speed up
155
- original_device = features.device
156
- device = original_device if self.device is None else self.device
157
-
158
- _n = features.shape[0]
159
- if self.num_sample >= _n:
160
- logging.info(
161
- f"NCUT nystrom num_sample is larger than number of input samples, nyström approximation is not needed, setting num_sample={_n}"
162
- )
163
- self.num_sample = _n
164
-
165
- # check if features dimension greater than num_eig
166
- if self.eig_solver in ["svd_lowrank", "lobpcg"]:
167
- assert (
168
- _n >= self.n_components * 2
169
- ), "number of nodes should be greater than 2*num_eig"
170
- elif self.eig_solver in ["svd", "eigh"]:
171
- assert (
172
- _n >= self.n_components
173
- ), "number of nodes should be greater than num_eig"
174
-
175
- assert self.distance in ["cosine", "euclidean", "rbf"], "distance should be 'cosine', 'euclidean', 'rbf'"
176
-
177
- if self.normalize_features:
178
- # features need to be normalized for affinity matrix computation (cosine distance)
179
- features = torch.nn.functional.normalize(features, dim=-1)
180
-
181
- if precomputed_sampled_indices is not None:
182
- sampled_indices = precomputed_sampled_indices
183
- else:
184
- sampled_indices = run_subgraph_sampling(
185
- features,
186
- num_sample=self.num_sample,
187
- sample_method=self.sample_method,
188
- )
189
- sampled_features = features[sampled_indices].to(device)
190
- OnlineNystrom.fit(self, sampled_features)
191
-
192
- _n_not_sampled = _n - len(sampled_features)
193
- if _n_not_sampled > 0:
194
- unsampled_indices = torch.full((_n,), True).scatter(0, sampled_indices, False)
195
- unsampled_features = features[unsampled_indices].to(device)
196
- V_unsampled, _ = OnlineNystrom.update(self, unsampled_features)
197
- else:
198
- unsampled_indices = V_unsampled = None
199
- return unsampled_indices, V_unsampled
200
-
201
- def fit(
202
- self,
203
- features: torch.Tensor,
204
- precomputed_sampled_indices: torch.Tensor = None,
205
- ):
206
- """Fit Nystrom Normalized Cut on the input features.
207
- Args:
208
- features (torch.Tensor): input features, shape (n_samples, n_features)
209
- precomputed_sampled_indices (torch.Tensor): precomputed sampled indices, shape (num_sample,)
210
- override the sample_method, if not None
211
- Returns:
212
- (NCUT): self
213
- """
214
- NewNCUT._fit_helper(self, features, precomputed_sampled_indices)
215
- return self
216
-
217
- def fit_transform(
218
- self,
219
- features: torch.Tensor,
220
- precomputed_sampled_indices: torch.Tensor = None,
221
- ) -> Tuple[torch.Tensor, torch.Tensor]:
222
- """
223
- Args:
224
- features (torch.Tensor): input features, shape (n_samples, n_features)
225
- precomputed_sampled_indices (torch.Tensor): precomputed sampled indices, shape (num_sample,)
226
- override the sample_method, if not None
227
-
228
- Returns:
229
- (torch.Tensor): eigen_vectors, shape (n_samples, num_eig)
230
- (torch.Tensor): eigen_values, sorted in descending order, shape (num_eig,)
231
- """
232
- unsampled_indices, V_unsampled = NewNCUT._fit_helper(self, features, precomputed_sampled_indices)
233
- V_sampled, L = OnlineNystrom.transform(self)
234
-
235
- if unsampled_indices is not None:
236
- V = torch.zeros((len(unsampled_indices), self.n_components))
237
- V[~unsampled_indices] = V_sampled
238
- V[unsampled_indices] = V_unsampled
239
- else:
240
- V = V_sampled
241
- return V, L
@@ -1,11 +0,0 @@
1
- nystrom_ncut/__init__.py,sha256=K8a7o9oP9jhG9auqsAFt1KPQMElRUP3_TFxBmRUz8-o,544
2
- nystrom_ncut/ncut_pytorch.py,sha256=f4VHCgOP3tEjn5NIr2wFE4hAGnQIWV6P6W4xuMt0d0I,22426
3
- nystrom_ncut/new_ncut_pytorch.py,sha256=wPG-OAcew4kw0mDMLQPJOetz-9sBfvFmexL7n0JVYjc,10419
4
- nystrom_ncut/nystrom.py,sha256=UOXfhgz-xB2FtKYfn-cwMDNkgCWrM-3yXHtPxOrgEV4,8569
5
- nystrom_ncut/propagation_utils.py,sha256=quykDk1RgFyHEUloRBcapSocq9Wvkk3hG_TYx-Tue6A,13813
6
- nystrom_ncut/visualize_utils.py,sha256=3TEdXF_H7sBUQFz1nK3QemmlKqRteo5BKkno1LozVTg,21840
7
- nystrom_ncut-0.0.1.dist-info/LICENSE,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
8
- nystrom_ncut-0.0.1.dist-info/METADATA,sha256=kj900xV7RSfTSW8jyzjhrGV2z1Ttzn5UoTFOlHpfZg8,6058
9
- nystrom_ncut-0.0.1.dist-info/WHEEL,sha256=A3WOREP4zgxI0fKrHUG8DC8013e3dK3n7a6HDbcEIwE,91
10
- nystrom_ncut-0.0.1.dist-info/top_level.txt,sha256=j7g_j0S048EvguFFnGgD5Ewd3r2H6klsxd5A4dd-wHw,13
11
- nystrom_ncut-0.0.1.dist-info/RECORD,,