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 00046 #include <config.h> 00047 00048 #include <stdlib.h> 00049 #include <stdio.h> 00050 #include <assert.h> 00051 #include <inttypes.h> 00052 #include <math.h> 00053 00054 #include <jwsc/base/error.h> 00055 #include <jwsc/base/option.h> 00056 #include <jwsc/vector/vector.h> 00057 #include <jwsc/vector/vector_io.h> 00058 #include <jwsc/matrix/matrix.h> 00059 #include <jwsc/matrix/matrix_io.h> 00060 #include <jwsc/matrix/matrix_math.h> 00061 #include <jwsc/prob/pdf.h> 00062 #include <jwsc/prob/mv_pdf.h> 00063 #include <jwsc/stat/regress.h> 00064 00065 00066 #define NUM_OPTS_NO_ARG 2 00067 #define NUM_OPTS_WITH_ARG 9 00068 00069 #define DEFAULT_SEED 289375 00070 #define DEFAULT_SIGMA 1.0f 00071 #define DEFAULT_DEGREE 1 00072 #define DEFAULT_NUM_PTS 100 00073 #define DEFAULT_W_MU 0.0 00074 #define DEFAULT_W_SIGMA 0.1 00075 #define DEFAULT_W_MIN -2 00076 #define DEFAULT_W_MAX 2 00077 #define DEFAULT_X_MIN -10 00078 #define DEFAULT_X_MAX 10 00079 #define DEFAULT_OUTLIER 0 00080 #define DEFAULT_OUTLIER_SIGMA 6.0f 00081 00082 00084 static Option_no_arg opts_no_arg[NUM_OPTS_NO_ARG]; 00085 00087 static Option_with_arg opts_with_arg[NUM_OPTS_WITH_ARG]; 00088 00090 static uint32_t seed = DEFAULT_SEED; 00091 00093 static float sigma = DEFAULT_SIGMA; 00094 00096 static uint32_t degree = DEFAULT_DEGREE; 00097 00099 static uint32_t num_pts = DEFAULT_NUM_PTS; 00100 00102 static float w_mu = DEFAULT_W_MU; 00103 00105 static float w_sigma = DEFAULT_W_SIGMA; 00106 00108 static float w_min = DEFAULT_W_MIN; 00109 00111 static float w_max = DEFAULT_W_MAX; 00112 00114 static float x_min = DEFAULT_X_MIN; 00115 00117 static float x_max = DEFAULT_X_MAX; 00118 00120 static float outlier = DEFAULT_OUTLIER; 00121 00123 static float outlier_sigma = DEFAULT_OUTLIER_SIGMA; 00124 00125 static enum { GAUSS=0, UNIFORM } w_sample_t; 00126 00127 00129 static void print_usage(void) 00130 { 00131 fprintf(stderr, "usage: polygen OPTIONS\n"); 00132 fprintf(stderr, "where OPTIONS is one or more of the following:\n"); 00133 print_options(stderr, 25, NUM_OPTS_NO_ARG, opts_no_arg, NUM_OPTS_WITH_ARG, 00134 opts_with_arg); 00135 } 00136 00138 static Error* process_help_opt(void) 00139 { 00140 print_usage(); 00141 exit(EXIT_SUCCESS); 00142 } 00143 00145 static Error* process_version_opt(void) 00146 { 00147 fprintf(stderr, "%s\n", POLYFIT_PACKAGE_STRING); 00148 exit(EXIT_SUCCESS); 00149 } 00150 00152 static Error* process_seed_opt(Option_arg arg) 00153 { 00154 if (arg == NULL) 00155 { 00156 return JWSC_EARG("Option 'seed' requires an argument"); 00157 } 00158 if (sscanf(arg, "%u", &seed) != 1) 00159 { 00160 return JWSC_EARG("Option 'seed' requires an argument"); 00161 } 00162 return NULL; 00163 } 00164 00166 static Error* process_sigma_opt(Option_arg arg) 00167 { 00168 if (arg == NULL) 00169 { 00170 return JWSC_EARG("Option 'sigma' requires an argument"); 00171 } 00172 if (sscanf(arg, "%f", &sigma) != 1 || sigma <= 1.0e-6) 00173 { 00174 return JWSC_EARG("Option 'sigma' must be > 0"); 00175 } 00176 return NULL; 00177 } 00178 00180 static Error* process_degree_opt(Option_arg arg) 00181 { 00182 if (arg == NULL) 00183 { 00184 return JWSC_EARG("Option 'degree' requires an argument"); 00185 } 00186 if (sscanf(arg, "%u", °ree) != 1) 00187 { 00188 return JWSC_EARG("Option 'degree' requires an argument"); 00189 } 00190 return NULL; 00191 } 00192 00194 static Error* process_num_pts_opt(Option_arg arg) 00195 { 00196 if (arg == NULL) 00197 { 00198 return JWSC_EARG("Option 'num-pts' requires an argument"); 00199 } 00200 if (sscanf(arg, "%u", &num_pts) != 1) 00201 { 00202 return JWSC_EARG("Option 'num-pts' requires an argument"); 00203 } 00204 return NULL; 00205 } 00206 00208 static Error* process_w_gauss_opt(Option_arg arg) 00209 { 00210 if (arg == NULL) 00211 { 00212 return JWSC_EARG("Option 'w-gauss' requires an argument"); 00213 } 00214 if (sscanf(arg, "%f,%f", &w_mu, &w_sigma) != 2) 00215 { 00216 return JWSC_EARG("Option 'w-gauss' has format mu,sigma"); 00217 } 00218 if (w_sigma < 0.01) 00219 { 00220 return JWSC_EARG("Option 'w-uni': min > max"); 00221 } 00222 w_sample_t = GAUSS; 00223 return NULL; 00224 } 00225 00227 static Error* process_w_uni_opt(Option_arg arg) 00228 { 00229 if (arg == NULL) 00230 { 00231 return JWSC_EARG("Option 'w-uni' requires an argument"); 00232 } 00233 if (sscanf(arg, "%f,%f", &w_min, &w_max) != 2) 00234 { 00235 return JWSC_EARG("Option 'w-uni' has format min,max"); 00236 } 00237 if (w_min > w_max) 00238 { 00239 return JWSC_EARG("Option 'w-uni': min > max"); 00240 } 00241 w_sample_t = UNIFORM; 00242 return NULL; 00243 } 00244 00246 static Error* process_x_range_opt(Option_arg arg) 00247 { 00248 if (arg == NULL) 00249 { 00250 return JWSC_EARG("Option 'x-range' requires an argument"); 00251 } 00252 if (sscanf(arg, "%f,%f", &x_min, &x_max) != 2) 00253 { 00254 return JWSC_EARG("Option 'x-range' has format min,max"); 00255 } 00256 if (x_min > x_max) 00257 { 00258 return JWSC_EARG("Option 'x-range': min > max"); 00259 } 00260 return NULL; 00261 } 00262 00264 static Error* process_outlier_opt(Option_arg arg) 00265 { 00266 if (arg == NULL) 00267 { 00268 return JWSC_EARG("Option 'outlier' requires an argument"); 00269 } 00270 if (sscanf(arg, "%f", &outlier) != 1 || outlier < 0 || outlier > 1) 00271 { 00272 return JWSC_EARG("Option 'outlier' must be in [0,1]"); 00273 } 00274 return NULL; 00275 } 00276 00278 static Error* process_outlier_sigma_opt(Option_arg arg) 00279 { 00280 if (arg == NULL) 00281 { 00282 return JWSC_EARG("Option 'outlier-sigma' requires an argument"); 00283 } 00284 if (sscanf(arg, "%f", &outlier_sigma) != 1 || outlier_sigma <= 1.0e-6) 00285 { 00286 return JWSC_EARG("Option 'outlier-sigma' must be > 0"); 00287 } 00288 return NULL; 00289 } 00290 00291 00292 static void sample_w(Vector_d** w_out) 00293 { 00294 uint32_t d; 00295 Vector_d* w_mu_v = 0; 00296 Matrix_d* w_sigma_m = 0; 00297 00298 if (w_sample_t == GAUSS) 00299 { 00300 create_init_vector_d(&w_mu_v, degree+1, w_mu); 00301 create_identity_matrix_d(&w_sigma_m, degree+1); 00302 multiply_matrix_by_scalar_d(&w_sigma_m, w_sigma_m, w_sigma); 00303 assert(sample_mv_gaussian_pdf_d(w_out, w_mu_v, w_sigma_m) == 0); 00304 } 00305 else if (w_sample_t == UNIFORM) 00306 { 00307 create_vector_d(w_out, degree+1); 00308 for (d = 0; d <= degree; d++) 00309 { 00310 (*w_out)->elts[ d ] = sample_uniform_pdf_d(w_min, w_max); 00311 } 00312 } 00313 else 00314 { 00315 abort(); 00316 } 00317 00318 free_vector_d(w_mu_v); 00319 free_matrix_d(w_sigma_m); 00320 } 00321 00322 00324 int main(int argc, const char** argv) 00325 { 00326 int argi; 00327 uint32_t i, d; 00328 00329 Option_string l_name; 00330 Option_flag s_name; 00331 Option_desc desc; 00332 Option_func_no_arg fnoarg; 00333 Option_func_with_arg farg; 00334 00335 Vector_d* w = NULL; 00336 Vector_d* x = NULL; 00337 Vector_d* y = NULL; 00338 Error* err; 00339 00340 i = 0; 00341 l_name = "help"; 00342 s_name = 'h'; 00343 desc = "Prints program usage."; 00344 fnoarg = process_help_opt; 00345 init_option_no_arg(&(opts_no_arg[i++]), l_name, s_name, desc, fnoarg); 00346 00347 l_name = "version"; 00348 s_name = 'v'; 00349 desc = "Prints program version."; 00350 fnoarg = process_version_opt; 00351 init_option_no_arg(&(opts_no_arg[i++]), l_name, s_name, desc, fnoarg); 00352 00353 i = 0; 00354 l_name = "seed"; 00355 s_name = 0; 00356 desc = "Random seed."; 00357 farg = process_seed_opt; 00358 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00359 00360 l_name = "w-gauss"; 00361 s_name = 0; 00362 desc = "Gaussian over w to generate points from. Has format mu,sigma."; 00363 farg = process_w_gauss_opt; 00364 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00365 00366 l_name = "w-uni"; 00367 s_name = 0; 00368 desc = "Uniform range over w to generate points from. Has format min,max."; 00369 farg = process_w_uni_opt; 00370 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00371 00372 l_name = "x-range"; 00373 s_name = 0; 00374 desc = "Range over x to generate points from."; 00375 farg = process_x_range_opt; 00376 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00377 00378 l_name = "sigma"; 00379 s_name = 0; 00380 desc = "Gaussian sigma for polynomial point generation."; 00381 farg = process_sigma_opt; 00382 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00383 00384 l_name = "outlier"; 00385 s_name = 0; 00386 desc = "Percentage of points to generate as outliers."; 00387 farg = process_outlier_opt; 00388 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00389 00390 l_name = "outlier-sigma"; 00391 s_name = 0; 00392 desc = "Gaussian sigma for polynomial outlier point generation."; 00393 farg = process_outlier_sigma_opt; 00394 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00395 00396 l_name = "degree"; 00397 s_name = 'd'; 00398 desc = "Polynomial degree."; 00399 farg = process_degree_opt; 00400 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00401 00402 l_name = "num-pts"; 00403 s_name = 'n'; 00404 desc = "Number of points to generate."; 00405 farg = process_num_pts_opt; 00406 init_option_with_arg(&(opts_with_arg[i++]), l_name, s_name, desc, farg); 00407 00408 if ((err = process_options(argc, argv, &argi, NUM_OPTS_NO_ARG, opts_no_arg, 00409 NUM_OPTS_WITH_ARG, opts_with_arg)) != NULL) 00410 { 00411 print_error_msg_exit("polygen", err->msg); 00412 } 00413 00414 srand(seed); rand(); rand(); 00415 00416 sample_w(&w); 00417 create_vector_d(&x, num_pts); 00418 create_vector_d(&y, num_pts); 00419 00420 for (i = 0; i < num_pts; i++) 00421 { 00422 x->elts[ i ] = sample_uniform_pdf_d(x_min, x_max); 00423 00424 if (outlier > sample_uniform_pdf_d(0, 1)) 00425 { 00426 y->elts[ i ] = sample_gaussian_pdf_d(0, outlier_sigma, 00427 -6*outlier_sigma, 6*outlier_sigma); 00428 fprintf(stderr, "outlier[ %2d ]: %f, %f\n", i, x->elts[ i ], y->elts[ i ]); 00429 } 00430 else 00431 { 00432 y->elts[ i ] = sample_gaussian_pdf_d(0, sigma, -6*sigma, 6*sigma); 00433 } 00434 00435 00436 for (d = 0; d <= degree; d++) 00437 { 00438 y->elts[ i ] += w->elts[ d ] * pow(x->elts[ i ], d); 00439 } 00440 00441 fprintf(stdout, "%13.6e %13.6e\n", x->elts[ i ], y->elts[ i ]); 00442 } 00443 00444 fprintf(stderr, "y(x) = %.6e", w->elts[ 0 ]); 00445 for (d = 1; d <= degree; d++) 00446 { 00447 fprintf(stderr, " + %.6e*x**%d", w->elts[ d ], d); 00448 } 00449 fprintf(stderr, "\n"); 00450 00451 00452 free_vector_d(w); 00453 free_vector_d(x); 00454 free_vector_d(y); 00455 00456 return EXIT_SUCCESS; 00457 }