128 Details<Index_> run(
const Matrix_& data,
const Cluster_ ncenters, Float_*
const centers, Cluster_*
const clusters)
const {
129 const auto nobs = data.num_observations();
130 if (internal::is_edge_case(nobs, ncenters)) {
131 return internal::process_edge_case(data, ncenters, centers, clusters);
134 auto total_sampled = sanisizer::create<std::vector<unsigned long long> >(ncenters);
135 auto last_changed = sanisizer::create<std::vector<unsigned long long> >(ncenters);
136 auto last_sampled = sanisizer::create<std::vector<unsigned long long> >(ncenters);
137 auto previous = sanisizer::create<std::vector<Cluster_> >(nobs);
139 const decltype(I(nobs)) actual_batch_size = sanisizer::min(nobs, my_options.
batch_size);
140 sanisizer::cast<std::size_t>(actual_batch_size);
141 auto chosen = sanisizer::create<std::vector<Index_> >(actual_batch_size);
144 const auto ndim = data.num_dimensions();
145 internal::QuickSearch<Float_, Cluster_> index;
149 aarand::sample(nobs, actual_batch_size, chosen.data(), eng);
151 for (
const auto o : chosen) {
152 previous[o] = clusters[o];
156 index.reset(ndim, ncenters, centers);
157 parallelize(my_options.
num_threads, actual_batch_size, [&](
const int,
const Index_ start,
const Index_ length) ->
void {
158 auto work = data.new_extractor(chosen.data() + start, static_cast<std::size_t>(length));
159 for (Index_ s = start, end = start + length; s < end; ++s) {
160 const auto ptr = work->get_observation();
161 clusters[chosen[s]] = index.find(ptr);
166 auto work = data.new_extractor(chosen.data(),
static_cast<std::size_t
>(chosen.size()));
167 for (
const auto o : chosen) {
168 const auto c = clusters[o];
169 auto& n = total_sampled[c];
172 const auto ocopy = work->get_observation();
173 for (
decltype(I(ndim)) d = 0; d < ndim; ++d) {
174 auto& curcenter = centers[sanisizer::nd_offset<std::size_t>(d, ndim, c)];
175 curcenter += (
static_cast<Float_
>(ocopy[d]) - curcenter) / n;
181 for (
const auto o : chosen) {
182 const auto p = previous[o];
184 const auto c = clusters[o];
193 bool too_many_changes =
false;
194 for (Cluster_ c = 0; c < ncenters; ++c) {
195 if (
static_cast<double>(last_changed[c]) >=
static_cast<double>(last_sampled[c]) * my_options.
max_change_proportion) {
196 too_many_changes =
true;
201 if (!too_many_changes) {
204 std::fill(last_sampled.begin(), last_sampled.end(), 0);
205 std::fill(last_changed.begin(), last_changed.end(), 0);
211 index.reset(ndim, ncenters, centers);
212 parallelize(my_options.
num_threads, nobs, [&](
const int,
const Index_ start,
const Index_ length) ->
void {
213 auto work = data.new_extractor(start, length);
214 for (Index_ s = start, end = start + length; s < end; ++s) {
215 const auto ptr = work->get_observation();
216 clusters[s] = index.find(ptr);
220 auto cluster_sizes = sanisizer::create<std::vector<Index_> >(ncenters);
221 for (Index_ o = 0; o < nobs; ++o) {
222 ++cluster_sizes[clusters[o]];
224 internal::compute_centroids(data, ncenters, centers, clusters, cluster_sizes);
232 return Details<Index_>(std::move(cluster_sizes), iter, status);