Wildmeshing Toolkit
AMIPSOptimizationSmoothing.cpp
Go to the documentation of this file.
2 
3 #include <polysolve/nonlinear/Problem.hpp>
4 #include <wmtk/Mesh.hpp>
5 #include <wmtk/Types.hpp>
9 #include <wmtk/utils/Logger.hpp>
10 #include <wmtk/utils/orient.hpp>
11 
12 #include <polysolve/nonlinear/Solver.hpp>
13 
14 #include <Eigen/Core>
15 
16 namespace wmtk::operations {
17 
18 template <int S>
19 class AMIPSOptimizationSmoothing::WMTKAMIPSProblem : public polysolve::nonlinear::Problem
20 {
21 public:
22  using typename polysolve::nonlinear::Problem::Scalar;
23  using typename polysolve::nonlinear::Problem::THessian;
24  using typename polysolve::nonlinear::Problem::TVector;
25 
26  WMTKAMIPSProblem(std::vector<std::array<double, S>>& cells);
27 
28  TVector initial_value() const;
29 
30  double value(const TVector& x) override;
31  void gradient(const TVector& x, TVector& gradv) override;
32  void hessian(const TVector& x, THessian& hessian) override
33  {
34  throw std::runtime_error("Sparse functions do not exist, use dense solver");
35  }
36  void hessian(const TVector& x, Eigen::MatrixXd& hessian) override;
37 
38  void solution_changed(const TVector& new_x) override;
39 
40  bool is_step_valid(const TVector& x0, const TVector& x1) override;
41 
42 private:
43  std::vector<std::array<double, S>> m_cells;
44 // Enables the use of power of p to approximate minimizing max energy
45 // #define WMTK_ENABLE_P
46 #ifdef WMTK_ENABLE_P
47  const int p = 4;
48 #endif
49 };
50 
51 template <int S>
53  std::vector<std::array<double, S>>& cells)
54  : m_cells(cells)
55 {}
56 
57 template <int S>
59 {
60  constexpr int64_t size = S == 6 ? 2 : 3;
61  Eigen::Matrix<double, size, 1> tmp;
62 
63  for (int64_t d = 0; d < size; ++d) {
64  tmp(d) = m_cells[0][d];
65  }
66 
67  return tmp;
68 }
69 
70 template <int S>
72 {
73  double res = 0;
74  for (auto c : m_cells) {
75  if constexpr (S == 6) {
76  assert(x.size() == 2);
77  c[0] = x[0];
78  c[1] = x[1];
79 #ifdef WMTK_ENABLE_P
81 #else
83 #endif
84  } else {
85  assert(x.size() == 3);
86  c[0] = x[0];
87  c[1] = x[1];
88  c[2] = x[2];
89 #ifdef WMTK_ENABLE_P
91 #else
93 #endif
94  }
95  }
96 
97  // std::cout << res << std::endl;
98  return res;
99 }
100 
101 template <int S>
102 void AMIPSOptimizationSmoothing::WMTKAMIPSProblem<S>::gradient(const TVector& x, TVector& gradv)
103 {
104  constexpr int64_t size = S == 6 ? 2 : 3;
105  gradv.resize(size);
106  gradv.setZero();
107  Eigen::Matrix<double, size, 1> tmp(size);
108 #ifdef WMTK_ENABLE_P
109  double tmp2 = 0;
110 #endif
111 
112  for (auto c : m_cells) {
113  if constexpr (S == 6) {
114  assert(x.size() == 2);
115  c[0] = x[0];
116  c[1] = x[1];
118 #ifdef WMTK_ENABLE_P
120 #endif
121  } else {
122  assert(x.size() == 3);
123  c[0] = x[0];
124  c[1] = x[1];
125  c[2] = x[2];
127 #ifdef WMTK_ENABLE_P
129 #endif
130  }
131 #ifdef WMTK_ENABLE_P
132  gradv += double(p) * pow(tmp2, p - 1) * tmp;
133 #else
134  gradv += tmp;
135 #endif
136  }
137 }
138 
139 template <int S>
141  const TVector& x,
142  Eigen::MatrixXd& hessian)
143 {
144  // std::cout << "error here !!! Hessian used" << std::endl;
145  constexpr int64_t size = S == 6 ? 2 : 3;
146  hessian.resize(size, size);
147  hessian.setZero();
148  Eigen::Matrix<double, size, size> tmp;
149 #ifdef WMTK_ENABLE_P
150  double tmp2 = 0;
151  Eigen::Matrix<double, size, 1> tmpj(size);
152 #endif
153 
154  for (auto c : m_cells) {
155  if constexpr (S == 6) {
156  assert(x.size() == 2);
157  c[0] = x[0];
158  c[1] = x[1];
160 #ifdef WMTK_ENABLE_P
163 #endif
164  } else {
165  assert(x.size() == 3);
166  c[0] = x[0];
167  c[1] = x[1];
168  c[2] = x[2];
170 #ifdef WMTK_ENABLE_P
173 #endif
174  }
175 #ifdef WMTK_ENABLE_P
176  hessian += (p) * (p - 1) * std::pow(tmp2, p - 2) * tmpj * tmpj.transpose() +
177  p * std::pow(tmp2, p - 1) * tmp;
178 #else
179  hessian += tmp;
180 #endif
181  // 12 f(x)^2 * f(x)'*f^T'(x) + 4 f(x)^3 * H
182  }
183 }
184 
185 template <int S>
187 {}
188 
189 template <int S>
191  const TVector& x0,
192  const TVector& x1)
193 {
194  constexpr int64_t size = S == 6 ? 2 : 3;
195  Eigen::Matrix<double, size, 1> p0 = x1, p1, p2, p3;
196  if constexpr (S == 6) {
197  for (const auto& c : m_cells) {
198  p1 << c[2], c[3];
199  p2 << c[4], c[5];
200 
201  if (wmtk::utils::wmtk_orient2d(p0, p1, p2) <= 0) return false;
202  }
203  } else {
204  for (const auto& c : m_cells) {
205  p1 << c[3], c[4], c[5];
206  p2 << c[6], c[7], c[8];
207  p3 << c[9], c[10], c[11];
208 
209  // Eigen::Matrix<double, size, 1> x0m = x0;
210  // assert(wmtk::utils::wmtk_orient3d(x0m, p1, p2, p3) > 0);
211  // if (wmtk::utils::wmtk_orient3d(p0, p1, p2, p3) <= 0) {
212  // // std::cout << wmtk::utils::wmtk_orient3d(x0m, p1, p2, p3) << std::endl;
213  // return false;
214  // }
215 
216  Eigen::Matrix<double, size, 1> x0m = x0;
217  assert(wmtk::utils::wmtk_orient3d(p3, x0m, p1, p2) > 0);
218  if (wmtk::utils::wmtk_orient3d(p3, p0, p1, p2) <= 0) {
219  // std::cout << wmtk::utils::wmtk_orient3d(p3, x0m, p1, p2) << std::endl;
220  return false;
221  }
222  }
223  }
224  return true;
225 }
226 
227 
229  Mesh& mesh,
230  const attribute::MeshAttributeHandle& coords)
232  , m_coordinate_handle(coords)
233  , m_amips(mesh, coords)
234 {
235  // assert(m_coordinate_handle.holds<Rational>());
236 
237  m_linear_solver_params = R"({"solver": "Eigen::LDLT"})"_json;
238  m_nonlinear_solver_params = R"({"solver": "DenseNewton", "max_iterations": 10})"_json;
239  // m_nonlinear_solver_params = R"({"solver": "L-BFGS", "max_iterations": 100,
240  // "advanced":{"apply_gradient_fd": "FullFiniteDiff"}})"_json;
241  // m_nonlinear_solver_params = R"({"solver": "GradientDescent", "max_iterations": 100})"_json;
242 
243 
244  create_solver();
245 }
246 
248 {
249  m_solver = polysolve::nonlinear::Solver::create(
252  1,
253  opt_logger());
254 }
255 
256 
257 std::vector<simplex::Simplex> AMIPSOptimizationSmoothing::execute(const simplex::Simplex& simplex)
258 {
260  auto accessor = mesh().create_accessor(m_coordinate_handle.as<Rational>());
261 
263  mesh(),
264  simplex,
265  mesh().top_simplex_type());
266 
267  if (mesh().top_simplex_type() == PrimitiveType::Triangle) {
268  std::vector<std::array<double, 6>> cells;
269 
270  for (const simplex::Simplex& cell : neighs) {
271  cells.emplace_back(m_amips.get_raw_coordinates<3, 2>(cell, simplex));
272  }
273  WMTKAMIPSProblem<6> problem(cells);
274 
275  auto x = problem.initial_value();
276  try {
277  m_solver->minimize(problem, x);
278 
279  for (int64_t d = 0; d < m_coordinate_handle.dimension(); ++d) {
280  accessor.vector_attribute(simplex.tuple())[d] = Rational(x[d], true);
281  }
282  } catch (const std::exception&) {
283  return {};
284  }
285  } else {
286  assert(mesh().top_simplex_type() == PrimitiveType::Tetrahedron);
287 
288  std::vector<std::array<double, 12>> cells;
289 
290  for (simplex::Simplex cell : neighs) {
291  // auto vertices = mesh().orient_vertices(cell.tuple());
292  // int idx = -1;
293  // for (int i = 0; i < 4; ++i) {
294  // if (simplex::Simplex::vertex(mesh(), vertices[i]) == simplex) {
295  // idx = i;
296  // break;
297  // }
298  // }
299  // if (idx == -1) {
300  // std::cout << "idx not found" << std::endl;
301  // }
302 
303  // std::rotate(vertices.begin(), vertices.begin() + idx, vertices.end());
304 
305 
306  // assert(vertices.size() == 4);
307  // if (!simplex::utils::SimplexComparisons::equal(
308  // mesh(),
309  // simplex::Simplex::vertex(mesh(), vertices[0]),
310  // simplex)) {
311  // std::cout << "error here" << std::endl;
312  // }
313 
314  if (!mesh().is_ccw(cell.tuple())) {
315  // switch any local id but NOT the vertex
316  cell = simplex::Simplex(
317  mesh(),
318  cell.primitive_type(),
319  mesh().switch_tuple(cell.tuple(), PrimitiveType::Edge));
320  }
321  assert(mesh().is_ccw(cell.tuple()));
322 
323  const auto vertices =
325 
326 
327  assert(vertices.size() == 4);
329  mesh(),
330  vertices.simplex_vector()[0],
331  simplex)) {
332  std::cout << "error here" << std::endl;
333  }
334 
335 
336  std::array<double, 12> single_cell;
337  std::vector<Vector3r> ps;
338  for (size_t i = 0; i < 4; ++i) {
339  const simplex::Simplex& v = vertices.simplex_vector()[i];
340  // const auto p = accessor.const_vector_attribute(vertices[i]);
341  const auto p = accessor.const_vector_attribute(v);
342  ps.push_back(p);
343  single_cell[3 * i + 0] = p[0].to_double();
344  single_cell[3 * i + 1] = p[1].to_double();
345  single_cell[3 * i + 2] = p[2].to_double();
346 
347  if (!p[0].is_rounded() || !p[1].is_rounded() || !p[2].is_rounded()) return {};
348  }
349  // if (wmtk::utils::wmtk_orient3d(ps[3], ps[0], ps[1], ps[2]) <= 0) {
350  // std::cout << "this is wrong" << std::endl;
351  // }
352 
353  cells.emplace_back(single_cell);
354 
355  // cells.emplace_back(m_amips.get_raw_coordinates<4, 3>(cell, simplex));
356  }
357  WMTKAMIPSProblem<12> problem(cells);
358 
359  {
360  Eigen::Vector3d x1(cells[0][0], cells[0][1], cells[0][2]);
361  if (!problem.is_step_valid(x1, x1)) {
362  std::cout << "step is not valid!!!!!!!!!!!!!!!!" << std::endl;
363  }
364  }
365 
366  auto x = problem.initial_value();
367  auto x0 = problem.initial_value();
368 
369  try {
370  m_solver->minimize(problem, x);
371 
372  } catch (const std::exception&) {
373  }
374 
375  // Hack for surface only
376 
377  auto child_meshes = mesh().get_child_meshes();
378 
379 
380  double alpha = 1.00;
381  // for (auto child_mesh : child_meshes) {
382  // if (!mesh().map_to_child(*child_mesh, simplex).empty()) {
383  // alpha = 0.01;
384  // break;
385  // }
386  // }
387 
388 
389  for (int64_t d = 0; d < m_coordinate_handle.dimension(); ++d) {
390  // accessor.vector_attribute(simplex.tuple())[d] = Rational(x[d], true);
391  accessor.vector_attribute(simplex.tuple())[d] =
392  Rational((1 - alpha) * x0[d] + alpha * x[d], true);
393  }
394  }
395 
396  // assert(attribute_handle() == m_function.attribute_handle());
397 
398  double res = 0;
399  } else {
400  auto accessor = mesh().create_accessor(m_coordinate_handle.as<double>());
401 
403  mesh(),
404  simplex,
405  mesh().top_simplex_type());
406 
407  if (mesh().top_simplex_type() == PrimitiveType::Triangle) {
408  std::vector<std::array<double, 6>> cells;
409 
410  for (const simplex::Simplex& cell : neighs) {
411  cells.emplace_back(m_amips.get_raw_coordinates<3, 2>(cell, simplex));
412  }
413  WMTKAMIPSProblem<6> problem(cells);
414 
415  auto x = problem.initial_value();
416  try {
417  m_solver->minimize(problem, x);
418 
419  for (int64_t d = 0; d < m_coordinate_handle.dimension(); ++d) {
420  accessor.vector_attribute(simplex.tuple())[d] = x[d];
421  }
422  } catch (const std::exception&) {
423  return {};
424  }
425  } else {
426  assert(mesh().top_simplex_type() == PrimitiveType::Tetrahedron);
427 
428  std::vector<std::array<double, 12>> cells;
429 
430  for (simplex::Simplex cell : neighs) {
431  if (!mesh().is_ccw(cell.tuple())) {
432  // switch any local id but NOT the vertex
433  cell = simplex::Simplex(
434  mesh(),
435  cell.primitive_type(),
436  mesh().switch_tuple(cell.tuple(), PrimitiveType::Edge));
437  }
438  assert(mesh().is_ccw(cell.tuple()));
439 
440  const auto vertices =
442 
443 
444  assert(vertices.size() == 4);
446  mesh(),
447  vertices.simplex_vector()[0],
448  simplex)) {
449  std::cout << "error here" << std::endl;
450  }
451 
452 
453  std::array<double, 12> single_cell;
454  std::vector<Vector3d> ps;
455  for (size_t i = 0; i < 4; ++i) {
456  const simplex::Simplex& v = vertices.simplex_vector()[i];
457  // const auto p = accessor.const_vector_attribute(vertices[i]);
458  const auto p = accessor.const_vector_attribute(v);
459  ps.push_back(p);
460  single_cell[3 * i + 0] = p[0];
461  single_cell[3 * i + 1] = p[1];
462  single_cell[3 * i + 2] = p[2];
463  }
464  // if (wmtk::utils::wmtk_orient3d(ps[3], ps[0], ps[1], ps[2]) <= 0) {
465  // std::cout << "this is wrong" << std::endl;
466  // }
467 
468  cells.emplace_back(single_cell);
469 
470  // cells.emplace_back(m_amips.get_raw_coordinates<4, 3>(cell, simplex));
471  }
472  WMTKAMIPSProblem<12> problem(cells);
473 
474  {
475  Eigen::Vector3d x1(cells[0][0], cells[0][1], cells[0][2]);
476  if (!problem.is_step_valid(x1, x1)) {
477  std::cout << "step is not valid!!!!!!!!!!!!!!!!" << std::endl;
478  }
479  }
480 
481  auto x = problem.initial_value();
482  auto x0 = problem.initial_value();
483 
484  try {
485  m_solver->minimize(problem, x);
486 
487  } catch (const std::exception&) {
488  }
489 
490  // Hack for surface only
491 
492  auto child_meshes = mesh().get_child_meshes();
493 
494 
495  double alpha = 1.00;
496  // for (auto child_mesh : child_meshes) {
497  // if (!mesh().map_to_child(*child_mesh, simplex).empty()) {
498  // alpha = 0.01;
499  // break;
500  // }
501  // }
502 
503 
504  for (int64_t d = 0; d < m_coordinate_handle.dimension(); ++d) {
505  // accessor.vector_attribute(simplex.tuple())[d] = Rational(x[d], true);
506  accessor.vector_attribute(simplex.tuple())[d] = (1 - alpha) * x0[d] + alpha * x[d];
507  }
508  }
509 
510  // assert(attribute_handle() == m_function.attribute_handle());
511 
512  double res = 0;
513  }
514 
515 
516  return AttributesUpdate::execute(simplex);
517 }
518 
519 } // namespace wmtk::operations
attribute::Accessor< T, Mesh, D > create_accessor(const attribute::MeshAttributeHandle &handle)
std::vector< std::shared_ptr< Mesh > > get_child_meshes() const
returns the direct multimesh child meshes for the current mesh
auto as() const -> const held_handle_type< held_type_from_primitive< T >()> &
std::array< double, NV *DIM > get_raw_coordinates(const simplex::Simplex &domain_simplex, const std::optional< simplex::Simplex > &variable_simplex={}) const
Definition: AMIPS.cpp:638
void hessian(const TVector &x, THessian &hessian) override
bool is_step_valid(const TVector &x0, const TVector &x1) override
std::vector< simplex::Simplex > execute(const simplex::Simplex &simplex) override
returns an empty vector in case of failure
std::shared_ptr< polysolve::nonlinear::Solver > m_solver
const attribute::MeshAttributeHandle & m_coordinate_handle
AMIPSOptimizationSmoothing(Mesh &mesh, const attribute::MeshAttributeHandle &coords)
virtual std::vector< simplex::Simplex > execute(const simplex::Simplex &simplex) override
returns an empty vector in case of failure
const Mesh & mesh() const
Definition: Operation.hpp:45
const Tuple & tuple() const
Definition: Simplex.hpp:53
static bool equal(const Mesh &m, const Simplex &s0, const Simplex &s1)
bool is_ccw(const Tuple &t)
Definition: is_ccw.hxx:8
void Tet_AMIPS_jacobian(const std::array< double, 12 > &T, Eigen::Vector3d &result_0)
Definition: AMIPS.cpp:75
void Tri_AMIPS_hessian(const std::array< double, 6 > &T, Eigen::Matrix2d &result_0)
Definition: AMIPS.cpp:542
void Tri_AMIPS_jacobian(const std::array< double, 6 > &T, Eigen::Vector2d &result_0)
Definition: AMIPS.cpp:503
double Tet_AMIPS_energy(const std::array< double, 12 > &T)
Definition: AMIPS.cpp:375
void Tet_AMIPS_hessian(const std::array< double, 12 > &T, Eigen::Matrix3d &result_0)
Definition: AMIPS.cpp:172
double Tri_AMIPS_energy(const std::array< double, 6 > &T)
Definition: AMIPS.cpp:473
std::vector< Tuple > vertices(const Mesh &m, const Simplex &simplex)
std::vector< Simplex > cofaces_single_dimension_simplices(const Mesh &mesh, const Simplex &simplex, PrimitiveType cofaces_type)
SimplexCollection faces_single_dimension(const Mesh &mesh, const Simplex &simplex, const PrimitiveType face_type)
Returns a vector with all faces in the boundary of a simplex of the given dimension.
int wmtk_orient3d(const Eigen::Ref< const Eigen::Vector3< Rational >> &p0, const Eigen::Ref< const Eigen::Vector3< Rational >> &p1, const Eigen::Ref< const Eigen::Vector3< Rational >> &p2, const Eigen::Ref< const Eigen::Vector3< Rational >> &p3)
Definition: orient.cpp:75
int wmtk_orient2d(double p0x, double p0y, double p1x, double p1y, double p2x, double p2y)
Definition: orient.cpp:178
Vector< double, 3 > Vector3d
Definition: Types.hpp:39
spdlog::logger & opt_logger()
Retrieves the logger for the optimization.
Definition: Logger.cpp:75
Rational pow(const Rational &x, int p)
Definition: Rational.cpp:166