testFourier.cpp
Go to the documentation of this file.
1 /* ----------------------------------------------------------------------------
2 
3  * GTSAM Copyright 2010, Georgia Tech Research Corporation,
4  * Atlanta, Georgia 30332-0415
5  * All Rights Reserved
6  * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7 
8  * See LICENSE for the license information
9 
10  * -------------------------------------------------------------------------- */
11 
20 #include <gtsam/base/Testable.h>
21 #include <gtsam/basis/FitBasis.h>
22 #include <gtsam/basis/Fourier.h>
24 
25 using namespace std;
26 using namespace gtsam;
27 
28 auto model = noiseModel::Unit::Create(1);
29 
30 // Coefficients for testing, respectively 3 and 7 parameter Fourier basis.
31 // They correspond to best approximation of TestFunction(x)
32 const Vector k3Coefficients = (Vector3() << 1.5661, 1.2717, 1.2717).finished();
33 const Vector7 k7Coefficients =
34  (Vector7() << 1.5661, 1.2717, 1.2717, -0.0000, 0.5887, -0.0943, 0.0943)
35  .finished();
36 
37 // The test-function used below
38 static double TestFunction(double x) { return exp(sin(x) + cos(x)); }
39 
40 //******************************************************************************
41 TEST(Basis, BasisEvaluationFunctor) {
42  // fx(0) takes coefficients c to calculate the value f(c;x==0)
43  FourierBasis::EvaluationFunctor fx(3, 0);
45  fx(k3Coefficients), 1e-9);
46 }
47 
48 //******************************************************************************
49 TEST(Basis, BasisEvaluationFunctorDerivative) {
50  // If we give the H argument, we get the derivative of fx(0) wrpt coefficients
51  // Needs to be Matrix so it can be used by OptionalJacobian.
52  Matrix H(1, 3);
53  FourierBasis::EvaluationFunctor fx(3, 0);
55  fx(k3Coefficients, H), 1e-9);
56 
57  Matrix13 expectedH(1, 1, 0);
58  EXPECT(assert_equal(expectedH, H));
59 }
60 
61 //******************************************************************************
62 TEST(Basis, Manual) {
63  const size_t N = 3;
64 
65  // We will create a linear factor graph
67 
68  // We create an unknown Vector expression for the coefficients
69  Key key(1);
70 
71  // We will need values below to test the Jacobians
72  Values values;
73  values.insert<Vector>(key, Vector::Zero(N)); // value does not really matter
74 
75  // At 16 different samples points x, check Predict_ expression
76  for (size_t i = 0; i < 16; i++) {
77  const double x = i * M_PI / 8;
78  const double desiredValue = TestFunction(x);
79 
80  // Manual JacobianFactor
81  Matrix A(1, N);
82  A << 1, cos(x), sin(x);
83  Vector b(1);
84  b << desiredValue;
85  JacobianFactor linearFactor(key, A, b);
86  graph.add(linearFactor);
87 
88  // Create factor to predict value at x
89  EvaluationFactor<FourierBasis> predictFactor(key, desiredValue, model, N,
90  x);
91 
92  // Check expression Jacobians
93  EXPECT_CORRECT_FACTOR_JACOBIANS(predictFactor, values, 1e-5, 1e-9);
94 
95  auto linearizedFactor = predictFactor.linearize(values);
96  auto linearizedJacobianFactor =
97  std::dynamic_pointer_cast<JacobianFactor>(linearizedFactor);
98  CHECK(linearizedJacobianFactor); // makes sure it's indeed a JacobianFactor
99  EXPECT(assert_equal(linearFactor, *linearizedJacobianFactor, 1e-9));
100  }
101 
102  // Solve linear graph
103  VectorValues actual = graph.optimize();
104  EXPECT(assert_equal((Vector)k3Coefficients, actual.at(key), 1e-4));
105 }
106 
107 //******************************************************************************
109  // Check fitting a function with a 7-parameter Fourier basis
110 
111  // Create linear factor graph
113  Key key(1);
114  for (size_t i = 0; i < 16; i++) {
115  double x = i * M_PI / 8, desiredValue = TestFunction(x);
117  model, 7, x);
118  }
119 
120  // Solve FourierFactorGraph
121  Values values;
122  values.insert<Vector>(key, Vector::Zero(7));
123  GaussianFactorGraph::shared_ptr lfg = graph.linearize(values);
124  VectorValues actual = lfg->optimize();
125 
126  EXPECT(assert_equal((Vector)k7Coefficients, actual.at(key), 1e-4));
127 }
128 
129 //******************************************************************************
130 TEST(Basis, WeightMatrix) {
131  // The WeightMatrix creates an m*n matrix, where m is the number of sample
132  // points, and n is the number of parameters.
133  Matrix expected(2, 3);
134  expected.row(0) << 1, cos(1), sin(1);
135  expected.row(1) << 1, cos(2), sin(2);
136  Vector2 X(1, 2);
137  Matrix actual = FourierBasis::WeightMatrix(3, X);
138  EXPECT(assert_equal(expected, actual, 1e-9));
139 }
140 
141 //******************************************************************************
142 TEST(Basis, Decomposition) {
143  // Create example sequence
145  for (size_t i = 0; i < 16; i++) {
146  double x = i * M_PI / 8, y = TestFunction(x);
147  sequence[x] = y;
148  }
149 
150  // Do Fourier Decomposition
151  FitBasis<FourierBasis> actual(sequence, model, 3);
152 
153  // Check
155 }
156 
157 //******************************************************************************
158 // Check derivative in two different ways: numerical and using D on f
159 double proxy(double x) {
160  return FourierBasis::EvaluationFunctor(7, x)(k7Coefficients);
161 }
162 
163 TEST(Basis, Derivative7) {
164  // Check Derivative evaluation at point x=0.2
165 
166  // Calculate expected values by numerical derivative of proxy.
167  const double x = 0.2;
168  Matrix numeric_dTdx = numericalDerivative11<double, double>(proxy, x);
169 
170  // Calculate derivatives at Chebyshev points using D7, interpolate
171  Matrix D7 = FourierBasis::DifferentiationMatrix(7);
172  Vector derivativeCoefficients = D7 * k7Coefficients;
173  FourierBasis::EvaluationFunctor fx(7, x);
174  EXPECT_DOUBLES_EQUAL(numeric_dTdx(0, 0), fx(derivativeCoefficients), 1e-8);
175 
176  // Do directly
177  FourierBasis::DerivativeFunctor dfdx(7, x);
178  EXPECT_DOUBLES_EQUAL(numeric_dTdx(0, 0), dfdx(k7Coefficients), 1e-8);
179 }
180 
181 //******************************************************************************
182 TEST(Basis, VecDerivativeFunctor) {
183  using DotShape = typename FourierBasis::VectorDerivativeFunctor;
184  const size_t N = 3;
185 
186  // MATLAB example, Dec 27 2019, commit 014eded5
187  double h = 2 * M_PI / 16;
188  Vector2 dotShape(0.5556, -0.8315); // at h/2
189  DotShape dotShapeFunction(2, N, h / 2);
190  Matrix theta = (Matrix32() << 0, 0, 0.7071, 0.7071, 0.7071, -0.7071)
191  .finished()
192  .transpose();
193  EXPECT(assert_equal(Vector(dotShape), dotShapeFunction(theta), 1e-4));
194 }
195 
196 //******************************************************************************
197 // Suppose we want to parameterize a periodic function with function values at
198 // specific times, rather than coefficients. Can we do it? This would be a
199 // generalization of the Fourier transform, and constitute a "pseudo-spectral"
200 // parameterization. One way to do this is to establish hard constraints that
201 // express the relationship between the new parameters and the coefficients.
202 // For example, below I'd like the parameters to be the function values at
203 // X = {0.1,0.2,0.3}, rather than a 3-vector of coefficients.
204 // Because the values f(X) = at these points can be written as f(X) = W(X)*c,
205 // we can simply express the coefficents c as c=inv(W(X))*f, which is a
206 // generalized Fourier transform. That also means we can create factors with the
207 // unknown f-values, as done manually below.
208 TEST(Basis, PseudoSpectral) {
209  // We will create a linear factor graph
211 
212  const size_t N = 3;
213  const Key key(1);
214 
215  // The correct values at X = {0.1,0.2,0.3} are simply W*c
216  const Vector X = (Vector3() << 0.1, 0.2, 0.3).finished();
217  const Matrix W = FourierBasis::WeightMatrix(N, X);
218  const Vector expected = W * k3Coefficients;
219 
220  // Check those values are indeed correct values of Fourier approximation
221  using Eval = FourierBasis::EvaluationFunctor;
222  EXPECT_DOUBLES_EQUAL(Eval(N, 0.1)(k3Coefficients), expected(0), 1e-9);
223  EXPECT_DOUBLES_EQUAL(Eval(N, 0.2)(k3Coefficients), expected(1), 1e-9);
224  EXPECT_DOUBLES_EQUAL(Eval(N, 0.3)(k3Coefficients), expected(2), 1e-9);
225 
226  // Calculate "inverse Fourier transform" matrix
227  const Matrix invW = W.inverse();
228 
229  // At 16 different samples points x, add a factor on fExpr
230  for (size_t i = 0; i < 16; i++) {
231  const double x = i * M_PI / 8;
232  const double desiredValue = TestFunction(x);
233 
234  // Manual JacobianFactor
235  Matrix A(1, 3);
236  A << 1, cos(x), sin(x);
237  Vector b(1);
238  b << desiredValue;
239  JacobianFactor linearFactor(key, A * invW, b);
240  graph.add(linearFactor);
241  }
242 
243  // Solve linear graph
244  VectorValues actual = graph.optimize();
245  EXPECT(assert_equal((Vector)expected, actual.at(key), 1e-4));
246 }
247 
248 //******************************************************************************
249 int main() {
250  TestResult tr;
251  return TestRegistry::runAllTests(tr);
252 }
253 //******************************************************************************
const gtsam::Symbol key('X', 0)
#define CHECK(condition)
Definition: Test.h:108
Jet< T, N > cos(const Jet< T, N > &f)
Definition: jet.h:426
Scalar * y
Scalar * b
Definition: benchVecAdd.cpp:17
Concept check for values that can be used in unit tests.
IsDerived< DERIVEDFACTOR > emplace_shared(Args &&... args)
Emplace a shared pointer to factor of given type.
Definition: FactorGraph.h:196
static int runAllTests(TestResult &result)
Eigen::Vector3d Vector3
Definition: Vector.h:43
Matrix expected
Definition: testMatrix.cpp:971
const Vector7 k7Coefficients
Definition: testFourier.cpp:33
int main()
bool assert_equal(const Matrix &expected, const Matrix &actual, double tol)
Definition: Matrix.cpp:40
Eigen::MatrixXd Matrix
Definition: base/Matrix.h:39
#define M_PI
Definition: main.h:106
leaf::MyValues values
Jet< T, N > sin(const Jet< T, N > &f)
Definition: jet.h:439
Fourier decomposition, see e.g. http://mathworld.wolfram.com/FourierSeries.html.
Definition: BFloat16.h:88
TEST(Basis, BasisEvaluationFunctor)
Definition: testFourier.cpp:41
Key W(std::uint64_t j)
Evaluate derivatives of a nonlinear factor numerically.
Matrix< SCALARA, Dynamic, Dynamic, opt_A > A
Definition: bench_gemm.cpp:48
set noclip points set clip one set noclip two set bar set border lt lw set xdata set ydata set zdata set x2data set y2data set boxwidth set dummy y set format x g set format y g set format x2 g set format y2 g set format z g set angles radians set nogrid set key title set key left top Right noreverse box linetype linewidth samplen spacing width set nolabel set noarrow set nologscale set logscale x set set pointsize set encoding default set nopolar set noparametric set set set set surface set nocontour set clabel set mapping cartesian set nohidden3d set cntrparam order set cntrparam linear set cntrparam levels auto set cntrparam points set size set set xzeroaxis lt lw set x2zeroaxis lt lw set yzeroaxis lt lw set y2zeroaxis lt lw set tics in set ticslevel set tics set mxtics default set mytics default set mx2tics default set my2tics default set xtics border mirror norotate autofreq set ytics border mirror norotate autofreq set ztics border nomirror norotate autofreq set nox2tics set noy2tics set timestamp bottom norotate set rrange [*:*] noreverse nowriteback set trange [*:*] noreverse nowriteback set urange [*:*] noreverse nowriteback set vrange [*:*] noreverse nowriteback set xlabel matrix size set x2label set timefmt d m y n H
#define N
Definition: gksort.c:12
Vector & at(Key j)
Definition: VectorValues.h:139
NonlinearFactorGraph graph
double proxy(double x)
#define EXPECT_DOUBLES_EQUAL(expected, actual, threshold)
Definition: Test.h:161
VectorValues optimize(const Eliminate &function=EliminationTraitsType::DefaultEliminate) const
std::shared_ptr< GaussianFactorGraph > linearize(const Values &linearizationPoint) const
Linearize a nonlinear factor graph.
EIGEN_DEVICE_FUNC const ExpReturnType exp() const
const Vector k3Coefficients
Definition: testFourier.cpp:32
void add(const GaussianFactor &factor)
std::map< double, double > Sequence
Our sequence representation is a map of {x: y} values where y = f(x)
Definition: FitBasis.h:36
Eigen::VectorXd Vector
Definition: Vector.h:38
Factor for enforcing the scalar value of the polynomial BASIS representation at x is the same as the ...
Definition: BasisFactors.h:39
#define EXPECT(condition)
Definition: Test.h:150
Array< double, 1, 3 > e(1./3., 0.5, 2.)
std::shared_ptr< This > shared_ptr
shared_ptr to this class
std::shared_ptr< GaussianFactor > linearize(const Values &x) const override
traits
Definition: chartTesting.h:28
const double h
auto model
Definition: testFourier.cpp:28
#define EXPECT_CORRECT_FACTOR_JACOBIANS(factor, values, numerical_derivative_step, tolerance)
Check the Jacobians produced by a factor against finite differences.
static double TestFunction(double x)
Definition: testFourier.cpp:38
void insert(Key j, const Value &val)
Definition: Values.cpp:155
const double fx
#define X
Definition: icosphere.cpp:20
set noclip points set clip one set noclip two set bar set border lt lw set xdata set ydata set zdata set x2data set y2data set boxwidth set dummy x
Parameters parameters() const
Return Fourier coefficients.
Definition: FitBasis.h:96
Eigen::Matrix< double, Eigen::Dynamic, 1 > Vector
std::uint64_t Key
Integer nonlinear key type.
Definition: types.h:102
Fit a Basis using least-squares.


gtsam
Author(s):
autogenerated on Tue Jul 4 2023 02:38:08