Skip to content

Commit e32ccdf

Browse files
committed
IntegrationMatrix
1 parent 9d79215 commit e32ccdf

File tree

3 files changed

+75
-21
lines changed

3 files changed

+75
-21
lines changed

gtsam/basis/Chebyshev2.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,4 +253,28 @@ Weights Chebyshev2::IntegrationWeights(size_t N) {
253253
Weights Chebyshev2::IntegrationWeights(size_t N, double a, double b) {
254254
return IntegrationWeights(N) * (b - a) / 2.0;
255255
}
256+
257+
Matrix Chebyshev2::IntegrationMatrix(size_t N) {
258+
// Obtain the differentiation matrix.
259+
Matrix D = DifferentiationMatrix(N);
260+
261+
// We want f = D * F, where F is the anti-derivative of f.
262+
// However, D is singular, so we enforce F(0) = f(0) by modifying its first row.
263+
D.row(0).setZero();
264+
D(0, 0) = 1.0;
265+
266+
// Now D is invertible; its inverse is the integration operator.
267+
return D.inverse();
268+
}
269+
270+
/**
271+
* Create vector of values at Chebyshev points given scalar-valued function.
272+
*/
273+
Vector Chebyshev2::vector(std::function<double(double)> f, size_t N, double a, double b) {
274+
Vector fvals(N);
275+
const Vector points = Points(N, a, b);
276+
for (size_t j = 0; j < N; j++) fvals(j) = f(points(j));
277+
return fvals;
278+
}
279+
256280
} // namespace gtsam

gtsam/basis/Chebyshev2.h

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -105,22 +105,28 @@ class GTSAM_EXPORT Chebyshev2 : public Basis<Chebyshev2> {
105105
/**
106106
* Evaluate Clenshaw-Curtis integration weights.
107107
* Trefethen00book, pg 128, clencurt.m
108+
* Note that N in clencurt.m is 1 less than our N
108109
*/
109110
static Weights IntegrationWeights(size_t N);
110111

111112
/// Evaluate Clenshaw-Curtis integration weights, for interval [a,b]
112113
static Weights IntegrationWeights(size_t N, double a, double b);
113114

114-
/**
115-
* Create matrix of values at Chebyshev points given vector-valued function.
116-
*/
115+
/// IntegrationMatrix returns the (N+1)×(N+1) matrix P such that for any f,
116+
/// F = P * f recovers F (the antiderivative) satisfying f = D * F and F(0)=0.
117+
static Matrix IntegrationMatrix(size_t N);
118+
119+
/// Create matrix of values at Chebyshev points given vector-valued function.
120+
static Vector vector(std::function<double(double)> f,
121+
size_t N, double a = -1, double b = 1);
122+
123+
/// Create matrix of values at Chebyshev points given vector-valued function.
117124
template <size_t M>
118125
static Matrix matrix(std::function<Eigen::Matrix<double, M, 1>(double)> f,
119-
size_t N, double a = -1, double b = 1) {
126+
size_t N, double a = -1, double b = 1) {
120127
Matrix Xmat(M, N);
121-
for (size_t j = 0; j < N; j++) {
122-
Xmat.col(j) = f(Point(N, j, a, b));
123-
}
128+
const Vector points = Points(N, a, b);
129+
for (size_t j = 0; j < N; j++) Xmat.col(j) = f(points(j));
124130
return Xmat;
125131
}
126132
}; // \ Chebyshev2

gtsam/basis/tests/testChebyshev2.cpp

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -247,13 +247,6 @@ double f(double x) {
247247
return 3.0 * pow(x, 3) - 2.0 * pow(x, 2) + 5.0 * x - 11;
248248
}
249249

