4#include "interpolation/rbf_inequality_fitter.h"
5#include "point_cloud/distance_filter.h"
6#include "boost/range/irange.hpp"
7#include "common/zip_sort.h"
8#include "fmm/fmm_tree_height.h"
9#include "interpolation/rbf_incremental_fitter.h"
10#include "interpolation/rbf_solver.h"
17#include <unordered_set>
20 rbf_inequality_fitter::rbf_inequality_fitter(
const model& model,
const geometry::points3d& points)
23 n_points_(points.rows()),
24 n_poly_basis_(model.poly_basis_size()),
25 bbox_(geometry::bbox3d::from_points(points)) {}
27 std::pair<std::vector<index_t>, valuesd> rbf_inequality_fitter::fit(
28 const valuesd& values,
const valuesd& values_lb,
29 const valuesd& values_ub,
double absolute_tolerance,
int max_iter)
const {
30 double filtering_distance = bbox_.size().mean() / 4.0;
32 auto not_nan = [](
double d) {
return !std::isnan(d); };
33 auto eq_idcs = arg_where(values, not_nan);
34 auto n_eq =
static_cast<index_t
>(eq_idcs.size());
36 auto lb_idcs = arg_where(values_lb, not_nan);
37 auto ub_idcs = arg_where(values_ub, not_nan);
38 std::vector<index_t> ineq_idcs;
39 std::set_union(lb_idcs.begin(), lb_idcs.end(), ub_idcs.begin(), ub_idcs.end(),
40 std::back_inserter(ineq_idcs));
41 auto n_ineq =
static_cast<index_t
>(ineq_idcs.size());
42 geometry::points3d ineq_points = points_(ineq_idcs, Eigen::all);
44 std::unique_ptr<rbf_solver> solver;
45 std::unique_ptr<rbf_evaluator<>> res_eval;
46 auto last_tree_height = 0;
48 valuesd weights = valuesd::Zero(n_points_ + n_poly_basis_);
49 auto centers = eq_idcs;
50 valuesd center_weights;
51 std::set<index_t> active_lb_idcs;
52 std::set<index_t> active_ub_idcs;
55 std::cout <<
"Active lower bounds: " << active_lb_idcs.size() <<
" / " << lb_idcs.size()
57 std::cout <<
"Active upper bounds: " << active_ub_idcs.size() <<
" / " << ub_idcs.size()
63 std::set_union(active_lb_idcs.begin(), active_lb_idcs.end(), active_ub_idcs.begin(),
64 active_ub_idcs.end(), std::back_inserter(centers));
69 if (!centers.empty()) {
70 auto n_centers =
static_cast<index_t
>(centers.size());
72 auto tree_height = fmm::fmm_tree_height(n_centers);
73 if (tree_height != last_tree_height) {
74 solver = std::make_unique<rbf_solver>(model_, tree_height, bbox_);
75 res_eval = std::make_unique<rbf_evaluator<>>(model_, tree_height, bbox_);
76 last_tree_height = tree_height;
79 geometry::points3d center_points = points_(centers, Eigen::all);
81 valuesd center_values = values(centers, Eigen::all);
82 for (index_t i = n_eq; i < n_centers; i++) {
83 auto idx = centers.at(i);
84 center_values(i) = active_lb_idcs.contains(idx) ? values_lb(idx) : values_ub(idx);
87 center_weights = weights(centers, Eigen::all);
88 center_weights.conservativeResize(n_centers + n_poly_basis_);
89 center_weights.tail(n_poly_basis_) = weights.tail(n_poly_basis_);
91 solver->set_points(center_points);
92 center_weights = solver->solve(center_values, absolute_tolerance, max_iter, center_weights);
94 for (index_t i = 0; i < n_centers; i++) {
95 auto idx = centers.at(i);
96 weights(idx) = center_weights(i);
98 weights.tail(n_poly_basis_) = center_weights.tail(n_poly_basis_);
100 res_eval->set_source_points(center_points);
101 res_eval->set_weights(center_weights);
102 values_fit = res_eval->evaluate(ineq_points);
104 center_weights = valuesd::Zero(n_poly_basis_);
105 values_fit = valuesd::Zero(n_ineq);
110 auto indices = complementary_indices(centers);
111 auto n_indices =
static_cast<index_t
>(indices.size());
112 valuesd residuals = valuesd::Zero(n_indices);
113 for (index_t i = 0; i < n_indices; i++) {
114 auto idx = indices.at(i);
115 auto lb = values_lb(idx);
116 auto ub = values_ub(idx);
117 auto lb_res = std::isnan(lb) ? 0.0 : std::max(lb - values_fit(i), 0.0);
118 auto ub_res = std::isnan(ub) ? 0.0 : std::max(values_fit(i) - ub, 0.0);
119 residuals(i) = std::max(lb_res, ub_res);
121 common::zip_sort(indices.begin(), indices.end(), residuals.begin(), residuals.end(),
122 [](
const auto& a,
const auto& b) { return a.second > b.second; });
123 point_cloud::distance_filter filter(points_, filtering_distance, indices);
124 std::unordered_set<index_t> filtered_indices(filter.filtered_indices().begin(),
125 filter.filtered_indices().end());
129 auto active_set_changed =
false;
130 for (index_t i = 0; i < n_ineq; i++) {
131 auto idx = ineq_idcs.at(i);
133 if (!std::isnan(values_lb(idx))) {
134 if (active_lb_idcs.contains(idx)) {
135 if (weights(idx) <= 0.0) {
136 active_lb_idcs.erase(idx);
137 active_set_changed =
true;
140 if (values_fit(i) < values_lb(idx) - absolute_tolerance) {
141 if (filtered_indices.contains(idx)) {
142 active_lb_idcs.insert(idx);
145 active_set_changed =
true;
149 if (!std::isnan(values_ub(idx))) {
150 if (active_ub_idcs.contains(idx)) {
151 if (weights(idx) >= 0.0) {
152 active_ub_idcs.erase(idx);
153 active_set_changed =
true;
156 if (values_fit(i) > values_ub(idx) + absolute_tolerance) {
157 if (filtered_indices.contains(idx)) {
158 active_ub_idcs.insert(idx);
161 active_set_changed =
true;
167 if (!active_set_changed) {
171 filtering_distance *= 0.5;
174 return {std::move(centers), std::move(center_weights)};
177 std::vector<index_t> rbf_inequality_fitter::complementary_indices(
178 const std::vector<index_t>& indices)
const {
179 std::vector<index_t> c_idcs(n_points_ - indices.size());
181 auto universe = boost::irange<index_t>(index_t{0}, n_points_);
183 std::sort(idcs.begin(), idcs.end());
184 std::set_difference(universe.begin(), universe.end(), idcs.begin(), idcs.end(), c_idcs.begin());