@@ -13,6 +13,7 @@ import numpy as np
13
13
import scipy.sparse as sp
14
14
cimport numpy as np
15
15
cimport cython
16
+ from cython cimport floating
16
17
17
18
from ..utils.extmath import norm
18
19
from sklearn.utils.sparsefuncs_fast import assign_rows_csr
@@ -23,18 +24,19 @@ ctypedef np.int32_t INT
23
24
24
25
cdef extern from " cblas.h" :
25
26
double ddot " cblas_ddot" (int N, double * X, int incX, double * Y, int incY)
27
+ float sdot " cblas_sdot" (int N, float * X, int incX, float * Y, int incY)
26
28
27
29
np.import_array()
28
30
29
31
30
32
@ cython.boundscheck (False )
31
33
@ cython.wraparound (False )
32
34
@ cython.cdivision (True )
33
- cpdef DOUBLE _assign_labels_array(np.ndarray[DOUBLE , ndim= 2 ] X,
34
- np.ndarray[DOUBLE , ndim= 1 ] x_squared_norms,
35
- np.ndarray[DOUBLE , ndim= 2 ] centers,
35
+ cpdef DOUBLE _assign_labels_array(np.ndarray[floating , ndim= 2 ] X,
36
+ np.ndarray[floating , ndim= 1 ] x_squared_norms,
37
+ np.ndarray[floating , ndim= 2 ] centers,
36
38
np.ndarray[INT, ndim= 1 ] labels,
37
- np.ndarray[DOUBLE , ndim= 1 ] distances):
39
+ np.ndarray[floating , ndim= 1 ] distances):
38
40
""" Compute label assignment and inertia for a dense array
39
41
40
42
Return the inertia (sum of squared distances to the centers).
@@ -43,33 +45,52 @@ cpdef DOUBLE _assign_labels_array(np.ndarray[DOUBLE, ndim=2] X,
43
45
unsigned int n_clusters = centers.shape[0 ]
44
46
unsigned int n_features = centers.shape[1 ]
45
47
unsigned int n_samples = X.shape[0 ]
46
- unsigned int x_stride = X.strides[ 1 ] / sizeof(DOUBLE)
47
- unsigned int center_stride = centers.strides[ 1 ] / sizeof(DOUBLE)
48
+ unsigned int x_stride
49
+ unsigned int center_stride
48
50
unsigned int sample_idx, center_idx, feature_idx
49
51
unsigned int store_distances = 0
50
52
unsigned int k
53
+ np.ndarray[floating, ndim= 1 ] center_squared_norms
54
+ # the following variables are always double cause make them floating
55
+ # does not save any memory, but makes the code much bigger
51
56
DOUBLE inertia = 0.0
52
57
DOUBLE min_dist
53
58
DOUBLE dist
54
- np.ndarray[DOUBLE, ndim= 1 ] center_squared_norms = np.zeros(
55
- n_clusters, dtype = np.float64)
59
+
60
+ if floating is float :
61
+ center_squared_norms = np.zeros(n_clusters, dtype = np.float32)
62
+ x_stride = X.strides[1 ] / sizeof(float )
63
+ center_stride = centers.strides[1 ] / sizeof(float )
64
+ else :
65
+ center_squared_norms = np.zeros(n_clusters, dtype = np.float64)
66
+ x_stride = X.strides[1 ] / sizeof(DOUBLE)
67
+ center_stride = centers.strides[1 ] / sizeof(DOUBLE)
56
68
57
69
if n_samples == distances.shape[0 ]:
58
70
store_distances = 1
59
71
60
72
for center_idx in range (n_clusters):
61
- center_squared_norms[center_idx] = ddot(
62
- n_features, & centers[center_idx, 0 ], center_stride,
63
- & centers[center_idx, 0 ], center_stride)
73
+ if floating is float :
74
+ center_squared_norms[center_idx] = sdot(
75
+ n_features, & centers[center_idx, 0 ], center_stride,
76
+ & centers[center_idx, 0 ], center_stride)
77
+ else :
78
+ center_squared_norms[center_idx] = ddot(
79
+ n_features, & centers[center_idx, 0 ], center_stride,
80
+ & centers[center_idx, 0 ], center_stride)
64
81
65
82
for sample_idx in range (n_samples):
66
83
min_dist = - 1
67
84
for center_idx in range (n_clusters):
68
85
dist = 0.0
69
86
# hardcoded: minimize euclidean distance to cluster center:
70
87
# ||a - b||^2 = ||a||^2 + ||b||^2 -2 <a, b>
71
- dist += ddot(n_features, & X[sample_idx, 0 ], x_stride,
72
- & centers[center_idx, 0 ], center_stride)
88
+ if floating is float :
89
+ dist += sdot(n_features, & X[sample_idx, 0 ], x_stride,
90
+ & centers[center_idx, 0 ], center_stride)
91
+ else :
92
+ dist += ddot(n_features, & X[sample_idx, 0 ], x_stride,
93
+ & centers[center_idx, 0 ], center_stride)
73
94
dist *= - 2
74
95
dist += center_squared_norms[center_idx]
75
96
dist += x_squared_norms[sample_idx]
@@ -87,16 +108,16 @@ cpdef DOUBLE _assign_labels_array(np.ndarray[DOUBLE, ndim=2] X,
87
108
@ cython.boundscheck (False )
88
109
@ cython.wraparound (False )
89
110
@ cython.cdivision (True )
90
- cpdef DOUBLE _assign_labels_csr(X, np.ndarray[DOUBLE , ndim= 1 ] x_squared_norms,
91
- np.ndarray[DOUBLE , ndim= 2 ] centers,
111
+ cpdef DOUBLE _assign_labels_csr(X, np.ndarray[floating , ndim= 1 ] x_squared_norms,
112
+ np.ndarray[floating , ndim= 2 ] centers,
92
113
np.ndarray[INT, ndim= 1 ] labels,
93
- np.ndarray[DOUBLE , ndim= 1 ] distances):
114
+ np.ndarray[floating , ndim= 1 ] distances):
94
115
""" Compute label assignment and inertia for a CSR input
95
116
96
117
Return the inertia (sum of squared distances to the centers).
97
118
"""
98
119
cdef:
99
- np.ndarray[DOUBLE , ndim= 1 ] X_data = X.data
120
+ np.ndarray[floating , ndim= 1 ] X_data = X.data
100
121
np.ndarray[INT, ndim= 1 ] X_indices = X.indices
101
122
np.ndarray[INT, ndim= 1 ] X_indptr = X.indptr
102
123
unsigned int n_clusters = centers.shape[0 ]
@@ -105,18 +126,28 @@ cpdef DOUBLE _assign_labels_csr(X, np.ndarray[DOUBLE, ndim=1] x_squared_norms,
105
126
unsigned int store_distances = 0
106
127
unsigned int sample_idx, center_idx, feature_idx
107
128
unsigned int k
129
+ np.ndarray[floating, ndim= 1 ] center_squared_norms
130
+ # the following variables are always double cause make them floating
131
+ # does not save any memory, but makes the code much bigger
108
132
DOUBLE inertia = 0.0
109
133
DOUBLE min_dist
110
134
DOUBLE dist
111
- np.ndarray[DOUBLE, ndim= 1 ] center_squared_norms = np.zeros(
112
- n_clusters, dtype = np.float64)
135
+
136
+ if floating is float :
137
+ center_squared_norms = np.zeros(n_clusters, dtype = np.float32)
138
+ else :
139
+ center_squared_norms = np.zeros(n_clusters, dtype = np.float64)
113
140
114
141
if n_samples == distances.shape[0 ]:
115
142
store_distances = 1
116
143
117
144
for center_idx in range (n_clusters):
118
- center_squared_norms[center_idx] = ddot(
119
- n_features, & centers[center_idx, 0 ], 1 , & centers[center_idx, 0 ], 1 )
145
+ if floating is float :
146
+ center_squared_norms[center_idx] = sdot(
147
+ n_features, & centers[center_idx, 0 ], 1 , & centers[center_idx, 0 ], 1 )
148
+ else :
149
+ center_squared_norms[center_idx] = ddot(
150
+ n_features, & centers[center_idx, 0 ], 1 , & centers[center_idx, 0 ], 1 )
120
151
121
152
for sample_idx in range (n_samples):
122
153
min_dist = - 1
@@ -142,18 +173,18 @@ cpdef DOUBLE _assign_labels_csr(X, np.ndarray[DOUBLE, ndim=1] x_squared_norms,
142
173
@ cython.boundscheck (False )
143
174
@ cython.wraparound (False )
144
175
@ cython.cdivision (True )
145
- def _mini_batch_update_csr (X , np.ndarray[DOUBLE , ndim = 1 ] x_squared_norms,
146
- np.ndarray[DOUBLE , ndim = 2 ] centers,
176
+ def _mini_batch_update_csr (X , np.ndarray[floating , ndim = 1 ] x_squared_norms,
177
+ np.ndarray[floating , ndim = 2 ] centers,
147
178
np.ndarray[INT , ndim = 1 ] counts,
148
179
np.ndarray[INT , ndim = 1 ] nearest_center,
149
- np.ndarray[DOUBLE , ndim = 1 ] old_center,
180
+ np.ndarray[floating , ndim = 1 ] old_center,
150
181
int compute_squared_diff ):
151
182
""" Incremental update of the centers for sparse MiniBatchKMeans.
152
183
153
184
Parameters
154
185
----------
155
186
156
- X: CSR matrix, dtype float64
187
+ X: CSR matrix, dtype float
157
188
The complete (pre allocated) training set as a CSR matrix.
158
189
159
190
centers: array, shape (n_clusters, n_features)
@@ -179,7 +210,7 @@ def _mini_batch_update_csr(X, np.ndarray[DOUBLE, ndim=1] x_squared_norms,
179
210
of the algorithm.
180
211
"""
181
212
cdef:
182
- np.ndarray[DOUBLE , ndim= 1 ] X_data = X.data
213
+ np.ndarray[floating , ndim= 1 ] X_data = X.data
183
214
np.ndarray[int , ndim= 1 ] X_indices = X.indices
184
215
np.ndarray[int , ndim= 1 ] X_indptr = X.indptr
185
216
unsigned int n_samples = X.shape[0 ]
@@ -245,9 +276,9 @@ def _mini_batch_update_csr(X, np.ndarray[DOUBLE, ndim=1] x_squared_norms,
245
276
@ cython.boundscheck (False )
246
277
@ cython.wraparound (False )
247
278
@ cython.cdivision (True )
248
- def _centers_dense (np.ndarray[DOUBLE , ndim = 2 ] X,
279
+ def _centers_dense (np.ndarray[floating , ndim = 2 ] X,
249
280
np.ndarray[INT , ndim = 1 ] labels, int n_clusters ,
250
- np.ndarray[DOUBLE , ndim = 1 ] distances):
281
+ np.ndarray[floating , ndim = 1 ] distances):
251
282
""" M step of the K-means EM algorithm
252
283
253
284
Computation of cluster centers / means.
@@ -275,7 +306,12 @@ def _centers_dense(np.ndarray[DOUBLE, ndim=2] X,
275
306
n_samples = X.shape[0 ]
276
307
n_features = X.shape[1 ]
277
308
cdef int i, j, c
278
- cdef np.ndarray[DOUBLE, ndim= 2 ] centers = np.zeros((n_clusters, n_features))
309
+ cdef np.ndarray[floating, ndim= 2 ] centers
310
+ if floating is float :
311
+ centers = np.zeros((n_clusters, n_features), dtype = np.float32)
312
+ else :
313
+ centers = np.zeros((n_clusters, n_features), dtype = np.float64)
314
+
279
315
n_samples_in_cluster = bincount(labels, minlength = n_clusters)
280
316
empty_clusters = np.where(n_samples_in_cluster == 0 )[0 ]
281
317
# maybe also relocate small clusters?
@@ -303,7 +339,7 @@ def _centers_dense(np.ndarray[DOUBLE, ndim=2] X,
303
339
@ cython.wraparound (False )
304
340
@ cython.cdivision (True )
305
341
def _centers_sparse (X , np.ndarray[INT , ndim = 1 ] labels, n_clusters ,
306
- np.ndarray[DOUBLE , ndim = 1 ] distances):
342
+ np.ndarray[floating , ndim = 1 ] distances):
307
343
""" M step of the K-means EM algorithm
308
344
309
345
Computation of cluster centers / means.
@@ -329,19 +365,23 @@ def _centers_sparse(X, np.ndarray[INT, ndim=1] labels, n_clusters,
329
365
cdef int n_features = X.shape[1 ]
330
366
cdef int curr_label
331
367
332
- cdef np.ndarray[DOUBLE , ndim= 1 ] data = X.data
368
+ cdef np.ndarray[floating , ndim= 1 ] data = X.data
333
369
cdef np.ndarray[int , ndim= 1 ] indices = X.indices
334
370
cdef np.ndarray[int , ndim= 1] indptr = X.indptr
335
371
336
- cdef np.ndarray[DOUBLE, ndim= 2 , mode= " c" ] centers = \
337
- np.zeros((n_clusters, n_features))
372
+ cdef np.ndarray[floating, ndim= 2 , mode= " c" ] centers
338
373
cdef np.ndarray[np.npy_intp, ndim= 1 ] far_from_centers
339
374
cdef np.ndarray[np.npy_intp, ndim= 1 , mode= " c" ] n_samples_in_cluster = \
340
375
bincount(labels, minlength = n_clusters)
341
376
cdef np.ndarray[np.npy_intp, ndim= 1 , mode= " c" ] empty_clusters = \
342
377
np.where(n_samples_in_cluster == 0 )[0 ]
343
378
cdef int n_empty_clusters = empty_clusters.shape[0 ]
344
379
380
+ if floating is float :
381
+ centers = np.zeros((n_clusters, n_features), dtype = np.float32)
382
+ else :
383
+ centers = np.zeros((n_clusters, n_features), dtype = np.float64)
384
+
345
385
# maybe also relocate small clusters?
346
386
347
387
if n_empty_clusters > 0 :
0 commit comments