36template <DiscreteSpace Space,
typename Scalar>
37using EmissionBuffer = Eigen::Ref<Eigen::Array<Scalar, Space::Cardinality, 1>>;
49template <
typename E,
typename Space>
54 emission.logLikelihoods(space, out);
64template <DiscreteSpace Space, RealScalar Scalar>
77template <
typename Scalar,
typename Space, EmissionModel<Space> E>
95template <
typename Scalar,
typename Space, EmissionModel<Space> E>
96[[nodiscard]] std::expected<CategoricalUpdate<Space, Scalar>,
WeightError>
100 auto const evidence = result.
posterior.
update(internal::emissionLogLikelihoods(prior, emission));
103 return std::unexpected{evidence.error()};
105 result.logEvidence = *evidence;
129template <DiscreteSpace Space_, DiscreteSpace ObservationSpace_, RealScalar Scalar_ =
double>
142 ObservationSpace_::Cardinality,
144 Space_::MaxCardinality,
145 ObservationSpace_::MaxCardinality>;
163 observed >= 0 &&
observed < table.m_matrix.cols() &&
"the observed value is out of range"
168 [[nodiscard]] Eigen::Index
178 EMRYS_FILTER_ASSERT(
space == m_table->m_space &&
"the table is over a different space");
179 EMRYS_FILTER_ASSERT(out.size() == m_table->m_matrix.rows());
180 out = m_table->m_matrix.col(m_observed).array().log();
186 Eigen::Index m_observed;
192 template <MatrixExpression Matrix>
194 : m_space{std::move(
space)}
195 , m_observations{std::move(observations)}
196 , m_matrix{internal::castScalar<
Scalar>(std::forward<Matrix>(
matrix))}
199 m_matrix.rows() == m_space.cardinality() && m_matrix.cols() == m_observations.cardinality()
200 &&
"the emission matrix has a row per value and a column per observed value"
202 EMRYS_FILTER_ASSERT((m_matrix.array() >=
Scalar(0)).all() &&
"emission probabilities must be nonnegative");
204 ((m_matrix.rowwise().sum().array() -
Scalar(1)).abs() <= Eigen::NumTraits<Scalar>::dummy_precision()).all()
205 &&
"each row of the emission matrix must sum to one"
210 [[nodiscard]] Space_
const &
217 [[nodiscard]] ObservationSpace_
const &
220 return m_observations;
231 [[nodiscard]] Observation
241 observe(Eigen::Index observed)
const && =
delete;
246 ObservationSpace_ m_observations;
251template <DiscreteSpace Space, DiscreteSpace ObservationSpace, MatrixExpression Matrix>
274template <
typename Observation,
typename DistributionOf>
280 using Scalar = std::remove_cvref_t<decltype(std::declval<DistributionOf const &>()(Eigen::Index{})
281 .logDensity(std::declval<Observation const &>()))>;
286 , m_distributionOf{std::move(distributionOf)}
290 [[nodiscard]] Observation
const &
293 return m_observation;
297 template <DiscreteSpace Space>
301 EMRYS_FILTER_ASSERT(out.size() == space.cardinality());
302 for (Eigen::Index v = 0; v < out.size(); ++v)
304 out(v) = m_distributionOf(v).logDensity(m_observation);
310 Observation m_observation;
311 DistributionOf m_distributionOf;
320template <DiscreteSpace Space, RealScalar Scalar>
329 template <
typename Derived>
330 explicit ValueRates(Eigen::DenseBase<Derived>
const & rates)
331 : m_rates{rates.derived().array().template cast<Scalar>()}
333 EMRYS_FILTER_ASSERT((m_rates >= Scalar(0)).all() &&
"rates must be nonnegative");
337 [[nodiscard]] ArrayType
const &
365template <DiscreteSpace Space, RealScalar Scalar_ =
double>
376 template <
typename Derived, std::
integral Count>
377 requires(!std::same_as<Count, bool>)
379 : internal::ValueRates<Space, Scalar_>{rates}
380 , m_count{Eigen::Index(
count)}
381 , m_dt{internal::toScalarTime<Scalar_>(dt)}
383 EMRYS_FILTER_ASSERT(
count >= 0 &&
"the count must be nonnegative");
384 EMRYS_FILTER_ASSERT(m_dt > 0 &&
"the interval must be positive");
388 [[nodiscard]] Eigen::Index
395 [[nodiscard]] Scalar_
405 auto const & rates = this->rates();
406 EMRYS_FILTER_ASSERT(out.size() == space.cardinality() && rates.size() == out.size());
409 for (Eigen::Index v = 0; v < out.size(); ++v)
411 Scalar const mean = rates(v) * m_dt;
414 out(v) = m_count == 0 ?
Scalar(0) : -Eigen::NumTraits<Scalar>::infinity();
418 out(v) = n * Eigen::numext::log(mean) - mean - logFactorial;
425 Eigen::Index m_count;
441template <DiscreteSpace Space, RealScalar Scalar_ =
double>
451 template <
typename Derived>
453 : internal::ValueRates<
Space, Scalar_>{rates}
454 , m_tau{internal::toScalarTime<Scalar_>(tau)}
458 [[nodiscard]] Scalar_
468 auto const & rates = this->rates();
469 EMRYS_FILTER_ASSERT(out.size() == space.cardinality() && rates.size() == out.size());
470 for (Eigen::Index v = 0; v < out.size(); ++v)
472 out(v) = Eigen::numext::log(rates(v)) - rates(v) * m_tau;
500template <DiscreteSpace Space, RealScalar Scalar_ =
double>
508 template <
typename Derived>
510 : internal::ValueRates<
Space, Scalar_>{rates}
518 return m_endsInArrival;
523 bool m_endsInArrival;
536template <
typename Space,
typename Scalar>
547template <
typename Space,
typename Scalar>
548[[nodiscard]] ArrivalKernel<Space, Scalar>
550 ContinuousTimeMarkovChain<Space, Scalar>
const & chain,
552 ArrivalInterval<Space, Scalar>
const & interval
555 EMRYS_FILTER_ASSERT(dt >= 0 &&
"the interval must be nonnegative");
557 auto const & rates = interval.rates();
558 EMRYS_FILTER_ASSERT(rates.size() == chain.space().cardinality() &&
"give one rate per value");
559 Scalar
const shift = rates.minCoeff();
560 MatrixType exponent = chain.generator() * dt;
561 exponent.diagonal().array() -= (rates - shift) * dt;
562 MatrixType kernel = exponent.exp();
563 kernel = kernel.cwiseMax(Scalar(0));
564 if (interval.endsInArrival())
566 kernel = kernel * rates.matrix().asDiagonal();
568 return {std::move(kernel), -shift * dt};
575template <
typename Space,
typename Scalar,
typename Kernel>
576[[nodiscard]] std::expected<CategoricalUpdate<Space, Scalar>,
WeightError>
577moveByKernel(CategoricalParams<Space, Scalar>
const & prior, Kernel
const & kernel, Scalar logScale)
580 auto const priorTotal = prior.logTotalWeight();
583 return std::unexpected{priorTotal.error()};
585 ArrayType
const logMoved = movedLogWeights<Scalar>(prior.logWeights(), kernel);
586 auto const total = logTotalWeight<Scalar>(logMoved);
589 return std::unexpected{total.error()};
591 return CategoricalUpdate<Space, Scalar>{
592 CategoricalParams<Space, Scalar>{prior.space(), logMoved - *total},
593 *total - *priorTotal + logScale
609template <
typename Scalar,
typename Space>
610[[nodiscard]] std::expected<CategoricalUpdate<Space, Scalar>,
WeightError>
618 EMRYS_FILTER_ASSERT(prior.
space() == chain.
space() &&
"the chain is on a different space");
619 auto const step = internal::arrivalKernel(chain, internal::toScalarTime<Scalar>(dt), interval);
620 return internal::moveByKernel(prior, step.kernel, step.logScale);
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,...
How a function that stores a matrix argument takes it (decisions.md [forwarded-matrices]): the concep...
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
Products of fixed-size matrices by Eigen's fixed-size product at every size: multiply() is by Eigen'...
The Space concept and what every continuous space shares (decisions.md [sizes-and-dimensions]): the t...
Time steps (decisions.md [time-steps]): TimeTraits, which converts a time type to a scalar,...
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
ArrivalInterval(Eigen::DenseBase< Derived > const &rates, bool endsInArrival=true)
The interval with rates one per value, ending in an event if endsInArrival.
Definition Emission.h:509
Scalar_ Scalar
the scalar type of the rates
Definition Emission.h:505
bool endsInArrival() const
Definition Emission.h:516
The parameters of a probability distribution over a discrete space: one weight per value.
Definition Categorical.h:257
std::expected< Scalar, WeightError > update(Eigen::DenseBase< Derived > const &logLikelihoods)
Bayes' rule: multiplies each value's probability by its likelihood , with logLikelihoods one per val...
Definition Categorical.h:444
Space_ const & space() const
Definition Categorical.h:330
Eigen::Array< Scalar_, CardinalityAtCompileTime, 1, Eigen::ColMajor, MaxCardinalityAtCompileTime, 1 > ArrayType
One entry per value, in flat-index order.
Definition Categorical.h:268
A continuous-time Markov chain on a discrete space: the rate of moving from each value to each other ...
Definition ContinuousTimeMarkovChain.h:61
Space_ const & space() const
Definition ContinuousTimeMarkovChain.h:227
typename MarkovChain< Space_, Scalar_ >::MatrixType MatrixType
The generator's type, the same as a MarkovChain's transition matrix.
Definition ContinuousTimeMarkovChain.h:70
void logLikelihoods(Space const &space, EmissionBuffer< Space, Scalar > out) const
Writes the observation's log-density under each value's distribution into out.
Definition Emission.h:299
DensityEmission(Observation observation, DistributionOf distributionOf)
The emission of observation, under the distribution distributionOf returns for each value.
Definition Emission.h:284
Observation const & observation() const
Definition Emission.h:291
std::remove_cvref_t< decltype(std::declval< DistributionOf const & >()(Eigen::Index{}) .logDensity(std::declval< Observation const & >()))> Scalar
The scalar type of the log-densities.
Definition Emission.h:280
The emission model of one observed value: the column of the table it selects.
Definition Emission.h:152
Eigen::Index observed() const
Definition Emission.h:169
Scalar_ Scalar
the scalar type of the probabilities
Definition Emission.h:155
Observation(EmissionTable const &table, Eigen::Index observed)
The observation of the value with flat index observed under table.
Definition Emission.h:158
void logLikelihoods(Space_ const &space, EmissionBuffer< Space_, Scalar_ > out) const
Writes into out for each value of space.
Definition Emission.h:176
An emission table: the probability of each observed value given each value of the state.
Definition Emission.h:131
ObservationSpace_ ObservationSpaceType
the discrete space of the observation
Definition Emission.h:136
Observation observe(Eigen::Index observed) const &
Definition Emission.h:232
void observe(Eigen::Index observed) const &&=delete
observe() of a temporary table is deleted: the observation refers to the table, which would be gone a...
Eigen::Matrix< Scalar_, Space_::Cardinality, ObservationSpace_::Cardinality, Eigen::RowMajor, Space_::MaxCardinality, ObservationSpace_::MaxCardinality > MatrixType
The emission matrix's type: a row per value of the state, a column per observed value.
Definition Emission.h:139
ObservationSpace_ const & observationSpace() const
Definition Emission.h:218
MatrixType const & matrix() const
Definition Emission.h:225
Space_ SpaceType
the discrete space of the state
Definition Emission.h:135
Scalar_ Scalar
the scalar type of the probabilities
Definition Emission.h:134
EmissionTable(Space_ space, ObservationSpace_ observations, Matrix &&matrix)
The table over space and observations with emission matrix matrix: nonnegative, with each row summing...
Definition Emission.h:193
Space_ const & space() const
Definition Emission.h:211
void logLikelihoods(Space const &space, EmissionBuffer< Space, Scalar > out) const
Writes the exponential log-density of the inter-arrival time under each value's rate into out.
Definition Emission.h:466
ExponentialEmission(Eigen::DenseBase< Derived > const &rates, TimeStep< Scalar_ > auto const &tau)
The emission of an inter-arrival time of tau, any TimeStep (see Time steps), with rates one per value...
Definition Emission.h:452
Scalar_ interval() const
Definition Emission.h:459
Scalar_ Scalar
the scalar type of the rates
Definition Emission.h:446
Eigen::Index count() const
Definition Emission.h:389
Scalar_ interval() const
Definition Emission.h:396
Scalar_ Scalar
the scalar type of the rates
Definition Emission.h:370
PoissonEmission(Eigen::DenseBase< Derived > const &rates, Count count, TimeStep< Scalar_ > auto const &dt)
The emission of count events over an interval of dt, any TimeStep (see Time steps),...
Definition Emission.h:378
void logLikelihoods(Space const &space, EmissionBuffer< Space, Scalar > out) const
Writes the Poisson log-probability of the count under each value's rate into out.
Definition Emission.h:403
A type usable as a discrete space: a finite set of values.
Definition Discrete.h:72
A measurement of a value in the discrete space Space: one log-likelihood per value.
Definition Emission.h:50
A type usable as the scalar of a distribution: a real number type such as float, double,...
Definition Math.h:80
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
Scalar logGamma(std::type_identity_t< Scalar > const &x)
Definition Math.h:216
NumericError WeightError
Reasons a collection of log-weights cannot be normalized: Empty, ZeroSum, or NonFinite of NumericErro...
Definition NumericError.h:53
CategoricalParams< Space, Scalar > predict(CategoricalParams< Space, Scalar > const &prior, MarkovChain< Space, Scalar > const &chain)
Definition Categorical.h:1049
Eigen::Ref< Eigen::Array< Scalar, Space::Cardinality, 1 > > EmissionBuffer
The buffer an emission model writes its log-likelihoods into: one coefficient per value of the discre...
Definition Emission.h:37
std::expected< CategoricalUpdate< Space, Scalar >, WeightError > update(CategoricalParams< Space, Scalar > const &prior, E const &emission)
Definition Emission.h:97
Eigen::Array< typename Derived::Scalar, Eigen::Dynamic, 1 > logLikelihoods(M const &measurement, Space const &space, Eigen::MatrixBase< Derived > const &states)
Definition Likelihood.h:408
The result of updating a CategoricalParams by an emission model: the posterior, whose weights sum to ...
Definition Emission.h:66
Scalar logEvidence
Definition Emission.h:68
CategoricalParams< Space, Scalar > posterior
the distribution given the measurement
Definition Emission.h:67