54template <
typename Space>
60 explicit RowCoverage(Space
const & space)
66 push_back(BlockRows
const & rows)
68 if (rows.start < 0 || rows.size < 0 || rows.start + rows.size > m_counts.size())
74 m_counts.segment(rows.start, rows.size) += 1;
82 return m_inRange && (m_counts == 1).all();
88 using CountsType = Eigen::
92 bool m_inRange =
true;
99enum class StaticPartition
109template <
typename Space,
typename... Lists>
113 auto counts = std::array<int, std::size_t(Space::FixedValueDimension)>{};
114 [[maybe_unused]]
auto const count = [&]<
typename... Paths>(TagPaths<Paths...> *) {
117 constexpr ResolvedIndex rows = ResolvePath<Space, Paths>::value;
118 for (
int i = rows.offset; i < rows.offset + rows.size; ++i)
120 ++counts[std::size_t(i)];
126 (count(
static_cast<Lists *
>(
nullptr)), ...);
127 for (
int const owners : counts)
140template <
typename Model,
typename Space>
141consteval StaticPartition
144 if constexpr (
requires { Model::template staticPartition<Space>(); })
146 return Model::template staticPartition<Space>();
150 return StaticPartition::unknown;
158template <
typename Model,
typename Space>
161 [[maybe_unused]] Model
const & model,
162 [[maybe_unused]] Space
const & space,
163 [[maybe_unused]]
char const * message =
"the process model must write every coordinate of the state space once"
166 constexpr StaticPartition known = staticPartition<Model, Space>();
168 known != StaticPartition::fails,
169 "the process model must write every coordinate of the state space exactly once: a block it writes twice, or "
170 "one no model writes, is not a partition"
172 if constexpr (known == StaticPartition::unknown)
174 EMRYS_FILTER_ASSERT(model.partitions(space) && message);
181template <
typename Space,
typename Paths>
182consteval StaticPartition
183staticPartitionOfWrites()
185 if constexpr (Space::ValueDimensionStep == 0)
187 return pathsPartition<Space, Paths>() ? StaticPartition::holds : StaticPartition::fails;
191 return StaticPartition::unknown;
196template <
typename Space,
typename... Paths>
198writesPartition(Space
const & space, TagPaths<Paths...> *)
200 auto coverage = RowCoverage<Space>{space};
201 (coverage.push_back(blockRows<Paths>(space)), ...);
202 return coverage.partitioned();
210template <
typename... Paths>
211struct PathsOf<TagPaths<Paths...>>
213 using type = std::tuple<Paths...>;
221template <
typename Model>
222inline constexpr bool writesWholeState = !Model::template has<PolicyKind::writes>;
225template <
typename Model>
226using WritePathsOf =
typename Model::template Policy<PolicyKind::writes>::PathList;
231template <
typename Model>
232using OutputSpaceOf =
typename Model::template Policy<PolicyKind::writes>::OutputSpace;
235template <
typename Model, std::
size_t K>
236using WritePathAt = std::tuple_element_t<K, typename PathsOf<WritePathsOf<Model>>::type>;
239template <
typename Model, std::
size_t K>
240using WriteNameAt = std::tuple_element_t<K, CompositeBlocks<OutputSpaceOf<Model>>>;
243template <
typename Model>
244inline constexpr std::size_t writeCount = std::tuple_size_v<typename PathsOf<WritePathsOf<Model>>::type>;
249template <
typename Model,
typename Space>
250using OutputSpaceIn =
typename decltype([] {
251 if constexpr (writesWholeState<Model>)
253 return std::type_identity<Space>{};
257 return std::type_identity<OutputSpaceOf<Model>>{};
265template <
typename Model,
typename Space>
266[[nodiscard]] OutputSpaceIn<Model, Space>
267outputOf(Space
const & space)
269 if constexpr (writesWholeState<Model>)
275 return [&]<std::size_t... K>(std::index_sequence<K...>) {
276 return OutputSpaceOf<Model>{nameOf<WriteNameAt<Model, K>>(pathBlock<WritePathAt<Model, K>>(space))...};
277 }(std::make_index_sequence<writeCount<Model>>{});
285template <
typename Target,
typename Source>
287renamedInstance(Source
const & source)
289 if constexpr (std::is_same_v<Target, Source>)
295 return []<
typename... Names,
typename... Blocks>(
297 std::tuple<Names...> *,
298 std::tuple<Blocks...> *
300 return Target{nameOf<Names>(pathBlock<Blocks>(from))...};
301 }(source,
static_cast<CompositeBlocks<Target> *
>(
nullptr),
static_cast<CompositeBlocks<Source> *
>(
nullptr));
307inline constexpr bool isOnPath =
false;
310template <
typename X,
typename Path>
311inline constexpr bool isOnPath<OnPath<X, Path>>{
true};
318template <
typename Matrix,
typename Value,
typename Scalar>
320addSpectralDensity(Matrix & q, BlockRows
const & rows, Value
const & value, Scalar dt)
322 if constexpr (MatrixExpression<Value>)
325 value.rows() == rows.size && value.cols() == rows.size
326 &&
"a spectral density matrix is over the block's differences"
328 q.block(rows.start, rows.start, rows.size, rows.size) += dt * value.template cast<Scalar>();
332 q.block(rows.start, rows.start, rows.size, rows.size).diagonal().array() += dt * Scalar(value);
340template <
typename Out,
typename Matrix,
typename Part,
typename Scalar>
342addContinuousPart(Out
const & out, Matrix & q, Part
const & part, Scalar dt)
344 if constexpr (isOnPath<Part>)
346 [&]<
typename X,
typename Path>(OnPath<X, Path>
const *) {
347 addSpectralDensity(q, differenceBlockRows<Path>(out), part.value, dt);
348 }(
static_cast<Part
const *
>(
nullptr));
350 else if constexpr (isPlacedByPath<Part>)
353 std::is_void_v<Part>,
354 "continuousNoise() takes no onEach<Path>(q); place the model on each repetition with onEach<Path>(model), "
356 "give it continuousNoise(q) for one repetition"
372template <
typename Set>
373[[nodiscard]]
consteval bool
374checkProcessPolicies()
377 Set::template has<PolicyKind::reads> || !Set::template has<PolicyKind::writes>,
378 "a process model that writes blocks with writes() says what it reads with reads(); give neither to read and "
379 "write the whole state"
382 Set::template has<PolicyKind::writes> || !Set::template has<PolicyKind::reads>,
383 "a process model that reads blocks with reads() says which blocks of the state it writes with writes(); give "
384 "neither to read and write the whole state"
387 Set::template has<PolicyKind::reads> || !Set::template has<PolicyKind::affineIn>,
388 "affineIn() names items of reads(), and a model without reads() reads the whole state under no names"
391 !Set::template has<PolicyKind::logLikelihood>,
392 "logLikelihood(g) is a measurement model's; a process model has no observation to weight points by"
395 !Set::template has<PolicyKind::sample>,
396 "sample(g) is a measurement model's; a process model's draw is its step and its noise"
399 !Set::template has<PolicyKind::outputSpace>,
400 "a process model's output is the blocks of the state it writes, whose spaces the state has, so it takes no "
404 !(Set::template has<PolicyKind::continuousNoise> && Set::template has<PolicyKind::additiveNoise>),
405 "a process model's noise is continuousNoise(), discretized over the step, or additiveNoise(), a fixed Q, not "
415template <
typename Matrix>
420 static constexpr bool IsAffine =
true;
423 explicit LinearStep(Matrix matrix)
424 : m_matrix{std::move(matrix)}
428 [[nodiscard]] Matrix
const &
437 template <
typename In,
typename Out,
typename X,
typename TimeScalar>
439 operator()([[maybe_unused]] In
const & in, Out
const &, X
const & x, TimeScalar)
const
443 "a matrix process model moves a state whose coordinates add their values; a state with a block such as a "
444 "unit quaternion takes a function"
450 "a matrix process model's matrix is square, with a row and a column per coordinate of the state"
453 m_matrix.rows() == x.size() && m_matrix.cols() == x.size()
454 &&
"a matrix process model's matrix is square, with a row and a column per coordinate of the state"
456 using T =
typename X::Scalar;
457 using Value = Eigen::Matrix<T, Matrix::RowsAtCompileTime, 1, Eigen::ColMajor, Matrix::MaxRowsAtCompileTime, 1>;
458 Value value = m_matrix.template cast<T>() * x;
459 return WithJacobian<Value, Matrix const &>{std::move(value), m_matrix};
471template <
bool Whole,
typename Writes>
481template <
typename Writes>
482struct ProcessWrites<true, Writes>
485 using Paths = TagPaths<WholeSpace>;
487 using OutputSpace = WholeSpace;
516template <
typename F,
typename... Policies>
520 using Set = internal::PolicySet<Policies...>;
531 template <
internal::PolicyKind K>
535 template <
internal::PolicyKind K>
536 using Policy =
typename Set::template Type<K>;
545 using WritePaths =
typename internal::ProcessWrites<Whole, Policy<internal::PolicyKind::writes>>::Paths;
555 , m_policies{std::move(
policies)...}
559 [[nodiscard]] F
const &
566 template <
internal::PolicyKind K>
570 return Set::template get<K>(m_policies);
574 [[nodiscard]] std::tuple<Policies...>
const &
584 template <
typename Scalar,
typename Out>
586 noiseOver(Out
const & out, [[maybe_unused]] Scalar dt)
const
590 Matrix q = Matrix::Zero(n, n);
602 [&](
auto const &... parts) {
603 (internal::addContinuousPart(out, q, parts, dt), ...);
611 using Placed = std::remove_cvref_t<
decltype(placed)>;
613 !internal::isGaussianMixture<typename Placed::Distribution>,
614 "a process model's additive noise is one Gaussian; a mixture over the step is a mixture argument of "
615 "predict() (roadmap 17z)"
617 auto const & distribution = placed.distribution();
618 q = internal::symmetricFromLower<Matrix>(distribution.covarianceLower().template cast<Scalar>());
626 template <
typename Space>
627 [[nodiscard]]
static consteval internal::StaticPartition
632 return internal::StaticPartition::holds;
636 return internal::staticPartitionOfWrites<Space, WritePaths>();
643 template <
typename Space>
653 return internal::writesPartition(space,
static_cast<WritePaths *
>(
nullptr));
660 std::tuple<Policies...> m_policies;
699template <
typename F,
typename... Policies>
700 requires(!MatrixExpression<F> && !internal::ModelPolicy<F>)
704 using Set = internal::PolicySet<std::remove_cvref_t<Policies>...>;
705 static_assert(internal::checkProcessPolicies<Set>());
707 std::forward<F>(function),
708 std::forward<Policies>(policies)...
724template <MatrixExpression Matrix,
typename... Policies>
728 using Plain = internal::PlainResult<std::remove_cvref_t<Matrix>>;
729 using Set = internal::PolicySet<std::remove_cvref_t<Policies>...>;
731 !Set::template has<internal::PolicyKind::reads> && !Set::template has<internal::PolicyKind::writes>,
732 "a matrix process model is over the whole state, so it takes no reads() or writes(); give a function for a "
733 "model of some blocks"
735 static_assert(internal::checkProcessPolicies<Set>());
737 internal::LinearStep<Plain>{Plain{std::forward<Matrix>(matrix)}},
738 std::forward<Policies>(policies)...
746template <
typename F,
typename... Policies>
748processFunctionBase(ProcessFunction<F, Policies...>
const *);
752processFunctionBase(
void const *);
755template <
typename Path>
756using ReadsOne = Reads<ReadItem<AsTagPath<Path>, PathTag<Path>>>;
759template <
typename... Paths>
760using ReadsOf = Reads<ReadItem<AsTagPath<Paths>, PathTag<Paths>>...>;
763template <
typename... Paths>
764using WritesOf = Writes<AsTagPath<Paths>...>;
772 template <
typename In,
typename Out,
typename X,
typename TimeScalar>
774 operator()(In
const &, Out
const & out, X
const & x, TimeScalar)
const
784template <
int Dimension,
typename Scalar>
785struct RandomWalkNoise
787 Eigen::Matrix<Scalar, Dimension, Dimension> spectralDensity;
790 template <
typename Out,
typename TimeScalar>
792 operator()(Out
const &, TimeScalar dt)
const
799[[nodiscard]]
constexpr int
802 return n <= 1 ? 1 : n * factorial(n - 1);
808template <
int N,
typename TimeScalar>
809[[nodiscard]] std::array<TimeScalar, N>
810powersOf(TimeScalar dt)
812 auto result = std::array<TimeScalar, N>{};
813 result[0] = TimeScalar(1);
814 for (
int k = 1; k < N; ++k)
816 result[k] = result[k - 1] * dt;
825template <
int Dimension,
typename... Tags>
828 static constexpr int Order = int(
sizeof...(Tags));
831 template <
typename In,
typename Out,
typename X,
typename TimeScalar>
833 operator()(In
const & in, Out
const & out, X
const & x, TimeScalar dt)
const
838 auto const starts = std::array<Eigen::Index, Order>{blockRows<Tags>(in).start...};
839 auto const differenceStarts = std::array<Eigen::Index, Order>{differenceBlockRows<Tags>(out).start...};
840 auto const dtPowers = powersOf<Order>(dt);
843 JacobianMatrix jacobian = JacobianMatrix::Identity(n, n);
844 for (
int i = 0; i < Order; ++i)
846 for (
int j = i + 1; j < Order; ++j)
848 TimeScalar
const scale = dtPowers[j - i] / TimeScalar(factorial(j - i));
849 y.template segment<Dimension>(starts[i]) += T(scale) * x.template segment<Dimension>(starts[j]);
863template <
int Dimension,
typename Scalar,
typename... Tags>
866 static constexpr int Order = int(
sizeof...(Tags));
868 Eigen::Matrix<Scalar, Dimension, Dimension> spectralDensity;
873 [[nodiscard]]
static constexpr int
874 denominator(
int i,
int j)
876 return (2 * Order - 1 - i - j) * factorial(Order - 1 - i) * factorial(Order - 1 - j);
880 template <
typename Out,
typename TimeScalar>
882 operator()(Out
const & out, TimeScalar dt)
const
885 auto const starts = std::array<Eigen::Index, Order>{differenceBlockRows<Tags>(out).start...};
886 auto const dtPowers = powersOf<2 * Order>(dt);
888 Matrix q = Matrix::Zero(n, n);
889 for (
int i = 0; i < Order; ++i)
891 for (
int j = 0; j < Order; ++j)
893 TimeScalar
const scale = dtPowers[2 * Order - 1 - i - j] / TimeScalar(denominator(i, j));
895 scale * spectralDensity.template cast<TimeScalar>();
905template <
typename Scalar>
906struct OrnsteinUhlenbeckStep
911 template <
typename TimeScalar>
912 [[nodiscard]] TimeScalar
913 decay(TimeScalar dt)
const
915 return Eigen::numext::exp(TimeScalar(-dt / TimeScalar(timeConstant)));
919 template <
typename In,
typename Out,
typename X,
typename TimeScalar>
921 operator()(In
const &, Out
const & out, X
const & x, TimeScalar dt)
const
925 TimeScalar
const phi = decay(dt);
929 JacobianMatrix{phi * JacobianMatrix::Identity(n, n)}
937template <
typename Scalar>
938struct OrnsteinUhlenbeckNoise
944 template <
typename Out,
typename TimeScalar>
946 operator()(Out
const & out, TimeScalar dt)
const
949 TimeScalar
const phi = Eigen::numext::exp(TimeScalar(-dt / TimeScalar(timeConstant)));
951 return Matrix{TimeScalar(variance) * (1 - phi * phi) * Matrix::Identity(n, n)};
971template <
typename Path, RealScalar Scalar_ =
double>
974 internal::IdentityStep,
975 internal::ReadsOne<Path>,
976 internal::WritesOf<Path>,
977 AffineIn<internal::PathTag<Path>>,
978 internal::DiscreteNoise<internal::RandomWalkNoise<
979 internal::DifferenceSpaceOf<internal::PathTag<Path>>::type::FixedValueDimension,
983 using Differences =
typename internal::DifferenceSpaceOf<internal::PathTag<Path>>::type;
985 static_assert(Differences::ValueDimensionStep == 0,
"the block of a random walk must have a fixed size");
988 using Noise = internal::RandomWalkNoise<Differences::FixedValueDimension, Scalar_>;
992 internal::IdentityStep,
993 internal::ReadsOne<Path>,
994 internal::WritesOf<Path>,
996 internal::DiscreteNoise<Noise>>;
1001 static constexpr int Dimension = Differences::FixedValueDimension;
1017 internal::IdentityStep{},
1018 internal::ReadsOne<Path>{},
1019 internal::WritesOf<Path>{},
1020 AffineIn<internal::PathTag<Path>>{},
1026 [[nodiscard]] SpectralDensityMatrix
const &
1061template <RealScalar Scalar_,
typename... Paths>
1064 internal::KinematicStep<
1065 internal::PathTag<std::tuple_element_t<0, std::tuple<Paths...>>>::FixedValueDimension,
1066 internal::PathTag<Paths>...>,
1067 internal::ReadsOf<Paths...>,
1068 internal::WritesOf<Paths...>,
1069 AffineIn<internal::PathTag<Paths>...>,
1070 internal::DiscreteNoise<internal::KinematicNoise<
1071 internal::PathTag<std::tuple_element_t<0, std::tuple<Paths...>>>::FixedValueDimension,
1073 internal::PathTag<Paths>...>>>
1075 static_assert(
sizeof...(Paths) > 1,
"a kinematic chain needs at least two blocks; one block is a RandomWalk");
1078 using First = internal::PathTag<std::tuple_element_t<0, std::tuple<Paths...>>>;
1083 static constexpr int Order = int(
sizeof...(Paths));
1086 static constexpr int Dimension = First::FixedValueDimension;
1089 ((internal::PathTag<Paths>::ValueDimensionStep == 0
1090 && internal::PathTag<Paths>::FixedValueDimension ==
Dimension)
1092 "the blocks of a kinematic chain must have the same fixed size"
1096 (internal::hasEqualSizes<internal::PathTag<Paths>> && ...),
1097 "the blocks of a kinematic chain must have differences of the same size as their values"
1109 using Step = internal::KinematicStep<Dimension, internal::PathTag<Paths>...>;
1112 using Noise = internal::KinematicNoise<Dimension, Scalar_, internal::PathTag<Paths>...>;
1117 internal::ReadsOf<Paths...>,
1118 internal::WritesOf<Paths...>,
1120 internal::DiscreteNoise<Noise>>;
1133 internal::ReadsOf<Paths...>{},
1134 internal::WritesOf<Paths...>{},
1135 AffineIn<internal::PathTag<Paths>...>{},
1136 internal::DiscreteNoise<Noise>{Noise{spectralDensity}}
1141 [[nodiscard]] SpectralDensityMatrix
const &
1151template <
typename Position,
typename Velocity, RealScalar Scalar =
double>
1157template <
typename Position,
typename Velocity,
typename Acceleration, RealScalar Scalar =
double>
1164template <RealScalar Scalar =
double>
1174template <RealScalar Scalar =
double>
1181template <
typename Value>
1182 requires std::is_arithmetic_v<Value>
1185template <
typename Value>
1186 requires std::is_arithmetic_v<Value>
1209template <
typename Path, RealScalar Scalar_ =
double>
1212 internal::OrnsteinUhlenbeckStep<Scalar_>,
1213 internal::ReadsOne<Path>,
1214 internal::WritesOf<Path>,
1215 AffineIn<internal::PathTag<Path>>,
1216 internal::DiscreteNoise<internal::OrnsteinUhlenbeckNoise<Scalar_>>>
1220 internal::OrnsteinUhlenbeckStep<Scalar_>,
1221 internal::ReadsOne<Path>,
1222 internal::WritesOf<Path>,
1224 internal::DiscreteNoise<internal::OrnsteinUhlenbeckNoise<Scalar_>>>;
1235 internal::ReadsOne<Path>{},
1236 internal::WritesOf<Path>{},
1237 AffineIn<internal::PathTag<Path>>{},
1238 internal::DiscreteNoise<internal::OrnsteinUhlenbeckNoise<Scalar_>>{
1239 internal::OrnsteinUhlenbeckNoise<Scalar_>{timeConstant.value, variance.value}
1243 EMRYS_FILTER_ASSERT(timeConstant.value > 0 &&
"the time constant must be positive");
1244 EMRYS_FILTER_ASSERT(variance.value >= 0 &&
"the variance must be nonnegative");
1248 [[nodiscard]] Scalar_
1251 return this->
function().timeConstant;
1255 [[nodiscard]] Scalar_
1267template <
typename Path>
1270 internal::IdentityStep,
1271 internal::ReadsOne<Path>,
1272 internal::WritesOf<Path>,
1277 internal::IdentityStep,
1278 internal::ReadsOne<Path>,
1279 internal::WritesOf<Path>,
1287 internal::IdentityStep{},
1288 internal::ReadsOne<Path>{},
1289 internal::WritesOf<Path>{},
1290 AffineIn<internal::PathTag<Path>>{}
1299template <
typename R>
1303template <
typename... Items>
1304struct ItemsOf<Reads<Items...>>
1306 using type = std::tuple<Items...>;
1314template <
typename Acc,
typename... Items>
1321template <
typename... Kept,
typename First,
typename... Rest>
1322struct ReadUnion<Reads<Kept...>, First, Rest...>
1324 static constexpr bool present = (std::is_same_v<typename Kept::NameType, typename First::NameType> || ...);
1327 ((!std::is_same_v<typename Kept::NameType, typename First::NameType>
1328 || std::is_same_v<typename Kept::PathType, typename First::PathType>)
1330 "two parts of a composition read different blocks under one name; read one under another name with "
1331 "on<Name>(path) in its reads()"
1334 using type =
typename ReadUnion<std::conditional_t<present, Reads<Kept...>, Reads<Kept..., First>>, Rest...>::type;
1340template <
typename Tuple>
1344template <
typename... Items>
1345struct ReadUnionOf<std::tuple<Items...>>
1347 using type =
typename ReadUnion<Reads<>, Items...>::type;
1355template <
typename Paths,
typename Names>
1356struct WritesOfTuple;
1359template <
typename... Paths,
typename... Names>
1360struct WritesOfTuple<std::tuple<Paths...>, std::tuple<Names...>>
1362 static_assert(distinctTypes<Paths...>(),
"two parts of a composition write the same block");
1364 distinctTypes<Names...>(),
1365 "two parts of a composition write blocks under one name, their paths' last tag; place each model on its "
1366 "block with on<Path>(model)"
1368 using type = WritesAs<TagPaths<Paths...>, Composite<Names...>>;
1374template <
typename Items,
typename Name>
1375inline constexpr bool itemsName =
false;
1380template <
typename... Items,
typename Name>
1381inline constexpr bool itemsName<std::tuple<Items...>, Name> = (std::is_same_v<typename Items::NameType, Name> || ...);
1386template <
typename Model,
typename Name>
1387inline constexpr bool modelReads =
1388 itemsName<typename ItemsOf<typename Model::template Policy<PolicyKind::reads>>::type, Name>;
1391template <
typename Model,
typename Name>
1392inline constexpr bool modelAffineIn =
1393 !modelReads<Model, Name> || affineNames<typename Model::template Policy<PolicyKind::affineIn>, Name>;
1398template <
typename Acc,
typename Models,
typename... Names>
1405template <
typename... Kept,
typename... Models,
typename First,
typename... Rest>
1406struct AffineUnion<AffineIn<Kept...>, std::tuple<Models...>, First, Rest...>
1408 static constexpr bool affine = (modelAffineIn<Models, First> && ...);
1410 using type =
typename AffineUnion<
1411 std::conditional_t<affine, AffineIn<Kept..., First>, AffineIn<Kept...>>,
1412 std::tuple<Models...>,
1419template <
typename Union,
typename Models>
1423template <
typename... Items,
typename Models>
1424struct AffineOf<Reads<Items...>, Models>
1426 using type =
typename AffineUnion<AffineIn<>, Models,
typename Items::NameType...>::type;
1435template <
typename Model,
typename Scalar,
typename... Extra>
1436inline constexpr bool returnsJacobianWith = isWithJacobian<std::remove_cvref_t<
decltype(callForm(
1437 std::declval<typename Model::FunctionType const &>(),
1438 std::declval<
typename Model::template Policy<PolicyKind::reads>::InputSpace
const &>(),
1439 std::declval<OutputSpaceOf<Model>
const &>(),
1441 typename Moments<
typename Model::template Policy<PolicyKind::reads>::InputSpace, Scalar>::VectorType
const &>(),
1442 std::declval<Extra>()...
1448template <
typename Model,
typename Scalar>
1449inline constexpr bool returnsStepJacobian = returnsJacobianWith<Model, Scalar, Scalar>;
1454template <
typename Model,
typename Space,
typename Scalar>
1455inline constexpr bool returnsStepJacobianIn = [] {
1456 if constexpr (readsWholeState<Model>)
1458 return isWithJacobian<std::remove_cvref_t<
decltype(callForm(
1459 std::declval<typename Model::FunctionType const &>(),
1460 std::declval<Space const &>(),
1461 std::declval<Space const &>(),
1462 std::declval<
typename Moments<Space, Scalar>::VectorType
const &>(),
1463 std::declval<Scalar>()
1468 return returnsStepJacobian<Model, Scalar>;
1473template <
typename Model>
1474using ReadItemsOf =
typename ItemsOf<typename Model::template Policy<PolicyKind::reads>>::type;
1479template <
typename Part,
typename In>
1481partIn(In
const & in)
1483 using PartIn =
typename Part::template Policy<PolicyKind::reads>::InputSpace;
1484 return [&]<
typename... Items>(std::tuple<Items...> *) {
1485 return PartIn{pathBlock<typename Items::NameType>(in)...};
1486 }(
static_cast<ReadItemsOf<Part> *
>(
nullptr));
1492template <
typename Part,
typename In,
typename PartIn,
typename X>
1494partVector(In
const & in, PartIn
const & pin, X
const & x)
1496 auto px = zeros<typename X::Scalar>(pin);
1497 [&]<
typename... Items>(std::tuple<Items...> *) {
1498 ((block<typename Items::NameType>(pin, px) = block<typename Items::NameType>(in, x)), ...);
1499 }(
static_cast<ReadItemsOf<Part> *
>(
nullptr));
1506template <
typename Part,
typename Out>
1507[[nodiscard]] OutputSpaceOf<Part>
1508partOut(Out
const & out)
1510 return [&]<
typename... Names>(std::tuple<Names...> *) {
1511 return OutputSpaceOf<Part>{pathBlock<Names>(out)...};
1512 }(
static_cast<CompositeBlocks<OutputSpaceOf<Part>
> *>(
nullptr));
1535 PJ
const & partJacobian,
1536 std::tuple<Items...> *
1539 ((diffBlock<Row, typename Items::NameType>(out, in, jacobian) =
1540 diffBlock<Row, typename Items::NameType>(pout, pin, partJacobian)),
1559 Parts
const & parts,
1564 [[maybe_unused]] J & jacobian,
1565 Extra
const &... extra
1568 using Part = std::tuple_element_t<I, Parts>;
1569 auto const pin = partIn<Part>(in);
1570 auto const pout = partOut<Part>(out);
1571 auto const px = partVector<Part>(in, pin, x);
1572 auto result = vectorReturned(callForm(std::get<I>(parts).function(), pin, pout, px, extra...));
1573 auto const & value = valueOf(result);
1574 using Names = CompositeBlocks<OutputSpaceOf<Part>>;
1575 [&]<
typename... Rows>(std::tuple<Rows...> *) {
1576 ((block<Rows>(out, y) = block<Rows>(pout, value)), ...);
1577 if constexpr (!std::is_same_v<J, NoPolicy>)
1580 Rows>(out, in, jacobian, pout, pin, result.jacobian,
static_cast<ReadItemsOf<Part> *
>(
nullptr)),
1583 }(
static_cast<Names *
>(
nullptr));
1591template <
typename JacobianScalar,
typename... Models,
typename In,
typename Out,
typename X,
typename... Extra>
1593stepParts(std::tuple<Models...>
const & parts, In
const & in, Out
const & out, X
const & x, Extra
const &... extra)
1595 constexpr bool AllJacobians = (returnsJacobianWith<Models, JacobianScalar, Extra...> && ...);
1596 auto y = zeros<typename X::Scalar>(out);
1597 auto jacobian = [&] {
1598 if constexpr (AllJacobians)
1600 using JacobianMatrix = MatrixOf<
1602 valueDimensionAtCompileTime<DifferenceSpace<Out>>,
1603 valueDimensionAtCompileTime<DifferenceSpace<In>>,
1604 maxValueDimensionAtCompileTime<DifferenceSpace<Out>>,
1605 maxValueDimensionAtCompileTime<DifferenceSpace<In>>>;
1613 [&]<std::size_t... I>(std::index_sequence<I...>) {
1614 (stepPart<I>(parts, in, out, x, y, jacobian, extra...), ...);
1615 }(std::index_sequence_for<Models...>{});
1616 if constexpr (AllJacobians)
1618 return withJacobian(std::move(y), std::move(jacobian));
1629template <
typename... Models>
1630inline constexpr bool anyTakesBatches =
1631 (takesBatches<std::remove_cvref_t<decltype(std::declval<Models const &>().function())>> || ...);
1639template <
typename... Models,
typename In,
typename Out,
typename X,
typename Y,
typename... Extra>
1642 std::tuple<Models...>
const & parts,
1647 Extra
const &... extra
1650 using T =
typename X::Scalar;
1652 [[maybe_unused]]
constexpr int Cols = X::ColsAtCompileTime;
1653 [[maybe_unused]]
constexpr int MaxCols = X::MaxColsAtCompileTime;
1654 Eigen::Index
const count = x.cols();
1655 auto & values = y.values();
1657 [&]<std::size_t... I>(std::index_sequence<I...>) {
1660 using Part = std::tuple_element_t<I, std::tuple<Models...>>;
1661 auto const pin = partIn<Part>(in);
1662 auto const pout = partOut<Part>(out);
1663 using PIn = std::remove_cvref_t<
decltype(pin)>;
1664 using POut = OutputSpaceOf<Part>;
1666 MatrixOf<T, valueDimensionAtCompileTime<PIn>, Cols, maxValueDimensionAtCompileTime<PIn>, MaxCols>;
1668 MatrixOf<T, valueDimensionAtCompileTime<POut>, Cols, maxValueDimensionAtCompileTime<POut>, MaxCols>;
1671 for (Eigen::Index j = 0; j < count; ++j)
1673 px.col(j) = partVector<Part>(in, pin, x.col(j));
1677 auto const & function = std::get<I>(parts).function();
1678 if constexpr (takesBatches<std::remove_cvref_t<
decltype(function)>>)
1680 callBatched(function, pin, pout, px, py, extra...);
1684 for (Eigen::Index j = 0; j < count; ++j)
1686 typename Moments<PIn, T>::VectorType
const column = px.col(j);
1687 py.col(j) = valueOf(vectorReturned(callForm(function, pin, pout, column, extra...)));
1690 [&]<
typename... Rows>(std::tuple<Rows...> *) {
1691 ((block<Rows>(out, values) = block<Rows>(pout, py)), ...);
1692 }(
static_cast<CompositeBlocks<POut> *
>(
nullptr));
1696 }(std::index_sequence_for<Models...>{});
1701template <
typename... Models>
1708template <
typename Model>
1709inline constexpr bool isComposition =
false;
1713template <
typename... Models>
1714inline constexpr bool isComposition<Composition<Models...>> =
true;
1721template <
typename Model>
1722struct DeclaredScalars
1724 using type = std::tuple<>;
1729template <
typename Model>
1730 requires(!isComposition<Model> &&
requires {
typename Model::Scalar; })
1731struct DeclaredScalars<Model>
1733 using type = std::tuple<typename Model::Scalar>;
1737template <
typename... Models>
1738struct DeclaredScalars<Composition<Models...>>
1740 using type =
decltype(std::tuple_cat(std::declval<
typename DeclaredScalars<Models>::type>()...));
1746template <
typename... Models>
1747using DeclaredScalarsOf =
decltype(std::tuple_cat(std::declval<
typename DeclaredScalars<Models>::type>()...));
1750template <
typename... Models>
1751[[nodiscard]]
consteval bool
1754 return []<
typename... Scalars>(std::tuple<Scalars...> *) {
1755 if constexpr (
sizeof...(Scalars) == 0)
1761 using First = std::tuple_element_t<0, std::tuple<Scalars...>>;
1762 return (std::is_same_v<First, Scalars> && ...);
1764 }(
static_cast<DeclaredScalarsOf<Models...
> *>(
nullptr));
1768template <
typename Scalars>
1771 using type = double;
1776template <
typename First,
typename... Rest>
1777struct FirstScalarOr<std::tuple<First, Rest...>>
1802template <
typename... Models>
1805 static_assert(
sizeof...(Models) > 0,
"a composition needs a process model");
1807 internal::oneDeclaredScalar<Models...>(),
1808 "the parts of a composition must have one scalar: give every built-in model the same Scalar"
1812 using AllItems =
decltype(std::tuple_cat(
1819 decltype(std::tuple_cat(std::declval<
typename internal::PathsOf<typename Models::WritePaths>::type>()...));
1823 decltype(std::tuple_cat(std::declval<internal::CompositeBlocks<typename Models::OutputSpace>>()...));
1828 using Scalar =
typename internal::FirstScalarOr<internal::DeclaredScalarsOf<Models...>>::type;
1839 using WritesPolicy =
typename internal::WritesOfTuple<AllPaths, AllNames>::type;
1845 template <
internal::PolicyKind K>
1847 K == internal::PolicyKind::reads || K == internal::PolicyKind::writes || K == internal::PolicyKind::affineIn;
1850 template <
internal::PolicyKind K>
1852 K == internal::PolicyKind::reads,
1855 K == internal::PolicyKind::writes,
1857 std::conditional_t<K == internal::PolicyKind::affineIn, AffinePolicy, internal::NoPolicy>>>;
1867 : m_parts{std::move(models)...}
1878 template <
internal::PolicyKind K>
1879 [[nodiscard]] Policy<K>
const &
1882 static_assert(
has<K>,
"the composition was not given this policy");
1888 [[nodiscard]] std::tuple<Models...>
const &
1898 template <
typename In,
typename Out,
typename X,
typename TimeScalar>
1900 operator()(In
const & in, Out
const & out, X
const & x, TimeScalar dt)
const
1903 return internal::stepParts<TimeScalar>(m_parts, in, out, x, dt);
1916 template <
typename In,
typename Out,
typename X,
typename TimeScalar,
typename Y>
1918 operator()(In
const & in, Out
const & out, X
const & x, TimeScalar dt, Y & y)
const
1921 internal::stepPartsBatch(m_parts, in, out, x, y, dt);
1927 template <
typename Scalar,
typename Out>
1933 Matrix q = Matrix::Zero(n, n);
1934 [&]<std::size_t... I>(std::index_sequence<I...>) {
1935 (noisePart<I, Scalar>(out, dt, q), ...);
1936 }(std::index_sequence_for<Models...>{});
1943 template <
typename Space>
1944 [[nodiscard]]
static consteval internal::StaticPartition
1947 return internal::staticPartitionOfWrites<Space, WritePaths>();
1951 template <
typename Space>
1955 return internal::writesPartition(space,
static_cast<WritePaths *
>(
nullptr));
1961 template <std::
size_t I>
1962 using Part = std::tuple_element_t<I, std::tuple<Models...>>;
1965 template <std::
size_t I,
typename Scalar,
typename Out,
typename Q>
1967 noisePart(Out
const & out, Scalar dt, Q & q)
const
1969 auto const pout = internal::partOut<Part<I>>(out);
1970 auto const partNoise = std::get<I>(m_parts).template noiseOver<Scalar>(pout, dt);
1971 using Names = internal::CompositeBlocks<typename Part<I>::OutputSpace>;
1972 [&]<
typename... Rows>(std::tuple<Rows...> *) {
1973 (noiseRow<Rows>(out, q, pout, partNoise,
static_cast<Names *
>(
nullptr)), ...);
1974 }(
static_cast<Names *
>(
nullptr));
1980 template <
typename Row,
typename Out,
typename Q,
typename POut,
typename PQ,
typename... Columns>
1982 noiseRow(Out
const & out, Q & q, POut
const & pout, PQ
const & partNoise, std::tuple<Columns...> *)
1984 ((diffBlock<Row, Columns>(out, out, q) = diffBlock<Row, Columns>(pout, pout, partNoise)), ...);
1987 std::tuple<Models...> m_parts;
1994template <
typename... Models>
1996compositionBase(Composition<Models...>
const *);
2000compositionBase(
void const *);
2009template <
typename M>
2010concept ProcessModel =
decltype(internal::processFunctionBase(std::declval<std::remove_cvref_t<M>
const *>()))::value
2011 ||
decltype(internal::compositionBase(std::declval<std::remove_cvref_t<M>
const *>()))::value;
2021 template <
typename Space>
2022 [[nodiscard]]
static consteval internal::StaticPartition
2025 if constexpr (Space::ValueDimensionStep == 0)
2027 return Space::FixedValueDimension == 0 ? internal::StaticPartition::holds
2028 : internal::StaticPartition::fails;
2032 return internal::StaticPartition::unknown;
2037 template <
typename Space>
2053template <ProcessModel... Models>
2054[[nodiscard]] Composition<std::remove_cvref_t<Models>...>
2057 static_assert((internal::wholeStateRefused<std::remove_cvref_t<Models>> && ...));
2067template <
typename Model,
typename Space>
2068[[nodiscard]]
consteval bool
2071 if constexpr (readsWholeState<Model>)
2077 return []<
typename... Items>(std::tuple<Items...> *) {
2079 []<
typename First,
typename... Rest>(TagPath<First, Rest...> *) {
2080 return countTag<First, Space>() > 0;
2081 }(
static_cast<typename Items::PathType *
>(
nullptr))
2084 }(
static_cast<typename ItemsOf<typename Model::template Policy<PolicyKind::reads>
>::type *>(
nullptr));
2089template <
typename Scalar, ProcessModel Model, Space S>
2090[[nodiscard]]
typename Moments<S, Scalar>::MatrixType
2091processNoiseOfBlocks(Model
const & model, S
const & space, Scalar dt)
2093 using Matrix =
typename Moments<S, Scalar>::MatrixType;
2094 auto const out = outputOf<Model>(space);
2095 auto const partial = model.template noiseOver<Scalar>(out, dt);
2097 Matrix q = Matrix::Zero(n, n);
2098 [&]<std::size_t... K>(std::index_sequence<K...>) {
2099 std::array<BlockRows,
sizeof...(K)>
const rows{differenceBlockRows<WritePathAt<Model, K>>(space)...};
2100 std::array<BlockRows,
sizeof...(K)>
const outRows{differenceBlockRows<WriteNameAt<Model, K>>(out)...};
2101 for (std::size_t k = 0; k <
sizeof...(K); ++k)
2103 for (std::size_t l = 0; l <
sizeof...(K); ++l)
2105 q.block(rows[k].start, rows[l].start, rows[k].size, rows[l].size) =
2106 partial.block(outRows[k].start, outRows[l].start, outRows[k].size, outRows[l].size);
2109 }(std::make_index_sequence<writeCount<Model>>{});
2117template <
typename Scalar, ProcessModel Model, Space S>
2118[[nodiscard]]
typename Moments<S, Scalar>::MatrixType
2119processNoise(Model
const & model, S
const & space, Scalar dt)
2121 using Matrix =
typename Moments<S, Scalar>::MatrixType;
2122 if constexpr (writesWholeState<Model>)
2124 return Matrix{model.template noiseOver<Scalar>(space, dt)};
2128 return processNoiseOfBlocks<Scalar>(model, space, dt);
2140template <
typename Call,
typename Space>
2141class ResolvedProcess :
public ResolvedReads<typename Call::ModelType, typename Call::ArgumentsType, Space>
2144 using Base = ResolvedReads<typename Call::ModelType, typename Call::ArgumentsType, Space>;
2148 using CallType = Call;
2149 using Model =
typename Call::ModelType;
2150 using Function =
typename Model::FunctionType;
2152 using Base::InputCount;
2153 using Base::ItemCount;
2154 using Base::StateCount;
2155 using typename Base::In;
2156 using typename Base::Reads;
2159 template <std::
size_t J>
2160 using Argument =
typename Base::template Argument<J>;
2163 template <
typename T>
2164 using InVector =
typename Base::template InVector<T>;
2167 using WritePaths =
typename Model::WritePaths;
2169 static constexpr std::size_t WriteCount = std::tuple_size_v<typename PathsOf<WritePaths>::type>;
2172 static constexpr bool Whole = writesWholeState<Model>;
2177 using Out = OutputSpaceIn<Model, Space>;
2180 static constexpr bool Affine = affineInEverything<Model>();
2183 static constexpr bool ReturnsJacobian = returnsStepJacobianIn<Model, Space, double>;
2187 using Base::argumentOf;
2188 using Base::inputItems;
2189 using Base::m_differenceRows;
2191 using Base::m_reads;
2192 using Base::m_stateDifferenceSize;
2193 using Base::stateIndexOf;
2194 using Base::stateItems;
2199 ResolvedProcess(Call
const & call, Space
const & space)
2200 : Base{call.arguments(), space}
2202 , m_out{outputOf<Model>(space)}
2205 if constexpr (Whole)
2207 m_writeRows[0] = m_outRows[0] = BlockRows{0, m_stateSize};
2208 m_writeDifferenceRows[0] = m_outDifferenceRows[0] = BlockRows{0,
differenceDimension(space)};
2212 [&]<std::size_t... K>(std::index_sequence<K...>) {
2213 ((m_writeRows[K] = blockRows<WritePathAt<Model, K>>(space),
2214 m_writeDifferenceRows[K] = differenceBlockRows<WritePathAt<Model, K>>(space),
2215 m_outRows[K] = blockRows<WriteNameAt<Model, K>>(m_out),
2216 m_outDifferenceRows[K] = differenceBlockRows<WriteNameAt<Model, K>>(m_out)),
2218 }(std::make_index_sequence<WriteCount>{});
2223 [[nodiscard]] Model
const &
2226 return m_call.model();
2230 [[nodiscard]] Out
const &
2237 template <
typename Scalar>
2238 [[nodiscard]]
typename Moments<Space, Scalar>::MatrixType
2239 noise(Scalar dt)
const
2241 using Matrix =
typename Moments<Space, Scalar>::MatrixType;
2242 auto const partial = model().template noiseOver<Scalar>(m_out, dt);
2243 Matrix q = Matrix::Zero(m_stateDifferenceSize, m_stateDifferenceSize);
2244 for (std::size_t k = 0; k < WriteCount; ++k)
2246 for (std::size_t l = 0; l < WriteCount; ++l)
2249 m_writeDifferenceRows[k].start,
2250 m_writeDifferenceRows[l].start,
2251 m_writeDifferenceRows[k].size,
2252 m_writeDifferenceRows[l].size
2255 m_outDifferenceRows[k].start,
2256 m_outDifferenceRows[l].start,
2257 m_outDifferenceRows[k].size,
2258 m_outDifferenceRows[l].size
2272 template <
typename State,
typename TimeScalar,
typename... Inputs>
2274 propagate(State
const & state, TimeScalar dt, Inputs
const &... inputs)
const
2277 sizeof...(Inputs) == InputCount ||
sizeof...(Inputs) == 0,
2278 "the step takes one vector per uncertain input, or none to read each at its mean"
2280 using T = ScalarOf<State>;
2281 using Vector =
typename Moments<Space, T>::VectorType;
2282 EMRYS_FILTER_ASSERT(state.size() == m_stateSize &&
"the state does not fit the space");
2284 this->
template gatherFromRows<T>(state, Eigen::Index{0}, std::tuple<Inputs
const &...>{inputs...});
2285 auto result = vectorReturned(callForm(model().function(), m_in, m_out, x, dt));
2286 using Result = std::remove_cvref_t<
decltype(result)>;
2288 scatter(y, valueOf(result));
2289 if constexpr (isWithJacobian<Result>)
2291 return withJacobian(std::move(y), expand(result.jacobian));
2300 static constexpr bool BatchedFunction = takesBatches<Function>;
2303 template <
typename S,
typename T>
2305 MatrixOf<T, valueDimensionAtCompileTime<S>, Eigen::Dynamic, maxValueDimensionAtCompileTime<S>, Eigen::Dynamic>;
2310 template <
typename T,
typename Column,
typename State,
typename... Inputs>
2312 gatherColumn(Column && column, State
const & state, Inputs
const &... inputs)
const
2314 static_assert(
sizeof...(Inputs) == InputCount,
"the step takes one vector per uncertain input");
2315 column = this->
template gatherFromRows<T>(state, Eigen::Index{0}, std::tuple<Inputs
const &...>{inputs...});
2322 template <
typename X,
typename TimeScalar,
typename Y>
2324 stepBatch(X
const & x, TimeScalar dt, Y && y)
const
2325 requires BatchedFunction
2327 callBatched(model().function(), m_in, m_out, x, y, dt);
2335 template <
typename Y,
typename States,
typename TimeScalar,
typename... Inputs>
2337 propagateColumns(Y & y, States
const & states, TimeScalar dt, Inputs
const &... inputs)
const
2338 requires BatchedFunction
2340 auto const x = gatherStates<Y>(states, inputs...);
2341 auto values = ValuesOf<Y, typename States::Scalar>{};
2343 stepBatch(x, dt, values);
2344 scatterColumns(y, states, values);
2353 template <
typename Y,
typename J,
typename States,
typename TimeScalar,
typename... Inputs>
2355 propagateColumnsWithAffineJacobians(
2358 States
const & states,
2360 Inputs
const &... inputs
2362 requires BatchedFunction
2364 using T =
typename States::Scalar;
2365 auto const x = gatherStates<Y>(states, inputs...);
2366 Eigen::Index
const count = states.cols();
2367 auto values = ValuesOf<Y, T>{};
2369 auto wanted = std::array<bool, std::tuple_size_v<InputBlocks<In>>>{};
2370 [&]<std::size_t... I>(std::index_sequence<I...>) {
2371 ((wanted[I] = Base::template itemFactored<I>()), ...);
2372 }(std::make_index_sequence<ItemCount>{});
2373 Eigen::Index
const l = jacobians.cols() / std::max<Eigen::Index>(count, 1);
2374 using OutJacobians = MatrixOf<
2376 valueDimensionAtCompileTime<DifferenceSpace<Out>>,
2377 J::ColsAtCompileTime,
2378 maxValueDimensionAtCompileTime<DifferenceSpace<Out>>,
2379 J::MaxColsAtCompileTime>;
2380 auto outJacobians = OutJacobians{};
2382 callBatchedWithJacobians(model().function(), m_in, m_out, x, wanted, values, outJacobians, dt);
2383 scatterColumns(y, states, values);
2384 if constexpr (Whole)
2386 jacobians = outJacobians;
2390 jacobians.setZero();
2391 Eigen::Index offset = 0;
2392 [&]<std::size_t... I>(std::index_sequence<I...>) {
2395 if constexpr (Base::template itemFactored<I>())
2397 if constexpr (Base::inState(I))
2399 BlockRows
const rows = this->m_reads.differenceRows(Base::stateIndexOf(I));
2400 for (Eigen::Index j = 0; j < count; ++j)
2402 for (Eigen::Index r = 0; r < rows.size; ++r)
2404 jacobians(rows.start + r, j * l + offset + r) = T(1);
2408 offset += this->m_differenceRows[I].size;
2413 }(std::make_index_sequence<ItemCount>{});
2414 for (std::size_t k = 0; k < WriteCount; ++k)
2416 BlockRows
const & written = m_writeDifferenceRows[k];
2417 BlockRows
const & rows = m_outDifferenceRows[k];
2418 jacobians.middleRows(written.start, written.size) = outJacobians.middleRows(rows.start, rows.size);
2427 template <
typename Y,
typename States,
typename... Inputs>
2429 gatherStates(States
const & states, Inputs
const &... inputs)
const
2431 static_assert(
sizeof...(Inputs) == InputCount);
2432 using T =
typename States::Scalar;
2435 valueDimensionAtCompileTime<In>,
2436 Y::ColsAtCompileTime,
2437 maxValueDimensionAtCompileTime<In>,
2438 Y::MaxColsAtCompileTime>;
2439 EMRYS_FILTER_ASSERT(states.rows() == m_stateSize &&
"the states do not fit the space");
2442 for (Eigen::Index j = 0; j < states.cols(); ++j)
2444 gatherColumn<T>(x.col(j), states.col(j), inputs.col(j)...);
2452 template <
typename Y,
typename T>
2453 using ValuesOf = MatrixOf<
2455 valueDimensionAtCompileTime<Out>,
2456 Y::ColsAtCompileTime,
2457 maxValueDimensionAtCompileTime<Out>,
2458 Y::MaxColsAtCompileTime>;
2463 template <
typename Y,
typename States,
typename Values>
2465 scatterColumns(Y & y, States
const & states, Values
const & values)
const
2468 for (Eigen::Index j = 0; j < states.cols(); ++j)
2470 auto column = y.col(j);
2471 scatter(column, values.col(j));
2478 template <
typename State,
typename Value>
2480 scattered(State
const & state, Value
const & value)
const
2482 using Vector =
typename Moments<Space, ScalarOf<State>>::VectorType;
2492 template <
typename Scalar>
2493 [[nodiscard]]
typename Moments<Space, Scalar>::MatrixType
2494 transition(Scalar dt)
const
2495 requires(Affine && ReturnsJacobian && InputCount == 0)
2497 using Vector =
typename Moments<Space, Scalar>::VectorType;
2498 auto const zero = Vector{Vector::Zero(m_stateSize)};
2507 template <
typename Y,
typename Value>
2509 scatter(Y & y, Value
const & value)
const
2511 EMRYS_FILTER_ASSERT(
2512 value.size() ==
valueDimension(m_out) &&
"a process model's function returns a vector in its output space"
2514 if constexpr (Whole)
2520 scatterBlocks(y, value);
2525 template <
typename Y,
typename Value>
2527 scatterBlocks(Y & y, Value
const & value)
const
2529 [&]<std::size_t... K>(std::index_sequence<K...>) {
2531 valueDimensionAtCompileTime<PathTag<std::tuple_element_t<K, typename PathsOf<WritePaths>::type>>>>(
2533 m_writeRows[K].start,
2537 PathTag<std::tuple_element_t<K, typename PathsOf<WritePaths>::type>>>>(
2543 }(std::make_index_sequence<WriteCount>{});
2551 template <
typename J>
2553 expand(J
const & jacobian)
const
2556 MatrixExpression<J>,
2557 "a process model's function returns its Jacobian as one matrix over its input's differences, "
2558 "withJacobian(y, J)"
2560 EMRYS_FILTER_ASSERT(
2562 &&
"a returned Jacobian has a row per difference coordinate of the output and a column per one of the input"
2564 using Scalar =
typename J::Scalar;
2565 using StateMatrix =
typename Moments<Space, Scalar>::MatrixType;
2566 StateMatrix f = StateMatrix::Identity(m_stateDifferenceSize, m_stateDifferenceSize);
2567 for (std::size_t k = 0; k < WriteCount; ++k)
2569 f.middleRows(m_writeDifferenceRows[k].start, m_writeDifferenceRows[k].size).setZero();
2570 [&]<std::size_t... I>(std::index_sequence<I...>) {
2572 m_writeDifferenceRows[k].start,
2573 m_reads.differenceRows(I).start,
2574 m_writeDifferenceRows[k].size,
2575 m_reads.differenceRows(I).size
2578 m_outDifferenceRows[k].start,
2579 m_differenceRows[stateItems(I)].start,
2580 m_outDifferenceRows[k].size,
2581 m_differenceRows[stateItems(I)].size
2584 }(std::make_index_sequence<StateCount>{});
2586 if constexpr (InputCount == 0)
2592 return [&]<std::size_t... P>(std::index_sequence<P...>) {
2593 return std::tuple{std::move(f), inputBlock<P>(jacobian)...};
2594 }(std::make_index_sequence<InputCount>{});
2599 template <std::
size_t P,
typename J>
2601 inputBlock(J
const & jacobian)
const
2604 std::remove_cvref_t<
decltype(inputOf(std::declval<Argument<argumentOf(inputItems(P))>
const &>()))>;
2605 using Block = MatrixOf<
2607 valueDimensionAtCompileTime<DifferenceSpace<Space>>,
2608 Input::DifferenceDimensionAtCompileTime,
2609 maxValueDimensionAtCompileTime<DifferenceSpace<Space>>,
2610 Input::MaxDifferenceDimensionAtCompileTime>;
2611 BlockRows
const & columns = m_differenceRows[inputItems(P)];
2612 Block
block = Block::Zero(m_stateDifferenceSize, columns.size);
2613 for (std::size_t k = 0; k < WriteCount; ++k)
2615 block.middleRows(m_writeDifferenceRows[k].start, m_writeDifferenceRows[k].size) =
2616 jacobian.block(m_outDifferenceRows[k].start, columns.start, m_outDifferenceRows[k].size, columns.size);
2621 Call
const & m_call;
2623 Eigen::Index m_stateSize;
2624 std::array<BlockRows, WriteCount> m_writeRows{};
2625 std::array<BlockRows, WriteCount> m_writeDifferenceRows{};
2626 std::array<BlockRows, WriteCount> m_outRows{};
2627 std::array<BlockRows, WriteCount> m_outDifferenceRows{};
2635template <
typename R,
typename TimeScalar>
2641 static constexpr bool EvaluatesBatches = R::BatchedFunction;
2644 ProcessStep(R
const & resolved, TimeScalar dt)
2645 : m_resolved{resolved}
2652 template <
typename X,
typename... Inputs>
2654 operator()(X
const & x, Inputs
const &... inputs)
const
2656 return m_resolved.propagate(x, m_dt, inputs...);
2662 template <
typename Y,
typename States,
typename... Inputs>
2664 evaluateBatch(Y & y, States
const & states, Inputs
const &... inputs)
const
2665 requires EvaluatesBatches
2667 m_resolved.propagateColumns(y, states, m_dt, inputs...);
2673 template <
typename Y,
typename J,
typename States,
typename... Inputs>
2675 evaluateBatchWithAffineJacobians(Y & y, J & jacobians, States
const & states, Inputs
const &... inputs)
const
2676 requires EvaluatesBatches
2678 m_resolved.propagateColumnsWithAffineJacobians(y, jacobians, states, m_dt, inputs...);
2683 R
const & m_resolved;
2691template <
typename R,
typename TimeScalar>
2692class ProcessStepValues :
public ProcessStep<R, TimeScalar>
2696 using ProcessStep<R, TimeScalar>::ProcessStep;
2699 template <
typename X,
typename... Inputs>
2701 operator()(X
const & x, Inputs
const &... inputs)
const
2703 return valueOf(ProcessStep<R, TimeScalar>::operator()(x, inputs...));
2710template <
typename Space,
typename Call>
2711[[nodiscard]] ResolvedProcess<Call, Space>
2712resolveProcess(Call
const & call, Space
const & space)
2714 return ResolvedProcess<Call, Space>{call, space};
2718template <
typename... Args>
2719inline constexpr bool referenceAfterFirst = []<std::size_t... I>(std::index_sequence<I...>) {
2720 return ((I > 0 && isLinearizedAbout<std::tuple_element_t<I, std::tuple<Args...>>>) || ... ||
false);
2721}(std::index_sequence_for<Args...>{});
2726template <
typename F,
typename... Args>
2727[[nodiscard]]
decltype(
auto)
2728withReference(F && f, Args
const &... args)
2731 !referenceAfterFirst<Args...>,
2732 "a prediction's linearizedAbout() reference comes first among the arguments after the step, once"
2734 if constexpr (leadsWithReference<Args...>)
2736 return std::forward<F>(f)(args...);
2740 return std::forward<F>(f)(NoReference{}, args...);
2752template <Space Space_,
typename Derived, ProcessModel Model,
typename... Args>
2753[[nodiscard]]
typename Moments<Space_, typename Derived::Scalar>::VectorType
2755 Space_
const & space,
2756 Eigen::MatrixBase<Derived>
const & x,
2757 Model
const & model,
2759 Args
const &... args
2762 using Scalar =
typename Derived::Scalar;
2763 EMRYS_FILTER_ASSERT(x.size() ==
valueDimension(space) &&
"the state does not fit the space");
2764 auto const call = internal::modelWith(std::cref(model), args...);
2765 auto const resolved = internal::resolveProcess(call, space);
2766 return internal::valueOf(resolved.propagate(x.derived(), internal::toScalarTime<Scalar>(dt)));
2769template <Space Space_,
typename Model_,
typename Method_, RealScalar Scalar_>
2770class FixedStepModel;
2775template <
typename Model>
2781template <
typename Space_,
typename Model_,
typename Method_,
typename Scalar_>
2784template <ProcessModel Model_,
typename Method_,
typename Time_>
2785class UnboundFixedStep;
2791template <
typename Model>
2797template <
typename Model_,
typename Method_,
typename Time_>
2815template <Space Space_, RealScalar Scalar_ =
double>
2839template <
typename Method,
typename R>
2840inline constexpr bool predictsByJacobian =
2841 std::is_same_v<Method, methods::Linearized> && R::InputCount == 0 && R::ReturnsJacobian;
2847template <
typename Method,
typename SpaceType,
typename Prior,
typename R,
typename TimeScalar>
2849transformStep(Method
const & method, SpaceType
const & space, Prior
const & prior, R
const & resolved, TimeScalar dt)
2851 auto const f = ProcessStep{resolved, dt};
2853 [&](
auto const &... inputs) {
2854 if constexpr (isSigmaPointMethod<Method> && R::factorsAffine())
2856 auto const factored = FactoredAt<R::affineSize(), R::affineMaxSize(), Method>{
2858 resolved.template affineCoordinates<true>()
2860 return transformBy(factored, f, space, prior, inputs...);
2864 return transformBy(method, f, space, prior, inputs...);
2872template <UncertainInput Prior>
2873[[nodiscard]] GaussianParams<typename Prior::SpaceType, typename Prior::Scalar>
2874gaussianOf(Prior
const & prior)
2876 using Result = GaussianParams<typename Prior::SpaceType, typename Prior::Scalar>;
2877 if constexpr (std::is_same_v<Prior, Result>)
2885 typename Result::VectorType{prior.mean()},
2886 typename Result::MatrixType{covarianceLowerOf(prior)}
2894template <UncertainInput Prior,
typename Form,
typename Noise>
2895[[nodiscard]] PredictedJoint<typename Prior::SpaceType, typename Prior::Scalar>
2897 Prior
const & prior,
2898 OutputWithCross<typename Prior::SpaceType, typename Prior::SpaceType, Form, typename Prior::Scalar> step,
2902 using Matrix =
typename PredictedJoint<typename Prior::SpaceType, typename Prior::Scalar>::MatrixType;
2903 Matrix cross = step.crossCovariance();
2904 auto predicted = std::move(step).output();
2905 predicted.setCovariance(predicted.covarianceLower() + noise);
2906 return {gaussianOf(prior), std::move(predicted), std::move(cross)};
2910template <UncertainInput Prior,
typename Form>
2911[[nodiscard]] PredictedJoint<typename Prior::SpaceType, typename Prior::Scalar>
2913 Prior
const & prior,
2914 OutputWithCross<typename Prior::SpaceType, typename Prior::SpaceType, Form, typename Prior::Scalar> step
2917 using Matrix =
typename PredictedJoint<typename Prior::SpaceType, typename Prior::Scalar>::MatrixType;
2918 Matrix cross = step.crossCovariance();
2919 return {gaussianOf(prior), std::move(step).output(), std::move(cross)};
2926template <
typename R,
typename Derived,
typename TimeScalar>
2928stepWithJacobian(R
const & resolved, Eigen::MatrixBase<Derived>
const & x, TimeScalar dt)
2930 return resolved.propagate(x.derived(), dt);
2939template <
typename Space,
typename Scalar,
typename R,
typename AddNoise>
2941predictInPlaceByJacobianWithNoise(
2942 GaussianParams<Space, Scalar> & state,
2945 AddNoise
const & addNoise
2948 using Matrix =
typename GaussianParams<Space, Scalar>::MatrixType;
2949 auto const & space = state.space();
2950 auto step = stepWithJacobian(resolved, state.mean(), dt);
2951 auto & moments = GaussianAccess::moments(state);
2952 Matrix & cov = moments.covarianceLower();
2953 cov.template triangularView<Eigen::StrictlyUpper>() = cov.transpose();
2954 auto propagated = Matrix{};
2955 propagated.noalias() = multiply(step.jacobian, cov);
2956 cov.noalias() = multiply(propagated, step.jacobian.transpose());
2958 moments.mean() = std::move(step.value);
2965template <
typename Space,
typename Scalar,
typename R>
2967predictInPlaceByJacobian(GaussianParams<Space, Scalar> & state, R
const & resolved, Scalar dt)
2969 auto const addNoise = [&](
typename GaussianParams<Space, Scalar>::MatrixType & cov) {
2970 cov += resolved.template noise<Scalar>(dt);
2972 predictInPlaceByJacobianWithNoise(state, resolved, dt, addNoise);
2981template <
typename Method,
typename R,
typename Space,
typename Base,
typename Reference>
2982[[nodiscard]] LinearizationAbout<Space, Space, typename Base::Scalar>
2984 Method
const & method,
2986 Space
const & space,
2987 typename Base::Scalar dt,
2988 Eigen::MatrixBase<Base>
const & base,
2989 LinearizedAbout<Reference>
const & about
2992 using Scalar =
typename Base::Scalar;
2993 using Result = LinearizationAbout<Space, Space, Scalar>;
2994 if constexpr (predictsByJacobian<Method, R>)
2996 typename Moments<Space, Scalar>::VectorType
const point = about.mean();
2997 auto step = stepWithJacobian(resolved, point, dt);
2999 auto result = Result{
3001 typename Result::OutputVector{std::move(step.value)},
3002 typename Result::JacobianMatrix{std::move(step.jacobian)},
3003 Result::ErrorMatrix::Zero(n, n),
3004 Result::ErrorMatrix::Zero(n, n)
3007 toChartOf(space, base, point, result);
3012 auto const f = ProcessStep{resolved, dt};
3014 [&](
auto const &... inputs) {
3015 if constexpr (isSigmaPointMethod<Method> && R::factorsAffine())
3017 auto const factored = FactoredAt<R::affineSize(), R::affineMaxSize(), Method>{
3019 resolved.template affineCoordinates<true>()
3021 return linearizeAbout(factored, f, space, space, base, about, inputs...);
3025 return linearizeAbout(method, f, space, space, base, about, inputs...);
3040template <
typename Method, UncertainInput Prior,
typename R,
typename Derived,
typename Reference>
3041[[nodiscard]] OutputWithCross<
3042 typename Prior::SpaceType,
3043 typename Prior::SpaceType,
3045 typename Prior::Scalar>
3047 Method
const & method,
3048 Prior
const & prior,
3050 typename Prior::Scalar dt,
3051 Eigen::MatrixBase<Derived>
const & noise,
3052 LinearizedAbout<Reference>
const & about
3055 using Space =
typename Prior::SpaceType;
3056 using Scalar =
typename Prior::Scalar;
3057 using Result = OutputWithCross<Space, Space, JacobianForm, Scalar>;
3058 using Matrix =
typename GaussianParams<Space, Scalar>::MatrixType;
3059 auto const & space = prior.space();
3060 auto linearization = linearizeStepAbout(method, resolved, space, dt, prior.mean(), about);
3061 Matrix
const cov = symmetricFromLower<Matrix>(covarianceLowerOf(prior));
3062 typename Result::CrossMatrix cross = multiply(cov, linearization.jacobian.transpose());
3063 Matrix explained = multiply(linearization.jacobian, cross);
3064 explained = (Scalar(0.5) * (explained + explained.transpose())).eval();
3066 Matrix others = linearization.inputs + symmetricFromLower<Matrix>(noise);
3069 std::move(linearization.value),
3070 std::move(linearization.jacobian),
3072 std::move(explained),
3074 std::move(linearization.unexplained)
3082template <
typename Method, UncertainInput Prior,
typename R,
typename Derived,
typename Reference = NoReference>
3083[[nodiscard]] GaussianParams<typename Prior::SpaceType, typename Prior::Scalar>
3085 Method
const & method,
3086 Prior
const & prior,
3088 typename Prior::Scalar dt,
3089 Eigen::MatrixBase<Derived>
const & noise,
3090 Reference
const & reference = {}
3093 if constexpr (isLinearizedAbout<Reference>)
3095 return predictAbout(method, prior, resolved, dt, noise, reference).output();
3099 auto predicted = transformStep(method, prior.space(), prior, resolved, dt);
3100 predicted.setCovariance(predicted.covarianceLower() + noise);
3106template <
typename Method, UncertainInput Prior,
typename R,
typename Derived,
typename Reference = NoReference>
3107[[nodiscard]] PredictedJoint<typename Prior::SpaceType, typename Prior::Scalar>
3108predictJointWithNoise(
3109 Method
const & method,
3110 Prior
const & prior,
3112 typename Prior::Scalar dt,
3113 Eigen::MatrixBase<Derived>
const & noise,
3114 Reference
const & reference = {}
3117 if constexpr (isLinearizedAbout<Reference>)
3119 return jointOf(prior, predictAbout(method, prior, resolved, dt, noise, reference));
3123 return jointOf(prior, transformStep(method, prior.space(),
withCross(prior), resolved, dt), noise);
3131template <
typename Method, UncertainInput Prior,
typename R,
typename Derived,
typename Reference>
3132[[nodiscard]] GaussianParams<typename Prior::SpaceType, typename Prior::Scalar>
3134 Method
const & method,
3135 Prior
const & prior,
3137 typename Prior::Scalar dt,
3138 Eigen::MatrixBase<Derived>
const & noise,
3139 Reference
const & reference
3142 using Result = GaussianParams<typename Prior::SpaceType, typename Prior::Scalar>;
3143 if constexpr (!isLinearizedAbout<Reference> && predictsByJacobian<Method, R>)
3147 auto result = Result{
3149 typename Result::VectorType{prior.mean()},
3150 typename Result::MatrixType{covarianceLowerOf(prior)}
3152 predictInPlaceByJacobianWithNoise(result, resolved, dt, [&](
typename Result::MatrixType & cov) {
3159 return predictWithNoise(method, prior, resolved, dt, noise, reference);
3166template <
typename Model,
typename Method>
3167inline constexpr bool predictsBy =
true;
3170template <
typename Space_,
typename Model_,
typename ModelMethod,
typename Scalar_,
typename Method>
3171inline constexpr bool predictsBy<FixedStepModel<Space_, Model_, ModelMethod, Scalar_>, Method>{
3172 std::is_same_v<ModelMethod, Method>
3182template <
typename Method>
3183[[nodiscard]]
consteval bool
3184checkPredictionMethod()
3187 !isIterated<Method>,
3188 "Iterated is a measurement update's method: a prediction has no posterior to relinearize at, so give the "
3189 "filter or the fixed-step model its inner method"
3197template <
typename Model>
3198struct DefaultMethodOf
3200 using type = methods::Linearized;
3204template <
typename Space_,
typename Model_,
typename Method_,
typename Scalar_>
3205struct DefaultMethodOf<FixedStepModel<Space_, Model_, Method_, Scalar_>>
3207 using type = Method_;
3219template <
typename Model>
3235template <
typename Model,
typename Method>
3244template <
typename Model,
typename Method>
3245 requires(!std::is_same_v<Method, ModelsMethod>)
3247predictionMethod(Model
const &, Method method)
3253template <
typename Space_,
typename Model_,
typename Method_,
typename Scalar_>
3254[[nodiscard]] Method_
3261template <
typename Model>
3262inline constexpr bool cachesTransition =
false;
3265template <
typename Space_,
typename Model_,
typename Method_,
typename Scalar_>
3266inline constexpr bool cachesTransition<FixedStepModel<Space_, Model_, Method_, Scalar_>> =
3310template <
typename Method, UncertainInput Prior, ProcessModel Model,
typename... Args>
3311[[nodiscard]] GaussianParams<typename Prior::SpaceType, typename Prior::Scalar>
3313 Method
const & method,
3314 Prior
const & prior,
3315 Model
const & model,
3317 Args
const &... args
3320 if constexpr (!
requires { prior.covarianceLower(); })
3322 return predict(method, internal::withCovariance(prior), model, time, args...);
3326 using Scalar =
typename Prior::Scalar;
3327 auto const & space = prior.space();
3328 Scalar
const dt = internal::toScalarTime<Scalar>(time);
3329 internal::assertPartitions(model, space);
3330 return internal::withReference(
3331 [&](
auto const & reference,
auto const &... inputs) {
3332 auto const call = internal::modelWith(std::cref(model), inputs...);
3333 auto const resolved = internal::resolveProcess(call, space);
3334 return internal::predictResolved(
3339 resolved.template noise<Scalar>(dt),
3360template <
typename Method, UncertainInput Prior, ProcessModel Model,
typename... Args>
3361[[nodiscard]] PredictedJoint<typename Prior::SpaceType, typename Prior::Scalar>
3363 Method
const & method,
3364 Prior
const & prior,
3365 Model
const & model,
3367 Args
const &... args
3370 if constexpr (!
requires { prior.covarianceLower(); })
3372 return predictJoint(method, internal::withCovariance(prior), model, time, args...);
3376 using Scalar =
typename Prior::Scalar;
3377 auto const & space = prior.space();
3378 Scalar
const dt = internal::toScalarTime<Scalar>(time);
3379 internal::assertPartitions(model, space);
3380 return internal::withReference(
3381 [&](
auto const & reference,
auto const &... inputs) {
3382 auto const call = internal::modelWith(std::cref(model), inputs...);
3383 auto const resolved = internal::resolveProcess(call, space);
3384 return internal::predictJointWithNoise(
3389 resolved.template noise<Scalar>(dt),
3413template <ProcessModel Model_,
typename Method_,
typename Time_>
3424 : m_model{std::move(
model)}
3425 , m_method{std::move(
method)}
3426 , m_timeStep{std::move(dt)}
3430 [[nodiscard]] Model_
const &
3437 [[nodiscard]] Method_
const &
3444 [[nodiscard]] Time_
const &
3451 template <
typename Space>
3455 return m_model.partitions(space);
3461 template <
typename Space>
3462 [[nodiscard]]
static consteval internal::StaticPartition
3465 return internal::staticPartition<Model_, Space>();
3481template <
typename Space_>
3486 FixedSizeSpace<Space_> && std::default_initializable<Space_>,
3487 "fixedStep(model, dt) binds to a fixed-size state space; over a space sized at run time, give "
3488 "FixedStepModel{model, space, dt} the space"
3530template <Space Space_,
typename Model_,
typename Method_ = methods::Linearized, RealScalar Scalar_ =
double>
3533 static_assert(
ProcessModel<Model_>,
"a fixed-step model is a process model taken over one step");
3534 static_assert(internal::checkPredictionMethod<Method_>());
3552 && internal::returnsStepJacobianIn<Model_, Space_, Scalar_>
3553 && internal::affineInEverything<Model_>();
3559 : m_model{std::move(model)}
3560 , m_method{std::move(method)}
3561 , m_timeStep{internal::toScalarTime<Scalar_>(dt)}
3562 , m_noise{internal::processNoise<Scalar_>(m_model, space, m_timeStep)}
3563 , m_noiseRoot{internal::defaultRoot(m_noise)}
3564 , m_transition{transitionOf(m_model, space, m_timeStep)}
3565 , m_transitionInverse{inverseOf(m_transition)}
3567 internal::assertPartitions(m_model, space);
3574 template <TimeStep<Scalar_> Time>
3582 template <
typename Space>
3583 [[nodiscard]]
static consteval internal::StaticPartition
3586 return internal::staticPartition<Model_, Space>();
3590 [[nodiscard]] Model_
const &
3597 [[nodiscard]] Method_
const &
3604 [[nodiscard]] Scalar_
3611 [[nodiscard]] MatrixType
const &
3618 [[nodiscard]] MatrixType
const &
3622 return m_transition;
3629 [[nodiscard]] MatrixType
const &
3638 [[nodiscard]] std::optional<MatrixType>
const &
3642 return m_transitionInverse;
3649 template <
typename Derived>
3651 propagate(Space_
const & space, Eigen::MatrixBase<Derived>
const & x)
const
3669 [[nodiscard]]
static auto
3671 [[maybe_unused]] Model_
const & model,
3672 [[maybe_unused]] Space_
const & space,
3673 [[maybe_unused]] Scalar_ dt
3676 if constexpr (CachesTransition)
3678 auto const call = internal::modelWith(std::cref(model));
3679 auto const resolved = internal::resolveProcess(call, space);
3680 return MatrixType{resolved.template transition<Scalar_>(dt)};
3684 return internal::NoTransition{};
3689 [[nodiscard]]
static auto
3690 inverseOf([[maybe_unused]]
auto const & transition)
3692 if constexpr (CachesTransition)
3694 auto const lu = Eigen::FullPivLU<MatrixType>{transition};
3695 return lu.isInvertible() ? std::optional<MatrixType>{internal::inverseFrom<MatrixType>(lu)} : std::nullopt;
3699 return internal::NoTransition{};
3707 MatrixType m_noiseRoot;
3715 std::conditional_t<CachesTransition, std::optional<MatrixType>, internal::NoTransition>
3716 m_transitionInverse;
3720template <
typename Model,
typename Space,
typename Time>
3721FixedStepModel(Model, Space, Time) -> FixedStepModel<Space, Model, methods::Linearized, double>;
3723template <
typename Model,
typename Space,
typename Time,
typename Method>
3724FixedStepModel(Model, Space, Time, Method) -> FixedStepModel<Space, Model, Method, double>;
3733template <ProcessModel Model,
typename Time,
typename Method = methods::Linearized>
3734 requires TimeStep<Time, double>
3735[[nodiscard]] UnboundFixedStep<Model, Method, Time>
3738 return UnboundFixedStep<Model, Method, Time>{std::move(model), std::move(dt), std::move(method)};
3745template <
typename Model,
typename Space,
typename Scalar>
3752template <
typename Model_,
typename Method_,
typename Time_,
typename Space,
typename Scalar>
3753struct BoundStepOf<UnboundFixedStep<Model_, Method_, Time_>, Space, Scalar>
3755 using type = FixedStepModel<Space, Model_, Method_, Scalar>;
3761template <
typename Model,
typename Space,
typename Scalar>
3762using BoundStep =
typename BoundStepOf<Model, Space, Scalar>::type;
3765template <
typename Model_,
typename Method_,
typename Time_>
3766struct DefaultMethodOf<UnboundFixedStep<Model_, Method_, Time_>>
3768 using type = Method_;
3780template <UncertainInput Prior,
typename Space_,
typename Model_,
typename Method_,
typename Scalar_>
3781 requires FixedStepModel<Space_, Model_, Method_, Scalar_>::CachesTransition
3782[[nodiscard]] OutputWithCross<Space_, Space_, JacobianForm, Scalar_>
3783predictByTransition(Prior
const & prior, FixedStepModel<Space_, Model_, Method_, Scalar_>
const & model)
3785 using MatrixType =
typename FixedStepModel<Space_, Model_, Method_, Scalar_>::MatrixType;
3786 auto const & space = prior.space();
3787 auto const & f = model.transition();
3788 typename Moments<Space_, Scalar_>::VectorType mean{model.propagate(space, prior.mean())};
3790 MatrixType cross = multiply(covarianceLowerOf(prior), f.transpose());
3791 MatrixType explained = multiply(f, cross);
3792 explained = (Scalar_(0.5) * (explained + explained.transpose())).eval();
3793 Eigen::Index
const n = explained.rows();
3795 return OutputWithCross<Space_, Space_, JacobianForm, Scalar_>{
3800 std::move(explained),
3801 MatrixType{model.noise()},
3802 MatrixType::Zero(n, n)
3820template <UncertainInput Prior,
typename Space_,
typename Model_,
typename Method_,
typename Scalar_,
typename... Args>
3821[[nodiscard]] GaussianParams<typename Prior::SpaceType, typename Prior::Scalar>
3824 static_assert(std::is_same_v<typename Prior::SpaceType, Space_>,
"the model must be over the prior's space");
3825 static_assert(std::is_same_v<typename Prior::Scalar, Scalar_>,
"the model's scalar must be the prior's");
3827 [[maybe_unused]]
auto const & space = prior.space();
3828 internal::assertPartitions(model, space);
3829 if constexpr (Model::CachesTransition &&
sizeof...(Args) == 0)
3831 return internal::predictByTransition(prior, model).output();
3835 return internal::withReference(
3836 [&](
auto const & reference,
auto const &... inputs) {
3837 auto const call = internal::modelWith(std::cref(model.
model()), inputs...);
3838 auto const resolved = internal::resolveProcess(call, space);
3839 return internal::predictResolved(
3860template <UncertainInput Prior,
typename Space_,
typename Model_,
typename Method_,
typename Scalar_,
typename... Args>
3861[[nodiscard]] PredictedJoint<typename Prior::SpaceType, typename Prior::Scalar>
3864 static_assert(std::is_same_v<typename Prior::SpaceType, Space_>,
"the model must be over the prior's space");
3865 static_assert(std::is_same_v<typename Prior::Scalar, Scalar_>,
"the model's scalar must be the prior's");
3867 [[maybe_unused]]
auto const & space = prior.space();
3868 internal::assertPartitions(model, space);
3869 if constexpr (Model::CachesTransition &&
sizeof...(Args) == 0)
3871 return internal::jointOf(prior, internal::predictByTransition(prior, model));
3875 return internal::withReference(
3876 [&](
auto const & reference,
auto const &... inputs) {
3877 auto const call = internal::modelWith(std::cref(model.
model()), inputs...);
3878 auto const resolved = internal::resolveProcess(call, space);
3879 return internal::predictJointWithNoise(
3897template <
typename Prior,
typename Model_,
typename Method_,
typename Time_>
3898[[nodiscard]] FixedStepModel<typename Prior::SpaceType, Model_, Method_, typename Prior::Scalar>
3899boundToPrior(Prior
const & prior, UnboundFixedStep<Model_, Method_, Time_>
const & step)
3901 return FixedStepModel<typename Prior::SpaceType, Model_, Method_, typename Prior::Scalar>{
3920template <UncertainInput Prior,
typename Model_,
typename Method_,
typename Time_,
typename... Args>
3921[[nodiscard]] GaussianParams<typename Prior::SpaceType, typename Prior::Scalar>
3924 return Emrys::predict(prior, internal::boundToPrior(prior, step), args...);
3933template <UncertainInput Prior,
typename Model_,
typename Method_,
typename Time_,
typename... Args>
3934[[nodiscard]] PredictedJoint<typename Prior::SpaceType, typename Prior::Scalar>
3946template <
typename Method,
typename Space,
typename Scalar, ProcessModel Model,
typename... Args>
3949 Method
const & method,
3950 GaussianParams<Space, Scalar> & state,
3951 Model
const & model,
3953 Args
const &... args
3957 [&](
auto const & reference,
auto const &... inputs) {
3958 auto const call = modelWith(std::cref(model), inputs...);
3959 auto const resolved = resolveProcess(call, state.space());
3960 using R = std::remove_cvref_t<
decltype(resolved)>;
3961 if constexpr (!isLinearizedAbout<std::remove_cvref_t<
decltype(reference)>> && predictsByJacobian<Method, R>)
3963 assertPartitions(model, state.space());
3964 predictInPlaceByJacobian(state, resolved, dt);
3989 GaussianParams<Space, Scalar> & state,
3990 FixedStepModel<Space_, Model_, Method_, Scalar_>
const & model,
3991 Args
const &... args
3994 using Model = FixedStepModel<Space_, Model_, Method_, Scalar_>;
3995 static_assert(std::is_same_v<Space, Space_>);
3996 static_assert(std::is_same_v<Scalar, Scalar_>);
3997 if constexpr (Model::CachesTransition &&
sizeof...(Args) == 0)
4004 [&](
auto const & reference,
auto const &... inputs) {
4005 auto const call = modelWith(std::cref(model.model()), inputs...);
4006 auto const resolved = resolveProcess(call, state.space());
4007 using R = std::remove_cvref_t<
decltype(resolved)>;
4008 if constexpr (!isLinearizedAbout<std::remove_cvref_t<
decltype(reference)>>
4009 && predictsByJacobian<Method_, R>)
4011 assertPartitions(model, state.space());
4012 predictInPlaceByJacobianWithNoise(state, resolved, model.timeStep(), [&](
auto & cov) {
4013 cov += model.noise();
Batched functions (decisions.md [batched-functions]): batched(), the mark that a function takes every...
Spaces built from other spaces (decisions.md [sizes-and-dimensions], [several-resizable-blocks]): Com...
How a function that stores a matrix argument takes it (decisions.md [forwarded-matrices]): the concep...
The Gaussian: its parameters GaussianParams, the Gaussian that evaluates the density,...
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
Measurement models and their observations (decisions.md [model-policies], [sensors],...
The transform methods, as policies made by their factories (decisions.md [policy-factories],...
Moments: the mean and covariance that a Gaussian carries and that a mixture or a sample set is moment...
Products of fixed-size matrices by Eigen's fixed-size product at every size: multiply() is by Eigen'...
The policies a model is built from, and the marks on a step's arguments (decisions....
How a model's reads() meet a state and a call, all internal (decisions.md [model-policies],...
Drawing samples: SamplesWithLogDensity and SampleWithLogDensity, the Gaussian draws drawSamples(),...
Solving by a factorization of a small matrix: transposedSolve() gives , the shape of a Kalman gain an...
The Space concept and what every continuous space shares (decisions.md [sizes-and-dimensions]): the t...
Subspaces, and growing and shrinking blocks (decisions.md [several-resizable-blocks]).
Tag paths and the index utilities (decisions.md [tag-addressing], [index-names]): TagPath and TagPath...
Time steps (decisions.md [time-steps]): TimeTraits, which converts a time type to a scalar,...
Process models side by side, as compose() puts them (decisions.md [model-policies],...
Definition ProcessModel.h:1804
typename WritesPolicy::OutputSpace OutputSpace
The output space: the Composite of the blocks written, in order.
Definition ProcessModel.h:1863
auto operator()(In const &in, Out const &out, X const &x, TimeScalar dt) const
Definition ProcessModel.h:1900
Policy< K > const & policy() const
Definition ProcessModel.h:1880
Composition const & function() const
Definition ProcessModel.h:1872
std::tuple< Models... > const & parts() const
Definition ProcessModel.h:1889
static constexpr bool HasFunction
a process model always has a function
Definition ProcessModel.h:1833
typename internal::AffineOf< ReadsPolicy, std::tuple< Models... > >::type AffinePolicy
The affineIn() of the whole.
Definition ProcessModel.h:1842
Moments< Out, Scalar >::MatrixType noiseOver(Out const &out, Scalar dt) const
Definition ProcessModel.h:1929
static consteval internal::StaticPartition staticPartition()
Definition ProcessModel.h:1945
std::conditional_t< K==internal::PolicyKind::reads, ReadsPolicy, std::conditional_t< K==internal::PolicyKind::writes, WritesPolicy, std::conditional_t< K==internal::PolicyKind::affineIn, AffinePolicy, internal::NoPolicy > > > Policy
The policy of kind K, or internal::NoPolicy.
Definition ProcessModel.h:1851
bool partitions(Space const &space) const
Definition ProcessModel.h:1953
internal::NoJacobians MarkType
The whole writes no Jacobian in a batch: a method that needs one differentiates it.
Definition ProcessModel.h:1910
typename WritesPolicy::PathList WritePaths
The paths of the blocks written, a TagPaths.
Definition ProcessModel.h:1860
static constexpr bool has
Whether the policy of kind K is one the whole has: reads(), writes(), and affineIn().
Definition ProcessModel.h:1846
typename internal::FirstScalarOr< internal::DeclaredScalarsOf< Models... > >::type Scalar
the scalar type: the one its parts state, or double where none states one
Definition ProcessModel.h:1828
static constexpr bool TakesBatches
whether the whole takes a batch: a part's function does (decisions.md [batched-functions])
Definition ProcessModel.h:1907
typename internal::ReadUnionOf< AllItems >::type ReadsPolicy
The reads() of the whole: the union of the parts', in order of first appearance.
Definition ProcessModel.h:1836
Composition(Models... models)
The composition of models.
Definition ProcessModel.h:1866
static constexpr bool IsModel
a model, for internal::isModel
Definition ProcessModel.h:1832
typename internal::WritesOfTuple< AllPaths, AllNames >::type WritesPolicy
The writes() of the whole: the parts', in order.
Definition ProcessModel.h:1839
void operator()(In const &in, Out const &out, X const &x, TimeScalar dt, Y &y) const
Writes into y the whole's step of a batch x, a column per point in the input space in,...
Definition ProcessModel.h:1918
Composition FunctionType
the function: the composition itself
Definition ProcessModel.h:1830
Constant()
The model.
Definition ProcessModel.h:1285
A process model over one fixed time step, with the work that depends only on the step done once.
Definition ProcessModel.h:3532
Space_ SpaceType
the state space
Definition ProcessModel.h:3539
Model_ const & model() const
Definition ProcessModel.h:3591
typename Moments< Space_, Scalar_ >::MatrixType MatrixType
and , over differences
Definition ProcessModel.h:3542
Model_ ModelType
the process model taken over the step
Definition ProcessModel.h:3540
MatrixType const & noise() const
Definition ProcessModel.h:3612
static consteval internal::StaticPartition staticPartition()
Definition ProcessModel.h:3584
FixedStepModel(UnboundFixedStep< Model_, Method_, Time > const &step)
step bound to the space Space_, which is fixed-size and so has one instance: how a filter given fixed...
Definition ProcessModel.h:3575
MatrixType const & noiseRoot() const
Definition ProcessModel.h:3630
FixedStepModel(Model_ model, Space_ const &space, TimeStep< Scalar_ > auto const &dt, Method_ method={})
model taken over the step dt in space, predicting by method.
Definition ProcessModel.h:3558
bool partitions(Space_ const &space) const
Definition ProcessModel.h:3659
Scalar_ timeStep() const
Definition ProcessModel.h:3605
static constexpr bool ReadsOnlyState
Definition ProcessModel.h:3545
Method_ const & method() const
Definition ProcessModel.h:3598
std::optional< MatrixType > const & transitionInverse() const
Definition ProcessModel.h:3639
Scalar_ Scalar
the scalar type
Definition ProcessModel.h:3538
Method_ MethodType
the prediction method
Definition ProcessModel.h:3541
MatrixType const & transition() const
Definition ProcessModel.h:3619
static constexpr bool CachesTransition
Whether the model caches the transition: under Linearized, for a model that is affine in everything i...
Definition ProcessModel.h:3551
Moments< Space_, typenameDerived::Scalar >::VectorType propagate(Space_ const &space, Eigen::MatrixBase< Derived > const &x) const
Definition ProcessModel.h:3651
A Gaussian distribution: a mean and covariance in a space.
Definition Gaussian.h:259
typename MomentsType::MatrixType MatrixType
Definition Gaussian.h:267
A chain of two or more derivatives whose last one is driven by white noise: constant velocity and con...
Definition ProcessModel.h:1074
Kinematic(Scalar_ spectralDensity)
Spectral density spectralDensity on every axis, independently.
Definition ProcessModel.h:1125
Eigen::Matrix< Scalar_, Dimension, Dimension > SpectralDensityMatrix
The spectral density's type: over the last derivative's coordinates.
Definition ProcessModel.h:1104
SpectralDensityMatrix const & spectralDensity() const
Definition ProcessModel.h:1142
Scalar_ Scalar
the scalar type of the spectral density
Definition ProcessModel.h:1101
static constexpr int Dimension
Definition ProcessModel.h:1086
Kinematic(SpectralDensityMatrix const &spectralDensity)
Spectral density matrix spectralDensity.
Definition ProcessModel.h:1130
static constexpr int Order
Definition ProcessModel.h:1083
DifferenceMatrix< Space_, Space_, Scalar > MatrixType
a matrix over differences, such as the covariance
Definition Moments.h:76
ValueVector< Space_, Scalar > VectorType
a value, such as the mean
Definition Moments.h:74
Scalar_ Scalar
the scalar type of the parameters
Definition ProcessModel.h:1229
Scalar_ timeConstant() const
Definition ProcessModel.h:1249
Scalar_ variance() const
Definition ProcessModel.h:1256
OrnsteinUhlenbeck(TimeConstant< Scalar_ > timeConstant, StationaryVariance< Scalar_ > variance)
Time constant timeConstant and stationary variance variance.
Definition ProcessModel.h:1232
A process model: a function of its input and the step, with the policies that say what it reads,...
Definition ProcessModel.h:518
Policy< K > const & policy() const
Definition ProcessModel.h:568
std::tuple< Policies... > const & policies() const
Definition ProcessModel.h:575
ProcessFunction(F function, Policies... policies)
The model of function with policies.
Definition ProcessModel.h:553
F FunctionType
the function
Definition ProcessModel.h:525
bool partitions(Space const &space) const
Definition ProcessModel.h:645
typename internal::ProcessWrites< Whole, Policy< internal::PolicyKind::writes > >::OutputSpace OutputSpace
The output space: the Composite of the blocks written, in order; for a whole-state model,...
Definition ProcessModel.h:550
static constexpr bool Whole
whether the model reads and writes the whole state (decisions.md [whole-state-models])
Definition ProcessModel.h:542
static constexpr bool HasFunction
a process model always has a function
Definition ProcessModel.h:528
F const & function() const
Definition ProcessModel.h:560
typename Set::template Type< K > Policy
The policy of kind K, or internal::NoPolicy.
Definition ProcessModel.h:536
static constexpr bool has
Whether the policy of kind K was given.
Definition ProcessModel.h:532
typename internal::ProcessWrites< Whole, Policy< internal::PolicyKind::writes > >::Paths WritePaths
The paths of the blocks written, a TagPaths; the whole space for a whole-state model.
Definition ProcessModel.h:545
static constexpr bool IsModel
a model, for internal::isModel
Definition ProcessModel.h:527
static consteval internal::StaticPartition staticPartition()
Definition ProcessModel.h:628
Moments< Out, Scalar >::MatrixType noiseOver(Out const &out, Scalar dt) const
Definition ProcessModel.h:586
SpectralDensityMatrix const & spectralDensity() const
Definition ProcessModel.h:1027
Eigen::Matrix< Scalar_, Dimension, Dimension > SpectralDensityMatrix
The spectral density's type: over the block's differences.
Definition ProcessModel.h:1007
RandomWalk(SpectralDensityMatrix const &spectralDensity)
Spectral density matrix spectralDensity.
Definition ProcessModel.h:1015
static constexpr int Dimension
Dimension of the block's differences.
Definition ProcessModel.h:1001
RandomWalk(Scalar_ spectralDensity)
Spectral density spectralDensity on every axis, independently.
Definition ProcessModel.h:1010
Scalar_ Scalar
the scalar type of the spectral density
Definition ProcessModel.h:1004
A process model taken over one fixed time step, not yet bound to a state space: what fixedStep() retu...
Definition ProcessModel.h:3415
Time_ TimeType
the time step's type
Definition ProcessModel.h:3420
Method_ const & method() const
Definition ProcessModel.h:3438
UnboundFixedStep(Model_ model, Time_ dt, Method_ method)
model taken over the step dt, predicting by method
Definition ProcessModel.h:3423
Time_ const & timeStep() const
Definition ProcessModel.h:3445
static consteval internal::StaticPartition staticPartition()
Definition ProcessModel.h:3463
Model_ const & model() const
Definition ProcessModel.h:3431
bool partitions(Space const &space) const
Definition ProcessModel.h:3453
Method_ MethodType
the prediction method
Definition ProcessModel.h:3419
Model_ ModelType
the process model taken over the step
Definition ProcessModel.h:3418
A dense matrix or matrix expression, possibly cv- or reference-qualified: what a const Eigen::MatrixB...
Definition Forwarding.h:39
A process model: what processModel() returns, a built-in model, or a composition of process models (d...
Definition ProcessModel.h:2010
A type usable as the space of a distribution.
Definition Space.h:186
A step of time that converts to Scalar: Scalar itself, or any type with a TimeTraits,...
Definition Time.h:128
#define EMRYS_FILTER_NO_UNIQUE_ADDRESS
Assertion used for programmer errors (misuse of the API).
Definition Macros.h:38
CategoricalParams< Space, Scalar > predict(CategoricalParams< Space, Scalar > const &prior, MarkovChain< Space, Scalar > const &chain)
Definition Categorical.h:1049
typename std::remove_cvref_t< X >::Scalar ScalarOf
The scalar type of the Eigen object or distribution X, for writing functions generic in it.
Definition Moments.h:26
UnboundFixedStep< Model, Method, Time > fixedStep(Model model, Time dt, Method method={})
Definition ProcessModel.h:3736
typename internal::DefaultMethodOf< Model >::type DefaultMethodFor
The prediction method of a filter with process model Model, unless it is given one: Linearized,...
Definition ProcessModel.h:3220
constexpr bool isFixedStep
Whether Model is a FixedStepModel, whose predictions take no time step.
Definition ProcessModel.h:2776
Kinematic< Scalar, Position, Velocity > ConstantVelocity
Constant velocity (white-noise acceleration): position moves by velocity times .
Definition ProcessModel.h:1152
Moments< Space_, typenameDerived::Scalar >::VectorType propagate(Space_ const &space, Eigen::MatrixBase< Derived > const &x, Model const &model, TimeStep< typename Derived::Scalar > auto const &dt, Args const &... args)
Definition ProcessModel.h:2754
PredictedJoint< typename Prior::SpaceType, typename Prior::Scalar > predictJoint(Method const &method, Prior const &prior, Model const &model, TimeStep< typename Prior::Scalar > auto const &time, Args const &... args)
Definition ProcessModel.h:3362
auto processModel(F &&function, Policies &&... policies)
Definition ProcessModel.h:702
auto compose(Models &&... models)
Definition Placement.h:1243
std::conditional_t< isFixedStep< Model >, ModelsMethod, Method > MethodFor
The type of a filter constructor's method argument: Method, or ModelsMethod for a fixed-step Model,...
Definition ProcessModel.h:3236
Kinematic< Scalar, Position, Velocity, Acceleration > ConstantAcceleration
Constant acceleration (white-noise jerk).
Definition ProcessModel.h:1158
constexpr bool isUnboundStep
Whether Model is a fixed step not yet bound to a state space: an UnboundFixedStep,...
Definition ProcessModel.h:2792
Eigen::Index valueDimension(S const &space)
Definition Space.h:272
auto block(Space auto const &space, X &&x)
Definition TagPath.h:979
Eigen::Index differenceDimension(S const &space)
Definition Space.h:284
constexpr int valueDimensionAtCompileTime
The value dimension of Space at compile time, or Eigen::Dynamic if it is resizable.
Definition Space.h:251
constexpr int maxValueDimensionAtCompileTime
The largest valid value dimension of Space, or Eigen::Dynamic if it is unbounded.
Definition Space.h:258
void normalizeValues(S const &space, Eigen::MatrixBase< Derived > const &block)
Brings the values in block (rows are space's coordinates, columns samples) into canonical form,...
Definition Space.h:331
The input names a model's function is affine in, given the rest, as affineIn() declares them (decisio...
Definition Policies.h:278
What a filter with a fixed-step model takes in place of a prediction method: nothing,...
Definition ProcessModel.h:3227
The process model of a state with nothing to move: what a PointMassFilter, whose grid covers the whol...
Definition ProcessModel.h:2019
static consteval internal::StaticPartition staticPartition()
Definition ProcessModel.h:2023
bool partitions(Space const &space) const
Definition ProcessModel.h:2039
One prediction step as a joint: the Gaussian the step started from, the Gaussian it predicted,...
Definition ProcessModel.h:2817
MatrixType cross
Definition ProcessModel.h:2829
typename GaussianType::MatrixType MatrixType
Definition ProcessModel.h:2821
GaussianType predicted
Definition ProcessModel.h:2824
GaussianParams< Space_, Scalar_ > GaussianType
Definition ProcessModel.h:2820
GaussianType filtered
Definition ProcessModel.h:2823
Scalar_ Scalar
Definition ProcessModel.h:2818
Space_ SpaceType
Definition ProcessModel.h:2819
The stationary variance of an OrnsteinUhlenbeck model, named so that it cannot be given in the place...
Definition ProcessModel.h:1176
Scalar value
, nonnegative
Definition ProcessModel.h:1177
The time constant of an OrnsteinUhlenbeck model, named so that it cannot be given in the place of it...
Definition ProcessModel.h:1166
Scalar value
, positive, in the unit of the time steps
Definition ProcessModel.h:1167
Composite< internal::PathTag< Paths >... > OutputSpace
The output space: the Composite of the blocks, in order.
Definition Policies.h:227
TagPaths< Paths... > PathList
The paths, as a TagPaths.
Definition Policies.h:230