/* * Implementation of the FFT tester described in * * Funda Ergün. Testing multivariate linear functions: Overcoming the * generator bottleneck. In Proceedings of the Twenty-Seventh Annual * ACM Symposium on the Theory of Computing, pages 407-416, Las Vegas, * Nevada, 29 May--1 June 1995. */ void test_ergun(int n, fftw_direction dir, fftw_plan plan) { fftw_complex *inA, *inB, *inC, *outA, *outB, *outC; fftw_complex *tmp; fftw_complex impulse; int i; int rounds = 20; FFTW_TRIG_REAL twopin = FFTW_K2PI / (FFTW_TRIG_REAL) n; inA = (fftw_complex *) fftw_malloc(n * sizeof(fftw_complex)); inB = (fftw_complex *) fftw_malloc(n * sizeof(fftw_complex)); inC = (fftw_complex *) fftw_malloc(n * sizeof(fftw_complex)); outA = (fftw_complex *) fftw_malloc(n * sizeof(fftw_complex)); outB = (fftw_complex *) fftw_malloc(n * sizeof(fftw_complex)); outC = (fftw_complex *) fftw_malloc(n * sizeof(fftw_complex)); tmp = (fftw_complex *) fftw_malloc(n * sizeof(fftw_complex)); WHEN_VERBOSE(2, printf("Validating plan, n = %d, dir = %s\n", n, dir == FFTW_FORWARD ? "FORWARD" : "BACKWARD")); /* test 1: check linearity */ for (i = 0; i < rounds; ++i) { fftw_complex alpha, beta; c_re(alpha) = DRAND(); c_im(alpha) = DRAND(); c_re(beta) = DRAND(); c_im(beta) = DRAND(); fill_random(inA, n); fill_random(inB, n); fftw_out_of_place(plan, n, inA, outA); fftw_out_of_place(plan, n, inB, outB); array_scale(outA, alpha, n); array_scale(outB, beta, n); array_add(tmp, outA, outB, n); array_scale(inA, alpha, n); array_scale(inB, beta, n); array_add(inC, inA, inB, n); fftw_out_of_place(plan, n, inC, outC); array_compare(outC, tmp, n); } /* test 2: check that the unit impulse is transformed properly */ c_re(impulse) = 1.0; c_im(impulse) = 0.0; for (i = 0; i < n; ++i) { /* impulse */ c_re(inA[i]) = 0.0; c_im(inA[i]) = 0.0; /* transform of the impulse */ outA[i] = impulse; } inA[0] = impulse; for (i = 0; i < rounds; ++i) { fill_random(inB, n); array_sub(inC, inA, inB, n); fftw_out_of_place(plan, n, inB, outB); fftw_out_of_place(plan, n, inC, outC); array_add(tmp, outB, outC, n); array_compare(tmp, outA, n); } /* test 3: check the time-shift property */ /* the paper performs more tests, but this code should be fine too */ for (i = 0; i < rounds; ++i) { int j; fill_random(inA, n); array_rol(inB, inA, n, 1, 1); fftw_out_of_place(plan, n, inA, outA); fftw_out_of_place(plan, n, inB, outB); for (j = 0; j < n; ++j) { FFTW_TRIG_REAL s = dir * FFTW_TRIG_SIN(j * twopin); FFTW_TRIG_REAL c = FFTW_TRIG_COS(j * twopin); c_re(tmp[j]) = c_re(outB[j]) * c - c_im(outB[j]) * s; c_im(tmp[j]) = c_re(outB[j]) * s + c_im(outB[j]) * c; } array_compare(tmp, outA, n); } WHEN_VERBOSE(2, printf("Validation done\n")); fftw_free(tmp); fftw_free(outC); fftw_free(outB); fftw_free(outA); fftw_free(inC); fftw_free(inB); fftw_free(inA); }
/* Same as test_ergun, but for multi-dimensional transforms: */ void testnd_ergun(int rank, int *n, fftw_direction dir, fftwnd_plan plan) { fftw_complex *inA, *inB, *inC, *outA, *outB, *outC; fftw_complex *tmp; fftw_complex impulse; int N, n_before, n_after, dim; int i, which_impulse; int rounds = 20; FFTW_TRIG_REAL twopin; N = 1; for (dim = 0; dim < rank; ++dim) N *= n[dim]; inA = (fftw_complex *) fftw_malloc(N * sizeof(fftw_complex)); inB = (fftw_complex *) fftw_malloc(N * sizeof(fftw_complex)); inC = (fftw_complex *) fftw_malloc(N * sizeof(fftw_complex)); outA = (fftw_complex *) fftw_malloc(N * sizeof(fftw_complex)); outB = (fftw_complex *) fftw_malloc(N * sizeof(fftw_complex)); outC = (fftw_complex *) fftw_malloc(N * sizeof(fftw_complex)); tmp = (fftw_complex *) fftw_malloc(N * sizeof(fftw_complex)); WHEN_VERBOSE(2, printf("Validating plan, N = %d, dir = %s\n", N, dir == FFTW_FORWARD ? "FORWARD" : "BACKWARD")); /* test 1: check linearity */ for (i = 0; i < rounds; ++i) { fftw_complex alpha, beta; c_re(alpha) = DRAND(); c_im(alpha) = DRAND(); c_re(beta) = DRAND(); c_im(beta) = DRAND(); fill_random(inA, N); fill_random(inB, N); fftwnd(plan, 1, inA, 1, N, outA, 1, N); fftwnd(plan, 1, inB, 1, N, outB, 1, N); array_scale(outA, alpha, N); array_scale(outB, beta, N); array_add(tmp, outA, outB, N); array_scale(inA, alpha, N); array_scale(inB, beta, N); array_add(inC, inA, inB, N); fftwnd(plan, 1, inC, 1, N, outC, 1, N); array_compare(outC, tmp, N); } /* * test 2: check that the unit impulse is transformed properly -- we * need to test both the real and imaginary impulses */ for (which_impulse = 0; which_impulse < 2; ++which_impulse) { if (which_impulse == 0) { /* real impulse */ c_re(impulse) = 1.0; c_im(impulse) = 0.0; } else { /* imaginary impulse */ c_re(impulse) = 0.0; c_im(impulse) = 1.0; } for (i = 0; i < N; ++i) { /* impulse */ c_re(inA[i]) = 0.0; c_im(inA[i]) = 0.0; /* transform of the impulse */ outA[i] = impulse; } inA[0] = impulse; for (i = 0; i < rounds; ++i) { fill_random(inB, N); array_sub(inC, inA, inB, N); fftwnd(plan, 1, inB, 1, N, outB, 1, N); fftwnd(plan, 1, inC, 1, N, outC, 1, N); array_add(tmp, outB, outC, N); array_compare(tmp, outA, N); } } /* test 3: check the time-shift property */ /* the paper performs more tests, but this code should be fine too */ /* -- we have to check shifts in each dimension */ n_before = 1; n_after = N; for (dim = 0; dim < rank; ++dim) { int n_cur = n[dim]; n_after /= n_cur; twopin = FFTW_K2PI / (FFTW_TRIG_REAL) n_cur; for (i = 0; i < rounds; ++i) { int j, jb, ja; fill_random(inA, N); array_rol(inB, inA, n_cur, n_before, n_after); fftwnd(plan, 1, inA, 1, N, outA, 1, N); fftwnd(plan, 1, inB, 1, N, outB, 1, N); for (jb = 0; jb < n_before; ++jb) for (j = 0; j < n_cur; ++j) { FFTW_TRIG_REAL s = dir * FFTW_TRIG_SIN(j * twopin); FFTW_TRIG_REAL c = FFTW_TRIG_COS(j * twopin); for (ja = 0; ja < n_after; ++ja) { c_re(tmp[(jb * n_cur + j) * n_after + ja]) = c_re(outB[(jb * n_cur + j) * n_after + ja]) * c - c_im(outB[(jb * n_cur + j) * n_after + ja]) * s; c_im(tmp[(jb * n_cur + j) * n_after + ja]) = c_re(outB[(jb * n_cur + j) * n_after + ja]) * s + c_im(outB[(jb * n_cur + j) * n_after + ja]) * c; } } array_compare(tmp, outA, N); } n_before *= n_cur; } WHEN_VERBOSE(2, printf("Validation done\n")); fftw_free(tmp); fftw_free(outC); fftw_free(outB); fftw_free(outA); fftw_free(inC); fftw_free(inB); fftw_free(inA); }
int cec(struct cec_context * context) { struct cec_matrix * X = context->points; struct cec_matrix * C = context->centers; const int m = X->m; const int k = C->m; const int n = X->n; const int max = context->max_iterations; const int min_card = context->min_card; cross_entropy_function * cross_entropy_functions = context->cross_entropy_functions; struct cross_entropy_context ** cross_entropy_contexts = context->cross_entropy_contexts; int _k = k; int removed_clusters = 0; double energy_sum = 0; int * cluster = context->clustering_vector; int * clusters_number = context->clusters_number; int card[k], removed[k], cluster_map[k]; double clusters_energy[k]; double * energy = context->energy; struct cec_matrix ** covariance_matrices = context->covriances; struct cec_matrix * t_mean_matrix = context->temp_data->t_mean_matrix; struct cec_matrix * t_matrix_nn = context->temp_data->t_matrix_nn; struct cec_matrix * n_covariance_matrix = context->temp_data->n_covariance_matrix; struct cec_matrix ** t_covariance_matrices = context->temp_data->t_covariance_matrices; context->iterations = 0; /* * Assign points to its closest clusters and calculate clusters means. */ for (int i = 0; i < m; i++) { double dist = BIG_DOUBLE; for (int j = 0; j < k; j++) { double dist_temp = dist2(cec_matrix_row(X, i), cec_matrix_row(C, j), n); if (dist > dist_temp) { dist = dist_temp; cluster[i] = j; } } } for (int i = 0; i < k; i++) { card[i] = 0; removed[i] = 0; cluster_map[i] = i; } cec_matrix_set(C, 0.0); for (int i = 0; i < m; i++) { int l = cluster[i]; card[l]++; array_add(cec_matrix_row(C, l), cec_matrix_row(X, i), n); } for (int i = 0; i < k; i++) { if (card[i] < min_card) { removed[i] = 1; array_fill(cec_matrix_row(C, i), NAN, n); removed_clusters++; continue; } array_mul(cec_matrix_row(C, i), 1.0 / card[i], n); } /* * Compute initial covariances using maximum likelihood estimator. */ for (int i = 0; i < k; i++) { cec_matrix_set(covariance_matrices[i], 0.0); cec_matrix_set(t_covariance_matrices[i], 0.0); } for (int i = 0; i < m; i++) { int l = cluster[i]; double t_vec[n]; array_copy(cec_matrix_row(X, i), t_vec, n); array_sub(t_vec, cec_matrix_row(C, l), n); cec_vector_outer_product(t_vec, t_matrix_nn, n); cec_matrix_add(covariance_matrices[l], t_matrix_nn); } for (int i = 0; i < k; i++) { if (removed[i]) continue; cec_matrix_mul(covariance_matrices[i], 1.0 / card[i]); double hx = cross_entropy_functions[i](cross_entropy_contexts[i], covariance_matrices[i]); if (isnan(hx)) return *(cross_entropy_contexts[i]->last_error); clusters_energy[i] = cluster_energy(m, hx, card[i]); energy_sum += clusters_energy[i]; } /* * Change cluster mapping for removed clusters. */ for (int i = k; i > 0; i--) { if (removed[i - 1]) { cluster_map[i - 1] = cluster_map[_k - 1]; _k--; } } if (_k == 0) { return ALL_CLUSTERS_REMOVED_ERROR; } clusters_number[0] = _k; energy[0] = energy_sum; /* * Special case when a cluster was removed before the first iteration. */ int handle_removed_flag = (k == _k) ? 0 : 1; /* * Main loop. */ for (int iter = (handle_removed_flag ? -1 : 0); iter < max; iter++) { int transfer_flag = 0; int removed_last_iteration_flag = 0; for (int i = 0; i < m; i++) { int l = cluster[i]; if (handle_removed_flag && !removed[l]) continue; /* * Energy and mean vector of cluster 'l' after removing point 'i'. * Initializing to NAN to get rid of compiler warning. */ double n_l_energy = NAN; double n_l_mean[n]; double energy_gain; if (!removed[l]) { /* * Compute mean of group 'l' after removing data point 'i' and store * its value in the n_mean. */ mean_remove_point(cec_matrix_row(C, l), n_l_mean, cec_matrix_row(X, i), card[l], n); /* * Compute covariance of group 'l' after removing data point 'i' and store * its value in n_covariance_matrix. */ cec_cov_remove_point(covariance_matrices[l], n_covariance_matrix, cec_matrix_row(C, l), cec_matrix_row(X, i), card[l], t_matrix_nn); /* * Compute energy of group 'l' after removing data point 'i'. */ double n_l_hx = cross_entropy_functions[l](cross_entropy_contexts[l], n_covariance_matrix); if (isnan(n_l_hx)) return *(cross_entropy_contexts[l]->last_error); n_l_energy = cluster_energy(m, n_l_hx, card[l] - 1); energy_gain = 0; } else { energy_gain = INFINITY; } int idx = -1; double best_energy; for (int _j = 0; _j < _k; _j++) { int j = cluster_map[_j]; if ((j == l) || (removed[j] == 1)) continue; mean_add_point(cec_matrix_row(C, j), cec_matrix_row(t_mean_matrix, j), cec_matrix_row(X, i), card[j], n); cec_cov_add_point(covariance_matrices[j], t_covariance_matrices[j], cec_matrix_row(C, j), cec_matrix_row(X, i), card[j], t_matrix_nn); double t_hx = cross_entropy_functions[j](cross_entropy_contexts[j], t_covariance_matrices[j]); if (isnan(t_hx)) return *(cross_entropy_contexts[j]->last_error); double t_energy = cluster_energy(m, t_hx, card[j] + 1); if (removed[l] == 1) { /* * Since the energy of cluster 'l' was subtracted from the energy sum (when 'l' was removed), * gain is only the change of the energy of cluster 'j' by adding point 'i'. */ double gain = (t_energy - clusters_energy[j]); if (gain < energy_gain) { idx = j; energy_gain = gain; best_energy = t_energy; } } else { double gain = (n_l_energy + t_energy) - (clusters_energy[l] + clusters_energy[j]); if (gain < energy_gain) { idx = j; energy_gain = gain; best_energy = t_energy; } } } /* * Transfer point 'i' to cluster 'idx'. */ if (idx != -1) { cluster[i] = idx; card[idx]++; clusters_energy[idx] = best_energy; cec_matrix_copy_data(t_covariance_matrices[idx], covariance_matrices[idx]); array_copy(cec_matrix_row(t_mean_matrix, idx), cec_matrix_row(C, idx), n); if (!removed[l]) { cec_matrix_copy_data(n_covariance_matrix, covariance_matrices[l]); clusters_energy[l] = n_l_energy; card[l]--; array_copy(n_l_mean, cec_matrix_row(C, l), n); if (card[l] < min_card) { removed_last_iteration_flag = 1; /* * If the cluster is being removed, subtract its energy from the energy sum. */ energy_sum -= clusters_energy[l]; array_fill(cec_matrix_row(C, l), NAN, n); cec_matrix_set(covariance_matrices[l], NAN); removed[l] = 1; } } energy_sum += energy_gain; transfer_flag = 1; } /* * Change the mapping and _k for removed clusters. */ for (int _j = _k; _j > 0; _j--) { int j = cluster_map[_j - 1]; if (removed[j]) { cluster_map[_j - 1] = cluster_map[_k - 1]; _k--; } } } energy[iter + 1] = energy_sum; clusters_number[iter + 1] = _k; context->iterations = iter + 1; if (!transfer_flag) return NO_ERROR; /* * If cluster was removed in this iteration, we need to perform another iteration * considering points that are not assigned. It will prevent energy dips. */ if (removed_last_iteration_flag) { handle_removed_flag = 1; iter--; } else handle_removed_flag = 0; } return NO_ERROR; }