22#include <Eigen/Eigenvalues>
44template <
typename Value,
typename Jacobian>
65template <MatrixExpression T>
66struct PlainResultOf<T>
68 using type =
typename T::PlainObject;
71template <
typename... Blocks>
72struct PlainResultOf<std::tuple<Blocks...>>
74 using type = std::tuple<typename PlainResultOf<std::remove_cvref_t<Blocks>>::type...>;
81using PlainResult =
typename PlainResultOf<T>::type;
89template <
typename F,
typename... Args>
90[[nodiscard]]
decltype(
auto)
91callEvaluated(F & f, Args &&... args)
93 using Returned = std::remove_cvref_t<decltype(f(std::forward<Args>(args)...))>;
94 if constexpr (MatrixExpression<Returned> && !std::same_as<PlainResult<Returned>, Returned>)
96 return PlainResult<Returned>{f(std::forward<Args>(args)...)};
100 return f(std::forward<Args>(args)...);
109template <
typename F,
typename Scalar,
typename... Args>
110concept TakesScalar =
requires(F & f, Args &&... args) { f.template operator()<Scalar>(std::forward<Args>(args)...); };
117template <
typename Scalar,
typename F>
123 template <
typename... Args>
124 requires(TakesScalar<F, Scalar, Args...> || std::invocable<F &, Args...>)
125 [[nodiscard]]
decltype(
auto)
126 operator()(Args &&... args)
const
128 if constexpr (TakesScalar<F, Scalar, Args...>)
130 return f.template operator()<Scalar>(std::forward<Args>(args)...);
134 return f(std::forward<Args>(args)...);
142template <
typename Scalar,
typename F,
typename... Args>
143[[nodiscard]]
decltype(
auto)
144callWithScalar(F & f, Args &&... args)
146 auto const supplied = WithScalar<Scalar, F>{f};
147 return callEvaluated(supplied, std::forward<Args>(args)...);
158template <
typename F,
typename In,
typename Out,
typename X,
typename... Rest>
159inline constexpr int functionForm = [] {
160 using G = WithScalar<typename X::Scalar, F const>
const &;
161 return std::invocable<G, In
const &, Out
const &, X
const &, Rest...> ? 3
162 : std::invocable<G, In
const &, X
const &, Rest...> ? 2
163 : std::invocable<G, X
const &, Rest...> ? 1
172template <
typename F,
typename In,
typename Out,
typename X,
typename... Rest>
173[[nodiscard]]
decltype(
auto)
174callForm(F
const & f, In
const & in, [[maybe_unused]] Out
const & out, X
const & x, Rest &&... rest)
176 constexpr int Form = functionForm<F, In, Out, X, Rest &&...>;
179 "a model's function takes h(x), h(in, x), or h(in, out, x), and a process model's f(x, dt), f(in, x, dt), or "
180 "f(in, out, x, dt), with in the input space, out the output space, and x a vector in the input space; a "
181 "batched function takes its output Y last; and each may take x's scalar as its one template parameter, "
182 "[]<typename T>(auto const & x)"
184 using Scalar =
typename X::Scalar;
185 if constexpr (Form == 3)
187 return callWithScalar<Scalar>(f, in, out, x, std::forward<Rest>(rest)...);
189 else if constexpr (Form == 2)
191 return callWithScalar<Scalar>(f, in, x, std::forward<Rest>(rest)...);
195 return callWithScalar<Scalar>(f, x, std::forward<Rest>(rest)...);
218template <
typename Value,
typename Jacobian>
220 internal::PlainResult<std::remove_cvref_t<Value>>,
221 internal::PlainResult<std::remove_cvref_t<Jacobian>>>
224 return {std::forward<Value>(value), std::forward<Jacobian>(jacobian)};
246template <
typename Scalar>
247inline constexpr bool takesDoubleConstants = std::same_as<Scalar, float> || std::same_as<Scalar, double>;
254template <
typename Scalar,
int Order>
255[[nodiscard]] std::pair<Eigen::Matrix<Scalar, Order, 1>, Eigen::Matrix<Scalar, Order, 1>>
258 using Vector = Eigen::Matrix<Scalar, Order, 1>;
259 using Matrix = Eigen::Matrix<Scalar, Order, Order>;
260 Matrix recursion = Matrix::Zero();
261 for (
int k = 1; k < Order; ++k)
263 recursion(k - 1, k) = Eigen::numext::sqrt(Scalar(k));
264 recursion(k, k - 1) = recursion(k - 1, k);
266 auto const solver = Eigen::SelfAdjointEigenSolver<Matrix>{recursion, Eigen::EigenvaluesOnly};
267 Vector
const nodes = solver.eigenvalues();
268 Scalar factorial = 1;
269 for (
int k = 2; k <= Order; ++k)
271 factorial *= Scalar(k);
273 auto weights = Vector{};
274 for (
int i = 0; i < Order; ++i)
278 Scalar current = nodes(i);
279 for (
int k = 1; k < Order - 1; ++k)
281 Scalar
const next = nodes(i) * current - Scalar(k) * previous;
285 Scalar
const value = Order == 1 ? Scalar(1) : current;
286 weights(i) = factorial / (Scalar(Order) * Scalar(Order) * value * value);
288 return {nodes, weights};
297template <
typename Scalar>
365template <
double Alpha = 1.0,
double Beta = 2.0,
double Kappa = 0.0>
368 static_assert(Alpha > 0,
"the unscented transform's alpha must be positive");
370 static constexpr double alpha = Alpha;
371 static constexpr double beta = Beta;
372 static constexpr double kappa = Kappa;
375 template <
typename Scalar>
379 return {Scalar(Alpha), Scalar(Beta), Scalar(Kappa)};
387template <
double Alpha = 1.0,
double Beta = 2.0,
double Kappa = 0.0>
388[[nodiscard]]
constexpr Unscented<Alpha, Beta, Kappa>
412 template <
typename Scalar>
416 return {Scalar(1), Scalar(0), Scalar(3) - Scalar(n)};
423[[nodiscard]]
constexpr FourthMomentUnscented
444 [[nodiscard]]
static constexpr Eigen::Index
456 template <
typename Po
ints,
typename Weights>
458 unitPoints(Eigen::Index n, Points & points, Weights & weights)
460 using Scalar =
typename Points::Scalar;
461 constexpr int N = Points::RowsAtCompileTime;
463 if constexpr (N != Eigen::Dynamic && N > 0 && internal::takesDoubleConstants<Scalar>)
465 constexpr Scalar radius = Scalar(internal::compileTimeSqrt(
double(N)));
466 points.diagonal().setConstant(radius);
467 points.rightCols(N).diagonal().setConstant(-radius);
468 weights.setConstant(Scalar(1.0 / (2.0 * N)));
472 Scalar
const radius = Eigen::numext::sqrt(Scalar(n));
473 for (Eigen::Index i = 0; i < n; ++i)
475 points(i, i) = radius;
476 points(i, n + i) = -radius;
478 weights.setConstant(Scalar(1) / Scalar(2 * n));
487[[nodiscard]]
constexpr Cubature
510template <
double CenterWeight = 0.0>
513 static_assert(CenterWeight >= 0 && CenterWeight < 1,
"a spherical simplex's center weight must be in [0, 1)");
518 [[nodiscard]]
static constexpr Eigen::Index
532 template <
typename Po
ints,
typename Weights>
534 unitPoints(Eigen::Index n, Points & points, Weights & weights)
536 using Scalar =
typename Points::Scalar;
538 Scalar
const center = Scalar(CenterWeight);
539 Scalar
const pointWeight = (Scalar(1) - center) / Scalar(n + 1);
541 weights.tail(n + 1).setConstant(pointWeight);
542 for (Eigen::Index j = 1; j <= n; ++j)
544 Scalar
const step = Scalar(1) / Eigen::numext::sqrt(Scalar(j * (j + 1)) * pointWeight);
545 points.row(j - 1).segment(1, j).setConstant(-step);
546 points(j - 1, j + 1) = Scalar(j) * step;
556template <
double CenterWeight = 0.0>
557[[nodiscard]]
constexpr SphericalSimplex<CenterWeight>
583template <
int Order = 3>
586 static_assert(Order >= 1,
"a Gauss-Hermite rule has at least one point per coordinate");
592 [[nodiscard]]
static constexpr Eigen::Index
595 Eigen::Index count = 1;
596 for (Eigen::Index i = 0; i < n && count <=
MaxPointCount; ++i)
610 template <
typename Po
ints,
typename Weights>
612 unitPoints(Eigen::Index n, Points & points, Weights & weights)
614 using Scalar =
typename Points::Scalar;
615 Eigen::Index
const count = weights.size();
617 count <=
MaxPointCount &&
"a Gauss-Hermite rule over this many coordinates has too many points"
619 auto const [nodes, nodeWeights] = internal::hermiteRule<Scalar, Order>();
620 for (Eigen::Index k = 0; k < count; ++k)
622 Eigen::Index digits = k;
624 for (Eigen::Index i = 0; i < n; ++i)
626 Eigen::Index
const digit = digits % Order;
628 points(i, k) = nodes(digit);
629 weight *= nodeWeights(digit);
633 Eigen::Index reference = 0;
634 weights.maxCoeff(&reference);
642template <
int Order = 3>
643[[nodiscard]]
constexpr GaussHermite<Order>
675 { T::template params<double>(n) } -> std::same_as<UnscentedParams<double>>;
703concept PointSet =
requires(Eigen::Index n, Eigen::MatrixXd & points, Eigen::VectorXd & weights) {
704 { T::pointCount(n) } -> std::convertible_to<Eigen::Index>;
705 { T::unitPoints(n, points, weights) } -> std::same_as<Eigen::Index>;
729template <
typename Rule,
typename A>
734 "spread() takes a sigma-point rule, an UnscentedRule or a PointSet, not linearized(), iterated(), or a "
735 "rule spread already"
737 static_assert(
Axes<A>,
"spread() takes axes, such as roots::principal(), after its rule");
748template <
typename Rule,
typename A>
749[[nodiscard]]
constexpr Spread<Rule, A>
752 return {std::move(rule), std::move(axes)};
764inline constexpr bool isPointSet = PointSet<T>;
770inline constexpr bool isUnscented = UnscentedRule<T>;
774inline constexpr bool isSpread =
false;
777template <
typename Rule,
typename A>
778inline constexpr bool isSpread<methods::Spread<Rule, A>>{
true};
787inline constexpr bool isSigmaPointMethod = isUnscented<T> || isPointSet<T> || isSpread<T>;
792template <
typename Method>
793[[nodiscard]]
constexpr decltype(
auto)
794ruleOf(Method
const & method)
796 if constexpr (isSpread<Method>)
798 return (method.rule);
809template <
typename Method>
810[[nodiscard]]
constexpr decltype(
auto)
811axesOf(Method
const & method)
813 if constexpr (isSpread<Method>)
815 return (method.axes);
824template <
typename Method>
825using RuleOf = std::remove_cvref_t<decltype(ruleOf(std::declval<Method const &>()))>;
828template <
typename Method>
829using AxesOf = std::remove_cvref_t<decltype(axesOf(std::declval<Method const &>()))>;
840 "a transform returns no std::expected, so its axes cannot fail: append roots::pivoted() to the list, or take "
841 "the root with roots::squareRoot(), which returns one"
848template <
typename Result>
849[[nodiscard]]
decltype(
auto)
850rootValue(Result && result)
852 if constexpr (
requires { result.has_value(); })
854 EMRYS_FILTER_ASSERT(result.has_value() &&
"a transform's axes cannot fail");
855 return *std::forward<Result>(result);
859 return std::forward<Result>(result);
867inline constexpr bool isLinearMethod =
false;
871inline constexpr bool isLinearMethod<methods::Linearized> =
true;
884template <RealScalar Scalar =
double>
960template <
typename Method = Linearized, RealScalar Scalar =
double>
964 internal::isLinearMethod<Method> || internal::isSigmaPointMethod<Method>,
965 "Iterated relinearizes Linearized or a sigma-point method (an UnscentedRule such as Unscented, or a PointSet "
966 "such as Cubature, SphericalSimplex, GaussHermite, or the user's own) (decisions.md [iterated-update], "
967 "[sigma-point-sets])"
983template <RealScalar Scalar =
double,
typename Method = Linearized>
987 EMRYS_FILTER_ASSERT(options.
tolerance >= Scalar(0) &&
"an iteration's tolerance must not be negative");
988 EMRYS_FILTER_ASSERT(options.
maxIterations >= 1 &&
"an Iterated method makes at least one linearization");
999inline constexpr bool isIterated =
false;
1002template <
typename Method,
typename Scalar>
1003inline constexpr bool isIterated<methods::Iterated<Method, Scalar>>{
true};
1018template <
typename M>
1020 internal::isLinearMethod<std::remove_cvref_t<M>> || internal::isSigmaPointMethod<std::remove_cvref_t<M>>
1021 || internal::isIterated<std::remove_cvref_t<M>>;
How a function that stores a matrix argument takes it (decisions.md [forwarded-matrices]): the concep...
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
constexpr int MaxPointCount
The most points a PointSet may have over a fixed number of coordinates, which bounds the compile-time...
Definition Methods.h:231
Square roots of a covariance (decisions.md [sigma-point-axes]): the Axes and Repair concepts that a r...
Tag paths and the index utilities (decisions.md [tag-addressing], [index-names]): TagPath and TagPath...
Axes: a way of taking a root of a covariance , , whose columns carry a sigma-point rule's unit point...
Definition Roots.h:300
A set of sigma points other than Unscented's: a method that evaluates a function at weighted points g...
Definition Methods.h:703
A rule for the scaled unscented transform's parameters by the number of coordinates it spreads its po...
Definition Methods.h:674
WithFallbacks< Cholesky, Pivoted > DefaultAxes
The axes every root the library takes uses unless told otherwise, cholesky(pivoted()): the Cholesky f...
Definition Roots.h:1254
The transform methods and the sigma-point rules, each made by its factory (decisions....
Definition Methods.h:312
The run-time settings of an Iterated method, given to methods::iterated() as iterated(method,...
Definition Methods.h:886
std::size_t maxIterations
the most linearizations it makes, at least 1
Definition Methods.h:888
Scalar tolerance
the largest step that stops the iteration, in prior standard deviations
Definition Methods.h:887
The parameters of the scaled unscented transform over a number of coordinates, in Scalar,...
Definition Methods.h:299
Scalar kappa
the secondary scaling
Definition Methods.h:302
Scalar beta
the center point's extra covariance weight
Definition Methods.h:301
Scalar alpha
the spread of the points about the mean
Definition Methods.h:300
What a function returns when it knows its Jacobian: its value, and the Jacobian of its output differe...
Definition Methods.h:46
Jacobian jacobian
its Jacobian, one matrix or a tuple of blocks
Definition Methods.h:48
Value value
the function's value
Definition Methods.h:47
Cubature method: the points over the joint input space of dimension , for each column of a root of...
Definition Methods.h:442
static constexpr Eigen::Index pointCount(Eigen::Index n)
Definition Methods.h:445
static Eigen::Index unitPoints(Eigen::Index n, Points &points, Weights &weights)
Fills points, , with , the plus points first, and weights with each, over coordinates; over a fixed...
Definition Methods.h:458
Unscented method with , , and over coordinates, so that : the points sit at standard deviations al...
Definition Methods.h:410
static constexpr UnscentedParams< Scalar > params(Eigen::Index n)
Definition Methods.h:414
Gauss-Hermite method: the product rule of Order points per coordinate over the joint input space of d...
Definition Methods.h:585
static constexpr Eigen::Index pointCount(Eigen::Index n)
Definition Methods.h:593
static Eigen::Index unitPoints(Eigen::Index n, Points &points, Weights &weights)
Fills points, , with every combination of the one-dimensional points of internal::hermiteRule(),...
Definition Methods.h:612
static constexpr int PointsPerCoordinate
Order
Definition Methods.h:587
Iterated method, for a measurement update: the update of Method relinearized with respect to its own ...
Definition Methods.h:962
std::size_t maxIterations
the most linearizations it makes, at least 1
Definition Methods.h:972
Method method
the method relinearized at each iterate
Definition Methods.h:970
Scalar tolerance
the largest step that stops it, in prior standard deviations
Definition Methods.h:971
Linearized method: and , with the Jacobians of f at the input means.
Definition Methods.h:327
Spherical simplex method: points over the joint input space of dimension , the mean with weight cent...
Definition Methods.h:512
static constexpr Eigen::Index pointCount(Eigen::Index n)
Definition Methods.h:519
static Eigen::Index unitPoints(Eigen::Index n, Points &points, Weights &weights)
Fills points, , with the center, then the points of the recursion of Julier and Uhlmann (2004),...
Definition Methods.h:534
static constexpr double centerWeight
CenterWeight
Definition Methods.h:515
A sigma-point rule spread by axes of its own (decisions.md [sigma-point-axes]): Rule's unit points an...
Definition Methods.h:731
A axes
the axes they are spread along
Definition Methods.h:740
Rule rule
the unit points and weights
Definition Methods.h:739
Unscented method: sigma points over the joint input space of dimension , spread by .
Definition Methods.h:367
static constexpr double kappa
Kappa
Definition Methods.h:372
static constexpr UnscentedParams< Scalar > params(Eigen::Index)
Definition Methods.h:377
static constexpr double alpha
Alpha
Definition Methods.h:370
static constexpr double beta
Beta
Definition Methods.h:371