Skip to content

Commit 4469e20

Browse files
authored
Merge pull request #3386 from stan-dev/fix/fwd-mode-jacobian
forward-mode jacobian aborts for mappings R^n -> R^m with m != n
2 parents 2be4d77 + f2f2b1c commit 4469e20

2 files changed

Lines changed: 77 additions & 2 deletions

File tree

stan/math/fwd/functor/jacobian.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ inline void jacobian(const F& f, const Eigen::Matrix<T, Eigen::Dynamic, 1>& x,
1414
using Eigen::Dynamic;
1515
using Eigen::Matrix;
1616
Matrix<fvar<T>, Dynamic, 1> x_fvar(x.size());
17-
J.resize(x_fvar.size(), x.size());
18-
fx.resize(x_fvar.size());
1917
for (int k = 0; k < x.size(); ++k) {
2018
x_fvar(k) = fvar<T>(x(k), 0);
2119
}
2220
x_fvar(0) = fvar<T>(x(0), 1);
2321
Matrix<fvar<T>, Dynamic, 1> fx_fvar = f(x_fvar);
22+
// size the outputs from the range of f, which is only known once f is applied
2423
fx = fx_fvar.val();
24+
J.resize(fx_fvar.size(), x.size());
2525
J.col(0) = fx_fvar.d();
2626
const fvar<T> switch_fvar(0, 1); // flips the tangents on and off
2727
for (int i = 1; i < x.size(); ++i) {
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include <stan/math/fwd.hpp>
2+
#include <gtest/gtest.h>
3+
#include <stdexcept>
4+
5+
using Eigen::Dynamic;
6+
using Eigen::Matrix;
7+
8+
// fun_2_3: R^2 --> R^3 | (x, y) --> [x, (x + y), (x * y)]
9+
struct fun_2_3 {
10+
template <typename T>
11+
inline Matrix<T, Dynamic, 1> operator()(
12+
const Matrix<T, Dynamic, 1>& x) const {
13+
Matrix<T, Dynamic, 1> z(3);
14+
z << x(0), x(0) + x(1), x(0) * x(1);
15+
return z;
16+
}
17+
};
18+
19+
// fun_3_2: R^3 --> R^2 | (x, y, z) --> [(x * y), (y + 2 * z)]
20+
struct fun_3_2 {
21+
template <typename T>
22+
inline Matrix<T, Dynamic, 1> operator()(
23+
const Matrix<T, Dynamic, 1>& x) const {
24+
Matrix<T, Dynamic, 1> z(2);
25+
z << x(0) * x(1), x(1) + 2.0 * x(2);
26+
return z;
27+
}
28+
};
29+
30+
TEST(FwdFunctor, jacobianMoreOutputsThanInputs) {
31+
fun_2_3 f;
32+
Matrix<double, Dynamic, 1> x(2);
33+
x << 1.5, 2.0;
34+
35+
Matrix<double, Dynamic, 1> fx;
36+
Matrix<double, Dynamic, Dynamic> J;
37+
stan::math::jacobian<double>(f, x, fx, J);
38+
39+
EXPECT_EQ(3, fx.size());
40+
EXPECT_FLOAT_EQ(x(0), fx(0));
41+
EXPECT_FLOAT_EQ(x(0) + x(1), fx(1));
42+
EXPECT_FLOAT_EQ(x(0) * x(1), fx(2));
43+
44+
EXPECT_EQ(3, J.rows());
45+
EXPECT_EQ(2, J.cols());
46+
EXPECT_FLOAT_EQ(1, J(0, 0));
47+
EXPECT_FLOAT_EQ(0, J(0, 1));
48+
EXPECT_FLOAT_EQ(1, J(1, 0));
49+
EXPECT_FLOAT_EQ(1, J(1, 1));
50+
EXPECT_FLOAT_EQ(x(1), J(2, 0));
51+
EXPECT_FLOAT_EQ(x(0), J(2, 1));
52+
}
53+
54+
TEST(FwdFunctor, jacobianFewerOutputsThanInputs) {
55+
fun_3_2 f;
56+
Matrix<double, Dynamic, 1> x(3);
57+
x << 1.5, 2.0, -3.0;
58+
59+
Matrix<double, Dynamic, 1> fx;
60+
Matrix<double, Dynamic, Dynamic> J;
61+
stan::math::jacobian<double>(f, x, fx, J);
62+
63+
EXPECT_EQ(2, fx.size());
64+
EXPECT_FLOAT_EQ(x(0) * x(1), fx(0));
65+
EXPECT_FLOAT_EQ(x(1) + 2.0 * x(2), fx(1));
66+
67+
EXPECT_EQ(2, J.rows());
68+
EXPECT_EQ(3, J.cols());
69+
EXPECT_FLOAT_EQ(x(1), J(0, 0));
70+
EXPECT_FLOAT_EQ(x(0), J(0, 1));
71+
EXPECT_FLOAT_EQ(0, J(0, 2));
72+
EXPECT_FLOAT_EQ(0, J(1, 0));
73+
EXPECT_FLOAT_EQ(1, J(1, 1));
74+
EXPECT_FLOAT_EQ(2, J(1, 2));
75+
}

0 commit comments

Comments
 (0)