00001
00002
00003
00004 #include "common.h"
00005 #include "settings.h"
00006 #include "logic.h"
00007 #include "fit.h"
00008 #include <ctype.h>
00009 #include <algorithm>
00010 #include <stdlib.h>
00011 #include <ctime>
00012
00013 using namespace std;
00014
00015 union OptVal
00016 {
00017 struct { int Settings::*ptr; int ini; } i;
00018 struct { double Settings::*ptr; double ini; } d;
00019 struct { bool Settings::*ptr; bool ini; } b;
00020 struct { string Settings::*ptr; const char* ini; } s;
00021 struct { const char* Settings::*ptr; const char* ini; } e;
00022
00023 OptVal(int Settings::*p, int ini) { i.ptr = p; i.ini = ini; }
00024 OptVal(double Settings::*p, double ini) { d.ptr = p; d.ini = ini; }
00025 OptVal(bool Settings::*p, bool ini) { b.ptr = p; b.ini = ini; }
00026 OptVal(string Settings::*p, const char* ini) { s.ptr = p; s.ini = ini; }
00027 OptVal(const char* Settings::*p, const char* ini) { e.ptr = p; e.ini = ini;}
00028 };
00029
00030 struct Option
00031 {
00032 const char* name;
00033 SettingsMgr::ValueType vtype;
00034 OptVal val;
00035 const char** allowed_values;
00036 };
00037
00038 double epsilon = 1e-12;
00039
00040 static const char* default_sigma_enum[] =
00041 { "sqrt", "one", NULL };
00042
00043 static const char* fitting_method_enum[] =
00044 { "levenberg_marquardt", "nelder_mead_simplex", "genetic_algorithms", NULL };
00045
00046 static const char* nm_distribution_enum[] =
00047 { "bound", "uniform", "gauss", "lorentz", NULL };
00048
00049 #define OPT(name, type, ini, allowed) \
00050 { #name, SettingsMgr::type, OptVal(&Settings::name, ini), allowed }
00051
00052 static const Option options[] = {
00053 OPT(verbosity, kInt, 0, NULL),
00054 OPT(autoplot, kBool, true, NULL),
00055 OPT(exit_on_warning, kBool, false, NULL),
00056 OPT(epsilon, kDouble, 1e-12, NULL),
00057 OPT(default_sigma, kEnum, default_sigma_enum[0], default_sigma_enum),
00058 OPT(pseudo_random_seed, kInt, 0, NULL),
00059 OPT(numeric_format, kString, "%g", NULL),
00060 OPT(logfile, kString, "", NULL),
00061 OPT(log_full, kBool, false, NULL),
00062 OPT(function_cutoff, kDouble, 0., NULL),
00063
00064 OPT(height_correction, kDouble, 1., NULL),
00065 OPT(width_correction, kDouble, 1., NULL),
00066 OPT(guess_uses_weights, kBool, true, NULL),
00067
00068 OPT(fitting_method, kEnum, fitting_method_enum[0], fitting_method_enum),
00069 OPT(max_wssr_evaluations, kInt, 1000, NULL),
00070 OPT(max_fitting_time, kDouble, 0., NULL),
00071 OPT(refresh_period, kInt, 4, NULL),
00072 OPT(fit_replot, kBool, false, NULL),
00073 OPT(domain_percent, kDouble, 30., NULL),
00074
00075 OPT(lm_lambda_start, kDouble, 0.001, NULL),
00076 OPT(lm_lambda_up_factor, kDouble, 10, NULL),
00077 OPT(lm_lambda_down_factor, kDouble, 10, NULL),
00078 OPT(lm_stop_rel_change, kDouble, 1e-4, NULL),
00079 OPT(lm_max_lambda, kDouble, 1e+15, NULL),
00080
00081 OPT(nm_convergence, kDouble, 0.0001, NULL),
00082 OPT(nm_move_all, kBool, false, NULL),
00083 OPT(nm_distribution, kEnum, nm_distribution_enum[0], nm_distribution_enum),
00084 OPT(nm_move_factor, kDouble, 1., NULL),
00085 };
00086
00087 const Option& find_option(const string& name)
00088 {
00089 size_t len = sizeof(options) / sizeof(options[0]);
00090 for (size_t i = 0; i != len; ++i)
00091 if (options[i].name == name)
00092 return options[i];
00093 throw ExecuteError("Unknown option: " + name);
00094 }
00095
00096 SettingsMgr::SettingsMgr(Ftk const* F)
00097 : F_(F)
00098 {
00099 size_t len = sizeof(options) / sizeof(options[0]);
00100 for (size_t i = 0; i != len; ++i) {
00101 const Option& opt = options[i];
00102 if (opt.vtype == kInt)
00103 m_.*opt.val.i.ptr = opt.val.i.ini;
00104 else if (opt.vtype == kDouble)
00105 m_.*opt.val.d.ptr = opt.val.d.ini;
00106 else if (opt.vtype == kBool)
00107 m_.*opt.val.b.ptr = opt.val.b.ini;
00108 else if (opt.vtype == kString)
00109 m_.*opt.val.s.ptr = opt.val.s.ini;
00110 else if (opt.vtype == kEnum)
00111 m_.*opt.val.e.ptr = opt.val.e.ini;
00112 }
00113 set_long_double_format(m_.numeric_format);
00114 }
00115
00116 void SettingsMgr::set_long_double_format(const string& double_fmt)
00117 {
00118 long_double_format_ = double_fmt;
00119 size_t pos = double_fmt.find_last_of("aAeEfFgG");;
00120 if (pos != string::npos && double_fmt[pos] != 'L')
00121 long_double_format_.insert(pos, "L");
00122 }
00123
00124 string SettingsMgr::get_as_string(string const& k) const
00125 {
00126 const Option& opt = find_option(k);
00127 if (opt.vtype == kInt)
00128 return S(m_.*opt.val.i.ptr);
00129 else if (opt.vtype == kDouble)
00130 return S(m_.*opt.val.d.ptr);
00131 else if (opt.vtype == kBool)
00132 return m_.*opt.val.b.ptr ? "1" : "0";
00133 else if (opt.vtype == kString)
00134 return "'" + S(m_.*opt.val.s.ptr) + "'";
00135 else if (opt.vtype == kEnum)
00136 return S(m_.*opt.val.e.ptr);
00137 assert(0);
00138 return "";
00139 }
00140
00141 int SettingsMgr::get_enum_index(string const& k) const
00142 {
00143 const Option& opt = find_option(k);
00144 assert(opt.vtype == kEnum);
00145 const char* val = m_.*opt.val.e.ptr;
00146 const char** av = opt.allowed_values;
00147 int n = 0;
00148 while (*av[n]) {
00149 if (val == av[n])
00150 break;
00151 ++n;
00152 }
00153 assert(*av[n]);
00154 return n;
00155 }
00156
00157 void SettingsMgr::set_as_string(string const& k, string const& v)
00158 {
00159 string sp = get_as_string(k);
00160 if (sp == v) {
00161 F_->msg("Option '" + k + "' already has value: " + v);
00162 return;
00163 }
00164 const Option& opt = find_option(k);
00165 assert(opt.vtype == kString || opt.vtype == kEnum);
00166 if (opt.vtype == kString) {
00167 if (k == "logfile" && !v.empty()) {
00168 FILE* f = fopen(v.c_str(), "a");
00169 if (!f)
00170 throw ExecuteError("Cannot open file for writing: " + v);
00171
00172 fprintf(f, "%s. LOG START: %s", fityk_version_line,
00173 time_now().c_str());
00174 fclose(f);
00175 }
00176 else if (k == "numeric_format") {
00177 if (count(v.begin(), v.end(), '%') != 1)
00178 throw ExecuteError("Exactly one `%' expected, e.g. '%.9g'");
00179 set_long_double_format(v);
00180 }
00181 m_.*opt.val.s.ptr = v;
00182 }
00183 else {
00184 const char **ptr = opt.allowed_values;
00185 while (*ptr) {
00186 if (*ptr == v) {
00187 m_.*opt.val.e.ptr = *ptr;
00188 return;
00189 }
00190 ++ptr;
00191 }
00192 throw ExecuteError("`" + v + "' is not a valid value for `" + k + "'");
00193 }
00194 }
00195
00196 void SettingsMgr::set_as_number(string const& k, double d)
00197 {
00198 string sp = get_as_string(k);
00199 if (sp == S(d)) {
00200 F_->msg("Option '" + k + "' already has value: " + sp);
00201 return;
00202 }
00203 const Option& opt = find_option(k);
00204 assert(opt.vtype == kInt || opt.vtype == kDouble || opt.vtype == kBool);
00205 if (opt.vtype == kInt) {
00206 m_.*opt.val.i.ptr = iround(d);
00207 if (k == "pseudo_random_seed")
00208 do_srand();
00209 }
00210 else if (opt.vtype == kDouble) {
00211 if (k == "epsilon") {
00212 if (d <= 0.)
00213 throw ExecuteError("Value of epsilon must be positive.");
00214 epsilon = d;
00215 }
00216 m_.*opt.val.d.ptr = d;
00217 }
00218 else
00219 m_.*opt.val.b.ptr = (fabs(d) >= 0.5);
00220 }
00221
00222 SettingsMgr::ValueType SettingsMgr::get_value_type(const string& k)
00223 {
00224 try {
00225 return find_option(k).vtype;
00226 }
00227 catch (ExecuteError&) {
00228 return kNotFound;
00229 }
00230 }
00231
00232 string SettingsMgr::get_type_desc(const string& k)
00233 {
00234 const Option& opt = find_option(k);
00235 switch (opt.vtype) {
00236 case kInt: return "integer number";
00237 case kDouble: return "real number";
00238 case kBool: return "boolean (0/1)";
00239 case kString: return "'string'";
00240 case kEnum: {
00241 const char **ptr = opt.allowed_values;
00242 string s = "one of: " + S(*ptr);
00243 while (*++ptr)
00244 s += S(", ") + *ptr;
00245 return s;
00246 }
00247 case kNotFound: assert(0);
00248 }
00249 return "";
00250 }
00251
00252 vector<string> SettingsMgr::get_key_list(const string& start)
00253 {
00254 vector<string> v;
00255 size_t len = sizeof(options) / sizeof(options[0]);
00256 for (size_t i = 0; i != len; ++i)
00257 if (startswith(options[i].name, start))
00258 v.push_back(options[i].name);
00259 sort(v.begin(), v.end());
00260 return v;
00261 }
00262
00263 const char** SettingsMgr::get_allowed_values(const std::string& k)
00264 {
00265 return find_option(k).allowed_values;
00266 }
00267
00268 void SettingsMgr::do_srand()
00269 {
00270 int seed = m_.pseudo_random_seed == 0 ? (int) time(NULL)
00271 : m_.pseudo_random_seed;
00272 srand(seed);
00273 F_->vmsg("Seed for pseudo-random numbers: " + S(seed));
00274 }
00275