Polyfit
fit a polynomial
|
00001 /* 00002 * This work is licensed under a Creative Commons 00003 * Attribution-Noncommercial-Share Alike 3.0 United States License. 00004 * 00005 * http://creativecommons.org/licenses/by-nc-sa/3.0/us/ 00006 * 00007 * You are free: 00008 * 00009 * to Share - to copy, distribute, display, and perform the work 00010 * to Remix - to make derivative works 00011 * 00012 * Under the following conditions: 00013 * 00014 * Attribution. You must attribute the work in the manner specified by the 00015 * author or licensor (but not in any way that suggests that they endorse you 00016 * or your use of the work). 00017 * 00018 * Noncommercial. You may not use this work for commercial purposes. 00019 * 00020 * Share Alike. If you alter, transform, or build upon this work, you may 00021 * distribute the resulting work only under the same or similar license to 00022 * this one. 00023 * 00024 * For any reuse or distribution, you must make clear to others the license 00025 * terms of this work. The best way to do this is by including this header. 00026 * 00027 * Any of the above conditions can be waived if you get permission from the 00028 * copyright holder. 00029 * 00030 * Apart from the remix rights granted under this license, nothing in this 00031 * license impairs or restricts the author's moral rights. 00032 */ 00033 00034 00096 #include <config.h> 00097 00098 #include <stdlib.h> 00099 #include <stdio.h> 00100 #include <assert.h> 00101 #include <inttypes.h> 00102 #include <math.h> 00103 00104 #include <jwsc/base/error.h> 00105 #include <jwsc/base/option.h> 00106 #include <jwsc/vector/vector.h> 00107 #include <jwsc/matrix/matrix.h> 00108 #include <jwsc/matrix/matrix_io.h> 00109 #include <jwsc/matrix/matrix_math.h> 00110 #include <jwsc/stat/regress.h> 00111 00112 00113 #define NUM_OPTS_NO_ARG 2 00114 #define NUM_OPTS_WITH_ARG 4 00115 00116 #define DEFAULT_SEED 289375 00117 #define DEFAULT_SIGMA 1.0f 00118 #define DEFAULT_DEGREE 1 00119 00120 #define DEFAULT_RANSAC_ENABLED 0 00121 #define DEFAULT_RANSAC_N 0 00122 #define DEFAULT_RANSAC_K 0 00123 #define DEFAULT_RANSAC_T 0 00124 #define DEFAULT_RANSAC_D 0 00125 00126 00128 static Option_no_arg opts_no_arg[NUM_OPTS_NO_ARG]; 00129 00131 static Option_with_arg opts_with_arg[NUM_OPTS_WITH_ARG]; 00132 00134 static uint32_t seed = DEFAULT_SEED; 00135 00137 static float sigma = DEFAULT_SIGMA; 00138 00140 static uint32_t degree = DEFAULT_DEGREE; 00141 00143 static struct 00144 { 00145 uint8_t enabled; 00146 uint32_t n; 00147 uint32_t k; 00148 float t; 00149 uint32_t d; 00150 } 00151 ransac = { DEFAULT_RANSAC_ENABLED, DEFAULT_RANSAC_N, DEFAULT_RANSAC_K, DEFAULT_RANSAC_T, DEFAULT_RANSAC_D }; 00152 00153 00155 static void print_usage(void) 00156 { 00157 fprintf(stderr, "usage: polyfit OPTIONS\n"); 00158 fprintf(stderr, "where OPTIONS is one or more of the following:\n"); 00159 print_options(stderr, 25, NUM_OPTS_NO_ARG, opts_no_arg, NUM_OPTS_WITH_ARG, 00160 opts_with_arg); 00161 } 00162 00164 static Error* process_help_opt(void) 00165 { 00166 print_usage(); 00167 exit(EXIT_SUCCESS); 00168 } 00169 00171 static Error* process_version_opt(void) 00172 { 00173 fprintf(stderr, "%s\n", POLYFIT_PACKAGE_STRING); 00174 exit(EXIT_SUCCESS); 00175 } 00176 00178 static Error* process_seed_opt(Option_arg arg) 00179 { 00180 if (arg == NULL) 00181 { 00182 return JWSC_EARG("Option 'seed' requires an argument"); 00183 } 00184 if (sscanf(arg, "%u", &seed) != 1) 00185 { 00186 return JWSC_EARG("Option 'seed' requires an argument"); 00187 } 00188 return NULL; 00189 } 00190 00192 static Error* process_sigma_opt(Option_arg arg) 00193 { 00194 if (arg == NULL) 00195 { 00196 return JWSC_EARG("Option 'sigma' requires an argument"); 00197 } 00198 if (sscanf(arg, "%f", &sigma) != 1 || sigma <= 0) 00199 { 00200 return JWSC_EARG("Option 'sigma' must be > 0"); 00201 } 00202 return NULL; 00203 } 00204 00206 static Error* process_degree_opt(Option_arg arg) 00207 { 00208 if (arg == NULL) 00209 { 00210 return JWSC_EARG("Option 'degree' requires an argument"); 00211 } 00212 if (sscanf(arg, "%u", °ree) != 1) 00213 { 00214 return JWSC_EARG("Option 'degree' requires an argument"); 00215 } 00216 return NULL; 00217 } 00218 00220 static Error* process_ransac_opt(Option_arg arg) 00221 { 00222 if (arg == NULL) 00223 { 00224 return JWSC_EARG("Option 'ransac' requires an argument"); 00225 } 00226 if (sscanf(arg, "%u,%u,%f,%u", &(ransac.n), &(ransac.k), &(ransac.t), 00227 &(ransac.d)) != 4) 00228 { 00229 return JWSC_EARG("Option 'ransac' has format n,k,t,d"); 00230 } 00231 ransac.enabled = 1; 00232 return NULL; 00233 } 00234 00235 00236 00237 00239 int main(int argc, const char** argv) 00240 { 00241 int argi; 00242 uint32_t i; 00243 uint32_t row; 00244 00245 float sse; 00246 00247 Option_string l_name; 00248 Option_flag s_name; 00249 Option_desc desc; 00250 Option_func_no_arg fnoarg; 00251 Option_func_with_arg farg; 00252 00253 Matrix_f* M = NULL; 00254 Matrix_f* x = NULL; 00255 Vector_f* y = NULL; 00256 Vector_f* w = NULL; 00257 Vector_f* r = NULL; 00258 Error* err; 00259 00260 i = 0; 00261 l_name = "help"; 00262 s_name = 'h'; 00263 desc = "Prints program usage."; 00264 fnoarg = process_help_opt; 00265 init_option_no_arg(&(opts_no_arg[i++]), l_name, s_name, desc, fnoarg); 00266 00267 l_name = "version"; 00268 s_name = 'v'; 00269 desc = "Prints program version."; 00270 fnoarg = process_version_opt; 00271 init_option_no_arg(&(opts_no_arg[i++]), l_name, s_name, desc, fnoarg); 00272 00273 i = 0; 00274 l_name = "seed"; 00275 s_name = 0; 00276 desc = "Random seed."; 00277 farg = process_seed_opt; 00278 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00279 00280 l_name = "sigma"; 00281 s_name = 's'; 00282 desc = "Gaussian sigma."; 00283 farg = process_sigma_opt; 00284 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00285 00286 l_name = "degree"; 00287 s_name = 'd'; 00288 desc = "Polynomial degree."; 00289 farg = process_degree_opt; 00290 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00291 00292 l_name = "ransac"; 00293 s_name = 0; 00294 desc = "RANSAC robust regression options. Has format n,k,t,d to represent: (n) Number of points to start with, (k) Iterations, (t) Goodness-of-fit threshold for adding a point, and (d) Min number of points needed for a good fit."; 00295 farg = process_ransac_opt; 00296 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00297 00298 if ((err = process_options(argc, argv, &argi, NUM_OPTS_NO_ARG, opts_no_arg, 00299 NUM_OPTS_WITH_ARG, opts_with_arg)) != NULL) 00300 { 00301 print_error_msg_exit("polyfit", err->msg); 00302 } 00303 00304 srand(seed); rand(); rand(); 00305 00306 00307 if ((err = read_matrix_fp_f(&M, stdin)) != NULL) 00308 { 00309 print_error_msg_exit("polyfit", err->msg); 00310 } 00311 00312 assert(M->num_cols == 2); 00313 00314 create_vector_f(&y, M->num_rows); 00315 for (row = 0; row < M->num_rows; row++) 00316 { 00317 y->elts[ row ] = M->elts[ row ][ 1 ]; 00318 } 00319 00320 create_matrix_f(&x, M->num_rows, degree+1); 00321 for (row = 0; row < M->num_rows; row++) 00322 { 00323 for (i = 0; i <= degree; i++) 00324 { 00325 x->elts[ row ][ i ] = pow(M->elts[ row ][ 0 ], i); 00326 } 00327 } 00328 00329 if (ransac.enabled) 00330 { 00331 if ((err = linear_least_squares_ransac_regress_f(&w, x, y, 00332 ransac.n, ransac.k, ransac.t, ransac.d))) 00333 { 00334 print_error_msg_exit("polyfit", err->msg); 00335 } 00336 } 00337 else 00338 { 00339 if ((err = linear_least_squares_regress_f(&w, x, y))) 00340 { 00341 print_error_msg_exit("polyfit", err->msg); 00342 } 00343 } 00344 00345 //write_formatted_vector_fp_f(w, "%.3e", stdout); 00346 fprintf(stdout, "y%d(x) = %.6e", degree, w->elts[ 0 ]); 00347 for (i = 1; i <= degree; i++) 00348 { 00349 fprintf(stdout, " + %.6e*x**%d", w->elts[ i ], i); 00350 } 00351 fprintf(stdout, "\n"); 00352 00353 // Compute the SSE of the residuals 00354 multiply_matrix_and_vector_f(&r, x, w); 00355 sse = 0; 00356 for (i = 0; i < y->num_elts; i++) 00357 { 00358 sse += (y->elts[ i ] - r->elts[ i ])*(y->elts[ i ] - r->elts[ i ]); 00359 } 00360 00361 fprintf(stdout, "SSE: %f\n", sse); 00362 00363 00364 free_matrix_f(M); 00365 free_matrix_f(x); 00366 free_vector_f(y); 00367 free_vector_f(w); 00368 free_vector_f(r); 00369 00370 return EXIT_SUCCESS; 00371 }