#pragma once #include #include #include #include #include #include #include #include #include "autoopt/util.hpp" namespace autoopt { struct test_data { Eigen::VectorXd _xs; Eigen::VectorXd _ys; double _center; size_t size() const { return _xs.size(); } std::pair operator[](size_t i) const { return {_xs(i) - _center, _ys(i)}; }; }; struct curve_base { Eigen::VectorXd initial; Eigen::VectorXd delta; std::vector param_names; bool flip = false; std::pair fit(this auto&& self, const test_data& measurements) { auto opt_func = [&](const Eigen::VectorX& params) { auto grapher = self.template grapher_from_params(params); T error{0.0}; for (size_t i = 0; i < measurements.size(); i++) { auto [x, y] = measurements[i]; T distance = T{self.flip ? -1.0 : 1.0} * std::atan(grapher.slope_at(T{self.flip ? -x : x})) - T{arcsec2rad(y)}; error = error + distance * distance; } return error / T(measurements.size()); }; auto_diff_optimization_problem problem(opt_func, self.initial); log_barrier_optimization_problem lb_problem(problem, self.delta, 1e-4); while (lb_problem._barrier_strength > 1e-20) { btls(lb_problem); lb_problem._barrier_strength *= 1e-2; } Eigen::VectorX fitted_params = problem.x(); return {fitted_params, std::sqrt(problem.objective(fitted_params))}; } Eigen::VectorXd plot(this auto&& self, const test_data& measurements, std::optional params = {}) { auto grapher = self.template grapher_from_params( params.value_or(self.initial)); Eigen::VectorXd ys(measurements.size()); for (size_t i = 0; i < measurements.size(); i++) { double x = measurements[i].first; ys(i) = rad2arcsec((self.flip ? -1.0 : 1.0) * atan(grapher.slope_at(self.flip ? -x : x))); } return ys; } }; } // namespace autoopt