// ch13_polynomial_models.cpp // Companion code for Chapter 13: Optimization and Data Fitting. // Standalone C++11; standard library only; console output without plotting. // Compile: c++ -std=c++11 -O2 -Wall -Wextra -pedantic ch13_polynomial_models.cpp -o ch13_polynomial_models // Edit the example settings in main(); routines above main() can be reused. // Fixed seeds reproduce runs with the same standard-library implementation. // Random distributions need not give identical sequences across compilers/languages. #include #include #include #include #include #include #include #include #include #include #include #include #include using Vec = std::vector; using Mat = std::vector; using Function = std::function; using ScalarFunction = std::function; const double pi = std::acos(-1.0); void require(bool condition, const std::string& message) { if (!condition) throw std::invalid_argument(message); } void finite_vector(const Vec& x) { require(!x.empty(), "A nonempty vector is required."); for (double v : x) require(std::isfinite(v), "Data must be finite."); } void check_bounds(const Vec& lo, const Vec& hi) { finite_vector(lo); finite_vector(hi); require(lo.size() == hi.size(), "Bounds must have matching dimensions."); for (std::size_t j=0; jlo[j] && std::isfinite(hi[j]-lo[j]), "Require finite upper > lower."); } double objective_value(double value) { require(std::isfinite(value), "Objective must return a finite real scalar."); return value; } double clip(double x, double lo, double hi) { return std::max(lo,std::min(hi,x)); } Vec linspace(double a, double b, int n) { require(n>=2, "Grid needs at least two points."); Vec x(n); for (int i=0; i uniform{0.0,1.0}; std::normal_distribution normal{0.0,1.0}; explicit Random(unsigned int seed) : engine(seed) {} double u() { return uniform(engine); } double z() { return normal(engine); } int index(int n) { return std::uniform_int_distribution(0,n-1)(engine); } }; struct Result { Vec point; double value=0.0; long long evaluations=0; int iterations=0; std::string status; }; void print_result(const Result& r) { std::cout << std::setprecision(12); for (std::size_t j=0; j(A.size()), n=static_cast(A[0].size()); require(m>=n && b.size()==A.size(), "Invalid least-squares dimensions."); for (int k=0;k1e-12,"Rank-deficient or poorly scaled polynomial design."); double alpha=-std::copysign(norm,A[k][k]); Vec v(m-k); for (int i=k;i=0;--i) { double sum=b[i]; for (int j=i+1;j=2 && degree>=0 && static_cast(degree)(degree)(end-unique.begin()),"Too few distinct x values."); Polynomial model; model.center=std::accumulate(x.begin(),x.end(),0.0)/x.size(); double sum=0; for (double v:x) sum+=(v-model.center)*(v-model.center); model.scale=std::sqrt(sum/(x.size()-1)); require(std::isfinite(model.scale) && model.scale>0,"No usable x spread."); Mat A(x.size(),Vec(degree+1,1.0)); for (std::size_t i=0;i degrees={1,5,14}; // Substitute separate training/validation data. Do not fit validation data. // Reserve fresh test data for assessing a model after selection. std::cout << std::setprecision(10) << "Degree Training RMSE Validation RMSE\n"; for (int degree:degrees) { Polynomial model=polynomial_fit(x,y,degree); double train=0,valid=0; for (std::size_t i=0;i