250-
Eigen::Matrix<double, -1, 1> calculateFvals(size_t N, double a = -1., double b = 1.) {
251-
const Vector xs = Chebyshev2::Points(N, a, b);
252-
Vector fvals(N);
253-
std::transform(xs.data(), xs.data() + N, fvals.data(), f);
254-
return fvals;
255-
}
256-
257250
// its derivative
258251
double fprime(double x) {
259252
// return 9*(x**2) - 4*(x) + 5
@@ -262,7 +255,7 @@ double fprime(double x) {
262255

263256
//******************************************************************************
264257
TEST(Chebyshev2, CalculateWeights) {
265-
Vector fvals = calculateFvals(N);
258+
Vector fvals = Chebyshev2::vector(f, N);
266259
double x1 = 0.7, x2 = -0.376;
267260
Weights weights1 = Chebyshev2::CalculateWeights(N, x1);
268261
Weights weights2 = Chebyshev2::CalculateWeights(N, x2);
@@ -272,7 +265,7 @@ Vector fvals = calculateFvals(N);
272265

273266
TEST(Chebyshev2, CalculateWeights2) {
274267
double a = 0, b = 10, x1 = 7, x2 = 4.12;
275-
Vector fvals = calculateFvals(N, a, b);
268+
Vector fvals = Chebyshev2::vector(f, N, a, b);
276269

277270
Weights weights1 = Chebyshev2::CalculateWeights(N, x1, a, b);
278271
EXPECT_DOUBLES_EQUAL(f(x1), weights1 * fvals, 1e-8);
@@ -298,22 +291,22 @@ TEST(Chebyshev2, CalculateWeights_CoincidingPoint) {
298291
}
299292

300293
TEST(Chebyshev2, DerivativeWeights) {
301-
Vector fvals = calculateFvals(N);
294+
Vector fvals = Chebyshev2::vector(f, N);
302295
std::vector<double> testPoints = {0.7, -0.376, 0.0};
303296
for (double x : testPoints) {
304297
Weights dWeights = Chebyshev2::DerivativeWeights(N, x);
305298
EXPECT_DOUBLES_EQUAL(fprime(x), dWeights * fvals, 1e-9);
306299
}
307300

308-
// test if derivative calculation at cheb point is correct
301+
// test if derivative calculation at Chebyshev point is correct
309302
double x4 = Chebyshev2::Point(N, 3);
310303
Weights dWeights4 = Chebyshev2::DerivativeWeights(N, x4);
311304
EXPECT_DOUBLES_EQUAL(fprime(x4), dWeights4 * fvals, 1e-9);
312305
}
313306

314307
TEST(Chebyshev2, DerivativeWeights2) {
315308
double x1 = 5, x2 = 4.12, a = 0, b = 10;
316-
Vector fvals = calculateFvals(N, a, b);
309+
Vector fvals = Chebyshev2::vector(f, N, a, b);
317310

318311
Weights dWeights1 = Chebyshev2::DerivativeWeights(N, x1, a, b);
319312
EXPECT_DOUBLES_EQUAL(fprime(x1), dWeights1 * fvals, 1e-8);
@@ -370,9 +363,8 @@ double proxy3(double x) {
370363
return Chebyshev2::EvaluationFunctor(6, x)(f3_at_6points);
371364
}
372365

366+
// Check Derivative evaluation at point x=0.2
373367
TEST(Chebyshev2, Derivative6) {
374-
// Check Derivative evaluation at point x=0.2
375-
376368
// calculate expected values by numerical derivative of synthesis
377369
const double x = 0.2;
378370
Matrix numeric_dTdx = numericalDerivative11<double, double>(proxy3, x);
@@ -496,6 +488,38 @@ TEST(Chebyshev2, IntegralWeights) {
496488
EXPECT(assert_equal(expected2, actual2));
497489
}
498490

491+
//******************************************************************************
492+
TEST(Chebyshev2, IntegrationMatrixOperator) {
493+
const int N = 10; // number of intervals => N+1 nodes
494+
const double a = -1.0, b = 1.0;
495+
496+
// Create integration matrix
497+
Matrix P = Chebyshev2::IntegrationMatrix(N);
498+
499+
// Get values of the derivative (fprime) at the Chebyshev nodes
500+
Vector ff = Chebyshev2::vector(fprime, N, a, b);
501+
502+
// Integrate to get back f, using the integration matrix
503+
Vector F_est = P * ff;
504+
505+
// Assert that the first value is ff(0)
506+
EXPECT_DOUBLES_EQUAL(ff(0), F_est(0), 1e-9);
507+
508+
// For comparison, get actual function values at the nodes
509+
Vector F_true = Chebyshev2::vector(f, N, a, b);
510+
511+
// Verify the integration matrix worked correctly
512+
F_est.array() += F_true(0) - F_est(0);
513+
EXPECT(assert_equal(F_true, F_est, 1e-9));
514+
515+
// Differentiate the result to get back to our derivative function
516+
Matrix D = Chebyshev2::DifferentiationMatrix(N);
517+
Vector ff_est = D * F_est;
518+
519+
// Verify the round trip worked
520+
EXPECT(assert_equal(ff, ff_est, 1e-9));
521+
}
522+
499523
//******************************************************************************
500524
int main() {
501525
TestResult tr;

0 commit comments

Comments
 (0)