Physics Simulation & Visualization Tool 0.1
A C++ physics simulation engine with real-time 3D visualization
Loading...
Searching...
No Matches
VectorRootSolver.cpp
Go to the documentation of this file.
1#include "VectorRootSolver.h"
2#include <iostream>
3
4namespace {
5constexpr float kSingularJacobianDeterminant = 1.0e-8f;
6constexpr float kSingularJacobianNudgeFactor = 0.01f;
7}
8
9template<typename InputT, typename OutputT>
10VectorRootSolver<InputT, OutputT>::VectorRootSolver(InitialGuessSetter initialGuessSetter, StopCondition stopCondition, ResultExtractor extractResult, const OutputT &tgt, double tol, int maxIter, double jacobianStep, double damping)
11 : setGuess(std::move(initialGuessSetter)), stopCondition(std::move(stopCondition)), extract(std::move(extractResult)), target(tgt), current(static_cast<InputT>(0)), maxIterations(maxIter), h(jacobianStep), tolerance(tol), alpha(damping) {
12 current = glm::vec3(0.0f); // Our initial guess is all zeroes
13 fPerturbed.fill(glm::vec3(0.0f));
14 state = SolverState::WaitingForBase;
15}
16
17template<typename InputT, typename OutputT>
19 switch (state) {
20 case SolverState::WaitingForBase:
21 // Wait until the simulation has run for the current guess.
22 // This ensures we have the base output for the current input.
23 if (!stopCondition()) return false;
24
25 baseOutput = extract();
26 if (glm::length(baseOutput - target) < tolerance) {
27 return true;
28 }
29 if (iterationCount >= maxIterations) {
30 return true;
31 }
32
33 currentPerturbation = 0;
34 state = SolverState::PerturbComponent;
35 return false;
36
37 case SolverState::PerturbComponent:
38 // Create a perturbed input by slightly incrementing the current component
39 xPerturbed = current;
40 xPerturbed[currentPerturbation] += h;
41
42 setGuess(xPerturbed); // resets simulation with perturbed guess
43 state = SolverState::WaitingForPerturbed; // Wait for the simulation to finish with this perturbed guess
44 return false;
45
46 case SolverState::WaitingForPerturbed:
47 // Wait until the simulation has run for the perturbed input
48 if (!stopCondition()) return false;
49
50 fPerturbed[currentPerturbation] = extract();
51
52 currentPerturbation++;
53 // Move to the next component to perturb
54 if (currentPerturbation < 3) // TODO: currently only works for F: R^3 -> R^3
55 state = SolverState::PerturbComponent;
56 else
57 state = SolverState::ComputeJacobian;
58
59 return false;
60
61 case SolverState::ComputeJacobian: {
62 glm::mat3 J(0.0f);
63 for (int j = 0; j < 3; ++j)
64 for (int i = 0; i < 3; ++i) {
65 // This is an approximation for the partial derivative for small h
66 J[i][j] = (fPerturbed[j][i] - baseOutput[i]) / h;
67 }
68
69 glm::vec3 error = baseOutput - target;
70
71 float detJ = glm::determinant(J);
72 glm::vec3 dx;
73
74 if (std::abs(detJ) < kSingularJacobianDeterminant) {
75 // Jacobian nearly singular, apply a small nudge in direction of negative error
76 dx = -kSingularJacobianNudgeFactor * error;
77 } else {
78 // Normal Newton step with damping
79 dx = -alpha * glm::inverse(J) * error;
80 }
81
82 current += dx;
83 ++iterationCount;
84 setGuess(current);
85 state = SolverState::WaitingForBase;
86
87 // Go back to WaitingForBase to check if new guess works
88 return false;
89 }
90 }
91 return false;
92}
93
A vector-valued root solver using Newton's method.
std::function< void(const InputT &)> InitialGuessSetter
std::function< OutputT()> ResultExtractor
VectorRootSolver(InitialGuessSetter initialGuessSetter, StopCondition stopCondition, ResultExtractor extractResult, const OutputT &target, double tolerance=1e-3, int maxIterations=30, double jacobianStep=0.01, double damping=1.0)
Construct a new VectorRootSolver object.
bool stepFrame() override
Performs one iteration of the vector root-finding solver.
std::function< bool()> StopCondition
Hash specialization for Vertex to enable use in unordered containers.
Definition Mesh.h:33