27template <std::
floating_po
int Real>
40 static constexpr Real infinity() {
return std::numeric_limits<Real>::infinity(); }
46template <std::
floating_po
int Real>
52 throw std::invalid_argument(
"An exact observation requires one finite value");
56 throw std::invalid_argument(
"A left-censored observation requires a finite upper bound");
60 throw std::invalid_argument(
"A right-censored observation requires a finite lower bound");
65 throw std::invalid_argument(
"An interval-censored observation requires ordered finite bounds");
70template <
class Distribution>
71double intervalProbability(
const Distribution &distribution,
typename Distribution::value_type lower,
72 typename Distribution::value_type upper)
74 const double cdfDifference = distribution.F(upper) - distribution.F(lower);
81template <
class Distribution>
83FitResult<Distribution>
87 using Real =
typename Distribution::value_type;
88 if (observations.empty())
93 std::vector<Real> initializationSample;
94 initializationSample.reserve(observations.size());
95 for (
const auto &observation : observations) {
96 detail::validateCensored(observation);
97 switch (observation.censoring) {
100 initializationSample.push_back(observation.lower);
103 initializationSample.push_back(observation.upper);
106 initializationSample.push_back(observation.lower + (observation.upper - observation.lower) / 2);
111 const auto logLikelihood = [observations](
const Distribution &distribution) {
112 long double result = 0;
113 for (
const auto &observation : observations) {
114 switch (observation.censoring) {
116 result += distribution.logf(observation.lower);
119 const double probability = distribution.F(observation.upper);
120 if (!(probability > 0))
121 return -std::numeric_limits<double>::infinity();
122 result += std::log(probability);
126 const double probability = distribution.S(observation.lower);
127 if (!(probability > 0))
128 return -std::numeric_limits<double>::infinity();
129 result += std::log(probability);
133 const double probability =
134 detail::intervalProbability(distribution, observation.lower, observation.upper);
135 if (!(probability > 0))
136 return -std::numeric_limits<double>::infinity();
137 result += std::log(probability);
142 return static_cast<double>(result);
145 logLikelihood,
true);
148template <
class Distribution, std::ranges::contiguous_range Range>
150 std::same_as<std::remove_cv_t<std::ranges::range_value_t<Range>>,
151 CensoredObservation<typename Distribution::value_type>> &&
152 (!std::same_as<std::remove_cvref_t<Range>,
153 std::span<const CensoredObservation<typename Distribution::value_type>>>)
156 using Observation = CensoredObservation<typename Distribution::value_type>;
157 return fit_censored<Distribution>(std::span<const Observation>(observations), std::move(options));
160template <
class Distribution>
163 typename Distribution::value_type lower,
typename Distribution::value_type upper,
166 using Real =
typename Distribution::value_type;
169 if (std::isnan(lower) || std::isnan(upper) || !(lower < upper))
170 throw std::invalid_argument(
"Truncation bounds should be ordered and not NaN");
171 if (std::ranges::any_of(
172 sample, [lower, upper](Real value) {
return !std::isfinite(value) || value < lower || value > upper; }))
175 const auto logLikelihood = [sample, lower, upper](
const Distribution &distribution) {
176 const double lowerCdf = std::isinf(lower) ? 0 : distribution.F(lower);
177 const double upperCdf = std::isinf(upper) ? 1 : distribution.F(upper);
178 const double lowerSurvival = std::isinf(lower) ? 1 : distribution.S(lower);
179 const double upperSurvival = std::isinf(upper) ? 0 : distribution.S(upper);
180 const double probability = std::max(upperCdf - lowerCdf, lowerSurvival - upperSurvival);
181 if (!(probability > 0))
182 return -std::numeric_limits<double>::infinity();
183 return distribution.LogLikelihoodFunction(sample) - sample.size() * std::log(probability);
188template <
class Distribution, std::ranges::contiguous_range Range>
190 std::same_as<std::remove_cv_t<std::ranges::range_value_t<Range>>,
typename Distribution::value_type> &&
191 (!std::same_as<std::remove_cvref_t<Range>, std::span<const typename Distribution::value_type>>)
193 typename Distribution::value_type upper,
FitOptions options = {})
195 using Real =
typename Distribution::value_type;
196 return fit_truncated<Distribution>(std::span<const Real>(sample), lower, upper, std::move(options));
Real standardNormal(G &g)
Ziggurat algorithm of Marsaglia and Tsang with 256 layers and an explicit sign bit.
Definition variates.hpp:45
Definition bivariate_normal.hpp:10
@ WrongSample
the sample could not have been drawn from this distribution
@ NotApplicable
the method cannot be applied to this distribution or sample
@ TooFewElements
the sample is too small
FitResult< Distribution > fit_truncated(std::span< const typename Distribution::value_type > sample, typename Distribution::value_type lower, typename Distribution::value_type upper, FitOptions options={})
Definition censored.hpp:162
Censoring
Definition censored.hpp:20
FitResult< Distribution > fit_censored(std::span< const CensoredObservation< typename Distribution::value_type > > observations, FitOptions options={})
Definition censored.hpp:84
Definition censored.hpp:29
Real upper
Definition censored.hpp:32
Censoring censoring
Definition censored.hpp:30
static CensoredObservation right(Real lower)
Definition censored.hpp:36
Real lower
Definition censored.hpp:31
static CensoredObservation exact(Real value)
Definition censored.hpp:34
static CensoredObservation interval(Real lower, Real upper)
Definition censored.hpp:37
static CensoredObservation left(Real upper)
Definition censored.hpp:35