EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
HiddenMarkovFilter.h
Go to the documentation of this file.
1#pragma once
2
9
10#include <expected>
11#include <type_traits>
12#include <utility>
13
24
25namespace Emrys
26{
27
28namespace internal
29{
30
32template <typename Chain>
33inline constexpr bool IsContinuousTimeChain = false;
34
36template <typename Space, typename Scalar>
37inline constexpr bool IsContinuousTimeChain<ContinuousTimeMarkovChain<Space, Scalar>> = true;
38
39} // namespace internal
40
56template <DiscreteSpace Space, RealScalar Scalar>
65
74template <typename Scalar = double>
76{
77public:
78
80 [[nodiscard]] Scalar
82 {
83 return m_logLikelihood;
84 }
85
86 bool accepted = false;
87
88private:
89
90 friend struct internal::UpdateAccess;
91
92 Scalar m_logLikelihood = 0;
93};
94
129template <
130 DiscreteSpace Space_,
131 typename Chain_ = MarkovChain<Space_>,
132 RealScalar Scalar_ = double,
133 typename Upkeep_ = upkeep::None>
134class HiddenMarkovFilter : public internal::HeldUpkeep<HiddenMarkovFilter<Space_, Chain_, Scalar_, Upkeep_>, Upkeep_>
135{
136 using UpkeepBase = internal::HeldUpkeep<HiddenMarkovFilter, Upkeep_>;
137
138 static_assert(
139 std::is_same_v<typename Chain_::SpaceType, Space_> && std::is_same_v<typename Chain_::Scalar, Scalar_>,
140 "the chain must be on the filter's space, in its scalar"
141 );
142
143public:
144
145 using Scalar = Scalar_;
146 using SpaceType = Space_;
147 using ChainType = Chain_;
149
153 HiddenMarkovFilter(Chain_ chain, StateType state, std::type_identity_t<Upkeep_> upkeep = {})
154 : UpkeepBase{std::move(upkeep)}
155 , m_chain{std::move(chain)}
156 , m_state{std::move(state)}
157 {
158 EMRYS_FILTER_ASSERT(m_chain.space() == m_state.space() && "the chain is on a different space");
159 }
160
162 explicit HiddenMarkovFilter(Chain_ chain)
163 : UpkeepBase{Upkeep_{}}
164 , m_chain{std::move(chain)}
165 , m_state{m_chain.space()}
166 {}
167
169 [[nodiscard]] StateType const &
170 state() const
171 {
172 return m_state;
173 }
174
179 [[nodiscard]] StateType &
181 {
182 return m_state;
183 }
184
188 void
190 {
191 EMRYS_FILTER_ASSERT(m_chain.space() == state.space() && "the state is on a different space");
192 m_state = std::move(state);
193 }
194
196 [[nodiscard]] Chain_ const &
197 chain() const
198 {
199 return m_chain;
200 }
201
203 [[nodiscard]] Chain_ &
205 {
206 return m_chain;
207 }
208
212 [[nodiscard]] Scalar
214 {
215 return m_logEvidence;
216 }
217
219 void
221 {
222 m_logEvidence = Scalar(0);
223 }
224
226 void
228 requires(!internal::IsContinuousTimeChain<Chain_>)
229 {
230 m_state = Emrys::predict(m_state, m_chain);
231 }
232
236 void
237 predict(TimeStep<Scalar> auto const & dt)
238 requires internal::IsContinuousTimeChain<Chain_>
239 {
240 m_state = Emrys::predict(m_state, m_chain, dt);
241 }
242
250 [[nodiscard]] std::expected<Scalar, WeightError>
252 requires internal::IsContinuousTimeChain<Chain_>
253 {
254 return apply(Emrys::predict(m_state, m_chain, dt, interval));
255 }
256
262 requires(!internal::IsContinuousTimeChain<Chain_>)
263 {
264 auto predicted = Emrys::predict(m_state, m_chain);
265 auto joint = CategoricalPredictedJoint<Space_, Scalar_>{std::move(m_state), predicted, m_chain.matrix()};
266 m_state = std::move(predicted);
267 return joint;
268 }
269
275 requires internal::IsContinuousTimeChain<Chain_>
276 {
277 auto step = m_chain.transition(dt);
278 auto predicted = Emrys::predict(m_state, step);
279 auto joint = CategoricalPredictedJoint<Space_, Scalar_>{std::move(m_state), predicted, step.matrix()};
280 m_state = std::move(predicted);
281 return joint;
282 }
283
290 [[nodiscard]] std::expected<CategoricalPredictedJoint<Space_, Scalar_>, WeightError>
292 requires internal::IsContinuousTimeChain<Chain_>
293 {
294 auto step = internal::arrivalKernel(m_chain, internal::toScalarTime<Scalar>(dt), interval);
295 auto moved = internal::moveByKernel(m_state, step.kernel, step.logScale);
296 if (!moved)
297 {
298 return std::unexpected{moved.error()};
299 }
300 // Divide the kernel by the step's total, so that it moves the filtered state onto the
301 // normalized prediction exactly.
302 step.kernel /= Eigen::numext::exp(Scalar(moved->logEvidence - step.logScale));
303 auto joint =
304 CategoricalPredictedJoint<Space_, Scalar_>{std::move(m_state), moved->posterior, std::move(step.kernel)};
305 m_state = std::move(moved->posterior);
306 m_logEvidence += moved->logEvidence;
307 return joint;
308 }
309
323 template <EmissionModel<Space_> E>
324 [[nodiscard]] std::expected<HiddenMarkovFilterUpdate<Scalar_>, WeightError>
325 update(E const & emission)
326 {
327 auto const prior = m_state.logTotalWeight();
328 if (!prior)
329 {
330 return std::unexpected{prior.error()};
331 }
332 auto const evidence = m_state.update(internal::emissionLogLikelihoods(m_state, emission));
333 auto result = HiddenMarkovFilterUpdate<Scalar_>{};
334 if (!evidence)
335 {
336 // The prior is valid, so a sum of zero is the measurement's: no value explains it.
337 if (evidence.error() != WeightError::ZeroSum)
338 {
339 return std::unexpected{evidence.error()};
340 }
341 internal::UpdateAccess::logLikelihood(result) = -Eigen::NumTraits<Scalar_>::infinity();
342 m_logEvidence = -Eigen::NumTraits<Scalar_>::infinity();
343 return result;
344 }
345 internal::UpdateAccess::logLikelihood(result) = *evidence;
346 result.accepted = true;
347 m_logEvidence += *evidence;
348 return result;
349 }
350
351private:
352
354 [[nodiscard]] std::expected<Scalar, WeightError>
355 apply(std::expected<CategoricalUpdate<Space_, Scalar_>, WeightError> result)
356 {
357 if (!result)
358 {
359 return std::unexpected{result.error()};
360 }
361 m_state = std::move(result->posterior);
362 m_logEvidence += result->logEvidence;
363 return result->logEvidence;
364 }
365
366 Chain_ m_chain;
367 StateType m_state;
368 Scalar m_logEvidence = 0;
369};
370
372template <typename Chain>
375
376template <typename Chain>
378
379template <typename Chain, typename U>
382
384
385} // namespace Emrys
386
387#ifndef EMRYS_FILTER_NO_IO
388
389namespace Emrys
390{
391
392namespace internal
393{
394
396template <typename Space_, typename Chain_, typename Scalar_, typename Upkeep_>
397void
398describe(std::ostream & stream, HiddenMarkovFilter<Space_, Chain_, Scalar_, Upkeep_> const & filter, Indent indent)
399{
400 describeFilter(stream, "hidden Markov filter", filter.state(), indent);
401}
402
403} // namespace internal
404
405} // namespace Emrys
406
407#endif // EMRYS_FILTER_NO_IO
The distribution over a discrete space (decisions.md [discrete-spaces], [params-and-density]): Catego...
The continuous-time chain on a discrete space: ContinuousTimeMarkovChain, built from a generator of r...
Discrete spaces (decisions.md [discrete-spaces]): the DiscreteSpace concept; Finite,...
The measurement of a discrete state: the EmissionModel concept, which fills one log-likelihood per va...
The upkeep a filter holds (decisions.md [upkeep]): internal::HeldUpkeep, the base that gives every fi...
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
Printing: operator<< and std::formatter for every distribution, every filter (which prints its name a...
The Recursion concept (decisions.md [filters-and-recursions], [user-extension-points]): what a Kalman...
Log-weights: the WeightError a normalization can fail with (NumericError.h), and the streaming log-su...
The observation of a Markov-modulated Poisson process over an interval: no event inside it,...
Definition Emission.h:502
The parameters of a probability distribution over a discrete space: one weight per value.
Definition Categorical.h:257
Space_ const & space() const
Definition Categorical.h:330
What a hidden Markov filter's update did: the measurement's log-evidence under the prior,...
Definition HiddenMarkovFilter.h:76
bool accepted
whether some value with a nonzero probability could explain the measurement
Definition HiddenMarkovFilter.h:86
Scalar logLikelihood() const
Definition HiddenMarkovFilter.h:81
A hidden Markov model filter: a CategoricalParams state, the chain that moves it, and the log-evidenc...
Definition HiddenMarkovFilter.h:135
void setState(StateType state)
Replaces the state estimate with one on the chain's space, as the constructor's must be,...
Definition HiddenMarkovFilter.h:189
CategoricalPredictedJoint< Space_, Scalar_ > predictJoint()
Moves the state one step of a MarkovChain, as predict(), and returns the step's joint for a Categoric...
Definition HiddenMarkovFilter.h:261
HiddenMarkovFilter(Chain_ chain, StateType state, std::type_identity_t< Upkeep_ > upkeep={})
The filter moved by chain, starting from state, with upkeep upkeep, which maintain() runs.
Definition HiddenMarkovFilter.h:153
Chain_ ChainType
the chain
Definition HiddenMarkovFilter.h:147
void predict()
Moves the state one step of a MarkovChain: .
Definition HiddenMarkovFilter.h:227
void resetLogEvidence()
Sets the log-evidence back to zero, to start a new record from the current state.
Definition HiddenMarkovFilter.h:220
CategoricalParams< Space_, Scalar_ > StateType
the state the filter owns
Definition HiddenMarkovFilter.h:148
Space_ SpaceType
the discrete space
Definition HiddenMarkovFilter.h:146
Scalar logEvidence() const
Definition HiddenMarkovFilter.h:213
StateType & state()
Definition HiddenMarkovFilter.h:180
CategoricalPredictedJoint< Space_, Scalar_ > predictJoint(TimeStep< Scalar > auto const &dt)
Moves the state over an interval of dt of a ContinuousTimeMarkovChain, as predict(dt),...
Definition HiddenMarkovFilter.h:274
Chain_ const & chain() const
Definition HiddenMarkovFilter.h:197
StateType const & state() const
Definition HiddenMarkovFilter.h:170
Scalar_ Scalar
the scalar type of the probabilities
Definition HiddenMarkovFilter.h:145
std::expected< Scalar, WeightError > predict(TimeStep< Scalar > auto const &dt, ArrivalInterval< Space_, Scalar_ > const &interval)
Moves the state over an interval of dt of a ContinuousTimeMarkovChain while observing interval of a M...
Definition HiddenMarkovFilter.h:251
std::expected< CategoricalPredictedJoint< Space_, Scalar_ >, WeightError > predictJoint(TimeStep< Scalar > auto const &dt, ArrivalInterval< Space_, Scalar_ > const &interval)
Moves the state over an interval of dt while observing interval, as predict(dt, interval),...
Definition HiddenMarkovFilter.h:291
std::expected< HiddenMarkovFilterUpdate< Scalar_ >, WeightError > update(E const &emission)
Updates the state by emission, by Bayes' rule, in place, and adds the measurement's log-evidence to t...
Definition HiddenMarkovFilter.h:325
void predict(TimeStep< Scalar > auto const &dt)
Moves the state over an interval of dt, any TimeStep (see Time steps), of a ContinuousTimeMarkovChain...
Definition HiddenMarkovFilter.h:237
Chain_ & chain()
Definition HiddenMarkovFilter.h:204
HiddenMarkovFilter(Chain_ chain)
The filter moved by chain, starting from the uniform distribution over its space.
Definition HiddenMarkovFilter.h:162
A Markov chain on a discrete space: the probability of moving from each value to each value in one st...
Definition Categorical.h:829
Eigen::Matrix< Scalar_, CardinalityAtCompileTime, CardinalityAtCompileTime, Eigen::RowMajor, MaxCardinalityAtCompileTime, MaxCardinalityAtCompileTime > MatrixType
The transition matrix's type: a row per value moved from, a column per value moved to.
Definition Categorical.h:838
A type usable as a discrete space: a finite set of values.
Definition Discrete.h:72
A type usable as the scalar of a distribution: a real number type such as float, double,...
Definition Math.h:80
A step of time that converts to Scalar: Scalar itself, or any type with a TimeTraits,...
Definition Time.h:128
NumericError WeightError
Reasons a collection of log-weights cannot be normalized: Empty, ZeroSum, or NonFinite of NumericErro...
Definition NumericError.h:53
@ ZeroSum
every weight is zero (every log-weight is -inf)
Definition NumericError.h:35
CategoricalParams< Space, Scalar > predict(CategoricalParams< Space, Scalar > const &prior, MarkovChain< Space, Scalar > const &chain)
Definition Categorical.h:1049
The upkeep operations, each made by its factory (decisions.md [policy-factories], [upkeep]): the acti...
Definition Upkeep.h:380
One prediction of a HiddenMarkovFilter as a smoother needs it: the distribution the step started from...
Definition HiddenMarkovFilter.h:58
CategoricalParams< Space, Scalar > filtered
Definition HiddenMarkovFilter.h:61
MatrixType transitions
Definition HiddenMarkovFilter.h:63
CategoricalParams< Space, Scalar > predicted
Definition HiddenMarkovFilter.h:62
typename MarkovChain< Space, Scalar >::MatrixType MatrixType
the transitions' type
Definition HiddenMarkovFilter.h:59
The result of updating a CategoricalParams by an emission model: the posterior, whose weights sum to ...
Definition Emission.h:66
The upkeep a filter holds when it is given none: it takes any state, changes nothing,...
Definition Upkeep.h:889