-
Notifications
You must be signed in to change notification settings - Fork 0
/
serial.c
229 lines (190 loc) · 7.56 KB
/
serial.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
/*
----------------------------------------------------------
| Title: Mean Shift Clustering (Serial) |
| Author: Giannis Meleziadis |
----------------------------------------------------------
| Description: |
| Serial implementation of Mean Shift clustering |
| on 600 2D points. Runs on the CPU, reads input, |
| performs clustering, writes output, and verifies |
| against a reference. |
----------------------------------------------------------
| Compilation: |
| gcc serial.c -o serial -O3 -lm |
----------------------------------------------------------
| Execution: |
| ./serial 0.5 |
----------------------------------------------------------
| Files: |
| input.txt - Input points |
| output.txt - Clustered output |
| output_reference.txt - Reference for validation |
----------------------------------------------------------
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/time.h>
#include <float.h>
float s = 1;
float k = 20; // Factor to scale tolerance, compensating for numerical errors
#define NUM_OF_POINTS 600
#define DIMENSIONS 2
// FUNCTIONS //
// Function to calculate the distance between two points
float distanceFunction(float *point1, float *point2) {
float distance = 0;
for (int i = 0; i < DIMENSIONS; i++) {
distance += (point1[i] - point2[i]) * (point1[i] - point2[i]);
}
return sqrt(distance);
}
// Function to calculate the kernel weight
float kernelFunction(float *pointYk, float *arrayXi) {
float distance = distanceFunction(pointYk, arrayXi);
distance *= distance;
return exp(-((distance) / (2 * (s * s))));
}
// Function to calculate the distance moved
float movedDistance(float *moved) {
float distance = 0;
for (int i = 0; i < DIMENSIONS; i++) {
distance += (moved[i]) * (moved[i]);
}
return sqrt(distance);
}
// Shift function to update the position of a point
void shiftFunction(float *Ykplus1, float *Yk, float *X, float e, int index) {
float numerator[DIMENSIONS] = {0};
float denominator = 0;
float weightFromGaussian = 0;
float Ypoint[DIMENSIONS], Xpoint[DIMENSIONS], moved[DIMENSIONS];
// Initialize moved to force at least one iteration
for (int dim = 0; dim < DIMENSIONS; dim++) {
moved[dim] = FLT_MAX;
}
// Keep shifting the index point until convergence criterion is met
while (movedDistance(moved) >= e) {
// Point specified by index
for (int i = 0; i < DIMENSIONS; i++) {
Ypoint[i] = Yk[index * DIMENSIONS + i];
}
// New shift, everything zero
denominator = 0;
weightFromGaussian = 0;
for (int i = 0; i < DIMENSIONS; i++) {
numerator[i] = 0;
}
// Evaluate each point in X
for (int i = 0; i < NUM_OF_POINTS; i++) {
// Check each point of X if they will be counted for Ykplus1
Xpoint[0] = X[i * DIMENSIONS];
Xpoint[1] = X[i * DIMENSIONS + 1];
float check = distanceFunction(Ypoint, Xpoint);
if (check <= s * s && check > 0) {
// Calculation of the weight for the point that falls within the bandwidth (check < s^2),
// and update the numerator and denominator accordingly.
weightFromGaussian = kernelFunction(Ypoint, Xpoint);
for (int j = 0; j < DIMENSIONS; j++) {
numerator[j] += weightFromGaussian * X[i * DIMENSIONS + j];
}
denominator += weightFromGaussian;
}
}
// Update the position of the current point using numerator, denominator
// and calculate the shift distance for the next iteration
for (int j = 0; j < DIMENSIONS; j++) {
float newY = numerator[j] / denominator;
moved[j] = newY - Yk[index * DIMENSIONS + j];
Yk[index * DIMENSIONS + j] = newY;
Ykplus1[index * DIMENSIONS + j] = newY;
}
}
}
// Function to verify results
int verifyResults(float *results, const char *refFileName, float tolerance) {
FILE *fRef = fopen(refFileName, "r");
if (fRef == NULL) {
fprintf(stderr, "Failed to open %s for validation.\n", refFileName);
return -1; // File opening error
}
float refValue;
int errors = 0;
for (int i = 0; i < NUM_OF_POINTS; i++) {
for (int j = 0; j < DIMENSIONS; j++) {
if (fscanf(fRef, "%f%*c", &refValue) != 1) {
fprintf(stderr, "Error reading validation file at point %d, dimension %d.\n", i, j);
fclose(fRef);
return -2; // Reading error
}
if (fabs(refValue - results[i * DIMENSIONS + j]) > tolerance) {
errors++;
}
}
}
fclose(fRef);
return errors;
}
int main(int argc, char **argv) {
if (argc != 2) {
printf("Parameter needed\nExample: ./serial 0.5\n");
return 1;
}
float e = atof(argv[1]); // Convergence parameter
// Open the input file
FILE *myFile = fopen("input.txt", "r");
if (myFile == NULL) {
fprintf(stderr, "Failed to open input.txt.\n");
return 1;
}
int nBytes = NUM_OF_POINTS * DIMENSIONS * sizeof(float*);
// Allocate memory for data arrays
float *arrayStatic = (float *)malloc(nBytes); // Data
float *arrayYk = (float *)malloc(nBytes); // MeanShift applies here y(k)
float *arrayYkplus1 = (float *)malloc(nBytes); // Returned y(k+1)
// Read points from file
for (int i = 0; i < NUM_OF_POINTS; i++) {
for (int j = 0; j < DIMENSIONS; j++) {
// Does not read char after float (',' or '\n') and fill arrayStatic
if (fscanf(myFile, "%f%*c", &arrayStatic[i * DIMENSIONS + j]) != 1) {
printf("Error reading file at point %d, dimension %d.\n", i, j);
return 1;
}
// Initialize arrayYk == arrayStatic
arrayYk[i * DIMENSIONS + j] = arrayStatic[i * DIMENSIONS + j];
}
}
printf("Finished reading from file\n");
fclose(myFile);
// Start the timer
struct timeval startwtime, endwtime;
gettimeofday(&startwtime, NULL);
// Process each point
for (int i = 0; i < NUM_OF_POINTS; i++) {
shiftFunction(arrayYkplus1, arrayYk, arrayStatic, e, i);
}
// Stop the timer and calculate elapsed time
gettimeofday(&endwtime, NULL);
float seq_time = (float)((endwtime.tv_usec - startwtime.tv_usec) / 1.0e6 + endwtime.tv_sec - startwtime.tv_sec);
printf("Wall clock time (serial) = %f ms\n", 1000 * seq_time);
// Write the results to output.txt
FILE *f = fopen("output.txt", "w");
if (f == NULL) {
printf("Error opening output file!\n");
exit(1);
}
for (int i = 0; i < NUM_OF_POINTS; i++) {
for (int j = 0; j < DIMENSIONS; j++) {
fprintf(f, "%f ", arrayYk[i * DIMENSIONS + j]);
}
fprintf(f, "\n");
}
fclose(f);
// Validate the results by comparing with a reference file
int errors = verifyResults(arrayYk, "output_reference.txt", k * e);
printf("The number of errors is %d\n", errors);
free(arrayStatic);
free(arrayYk);
free(arrayYkplus1);
return 0;
}