RandLib 2.0.0
Probability distributions, fitting, and heavy-tail tools for C++20
Loading...
Searching...
No Matches
mle.hpp
Go to the documentation of this file.
1#pragma once
2
39
40#include <algorithm>
41#include <array>
42#include <cmath>
43#include <concepts>
44#include <initializer_list>
45#include <limits>
46#include <map>
47#include <numbers>
48#include <numeric>
49#include <optional>
50#include <span>
51#include <stdexcept>
52#include <string>
53#include <string_view>
54#include <utility>
55#include <vector>
56
57#if defined(__clang__)
58#pragma clang diagnostic push
59#pragma clang diagnostic ignored "-Wdeprecated-declarations"
60#elif defined(__GNUC__)
61#pragma GCC diagnostic push
62#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
63#elif defined(_MSC_VER)
64#pragma warning(push)
65#pragma warning(disable : 4996)
66#endif
67
68namespace randlib
69{
70
71namespace detail
72{
73
75{
76 std::string_view name;
77 double value;
78 std::optional<double> lower;
79 std::optional<double> upper;
80 bool lowerInclusive = false;
81 bool upperInclusive = false;
82 bool integral = false;
83};
84
85using FixedParameterMap = std::map<std::string, double, std::less<>>;
86
87template <class Distribution>
88struct FitModel;
89
90template <class Distribution>
91concept FittableModel = requires(Distribution distribution, std::span<const typename Distribution::value_type> sample,
92 std::span<const double> parameters, Method method, const FixedParameterMap &fixed) {
93 { FitModel<Distribution>::parameters(distribution) } -> std::same_as<std::vector<FitParameter>>;
94 { FitModel<Distribution>::make(parameters) } -> std::same_as<Distribution>;
95 { FitModel<Distribution>::initial(sample, method, fixed) } -> std::same_as<Distribution>;
96 { FitModel<Distribution>::hasClosedForm(method) } -> std::same_as<bool>;
97};
98
99inline double toUnconstrained(double value, const FitParameter &parameter)
100{
101 if (parameter.lower && parameter.upper) {
102 const double fraction = (value - *parameter.lower) / (*parameter.upper - value);
103 return std::log(fraction);
104 }
105 if (parameter.lower)
106 return std::log(value - *parameter.lower);
107 if (parameter.upper)
108 return std::log(*parameter.upper - value);
109 return value;
110}
111
112inline double fromUnconstrained(double value, const FitParameter &parameter)
113{
114 if (parameter.lower && parameter.upper) {
115 const double width = *parameter.upper - *parameter.lower;
116 if (value >= 0) {
117 const double inverse = std::exp(-value);
118 return *parameter.lower + width / (1 + inverse);
119 }
120 const double exponential = std::exp(value);
121 return *parameter.lower + width * exponential / (1 + exponential);
122 }
123 if (parameter.lower)
124 return *parameter.lower + std::exp(value);
125 if (parameter.upper)
126 return *parameter.upper - std::exp(value);
127 return value;
128}
129
130inline bool withinBounds(double value, const FitParameter &parameter)
131{
132 if (!std::isfinite(value))
133 return false;
134 if (parameter.lower && (value < *parameter.lower || (!parameter.lowerInclusive && value == *parameter.lower)))
135 return false;
136 if (parameter.upper && (value > *parameter.upper || (!parameter.upperInclusive && value == *parameter.upper)))
137 return false;
138 if (parameter.integral && value != std::round(value))
139 return false;
140 return true;
141}
142
143inline void requireMethod(Method method, std::string_view distribution,
144 std::initializer_list<Method> supported = {Method::MLE, Method::MethodOfMoments})
145{
146 if (std::ranges::find(supported, method) == supported.end())
148 std::string(distribution) + " distribution: requested fitting method is not implemented");
149}
150
151template <class Distribution>
153{
154 if constexpr (requires { FitModel<Distribution>::requiresOptimization(method, fixed); })
156 return !FitModel<Distribution>::hasClosedForm(method) || !fixed.empty();
157}
158
159template <class T>
160DoublePair sampleMeanAndVariance(std::span<const T> sample)
161{
162 const long double sum = std::accumulate(sample.begin(), sample.end(), 0.0L);
163 const long double mean = sum / sample.size();
164 long double squaredDeviation = 0;
165 for (T value : sample) {
166 const long double difference = value - mean;
168 }
169 return {static_cast<double>(mean), static_cast<double>(squaredDeviation / sample.size())};
170}
171
172template <class T>
173double sampleQuantile(std::span<const T> sample, double probability)
174{
175 std::vector<T> sorted(sample.begin(), sample.end());
176 std::ranges::sort(sorted);
177 const double index = probability * (sorted.size() - 1);
178 const std::size_t lower = static_cast<std::size_t>(index);
179 const std::size_t upper = std::min(lower + 1, sorted.size() - 1);
180 const double fraction = index - lower;
181 return static_cast<double>(sorted[lower]) * (1 - fraction) + static_cast<double>(sorted[upper]) * fraction;
182}
183
184template <std::size_t Rows, std::size_t Columns>
185double bilinearInterpolation(const std::array<double, Rows> &rowGrid, const std::array<double, Columns> &columnGrid,
186 const std::array<std::array<double, Columns>, Rows> &values, double row, double column)
187{
188 const auto bracket = [](const auto &grid, double value) {
189 const auto upper = std::ranges::upper_bound(grid, value);
190 if (upper == grid.begin())
191 return std::pair<std::size_t, double>{0, 0};
192 if (upper == grid.end())
193 return std::pair<std::size_t, double>{grid.size() - 2, 1};
194 const std::size_t lowerIndex = static_cast<std::size_t>(upper - grid.begin() - 1);
195 const double fraction = (value - grid[lowerIndex]) / (grid[lowerIndex + 1] - grid[lowerIndex]);
196 return std::pair<std::size_t, double>{lowerIndex, fraction};
197 };
198 const auto [i, rowFraction] = bracket(rowGrid, row);
199 const auto [j, columnFraction] = bracket(columnGrid, column);
200 const double lower = values[i][j] * (1 - columnFraction) + values[i][j + 1] * columnFraction;
201 const double upper = values[i + 1][j] * (1 - columnFraction) + values[i + 1][j + 1] * columnFraction;
202 return lower * (1 - rowFraction) + upper * rowFraction;
203}
204
205inline std::optional<int> fixedPositiveInteger(const FixedParameterMap &fixed, std::string_view name,
206 std::string_view distribution)
207{
208 const auto found = fixed.find(name);
209 if (found == fixed.end())
210 return std::nullopt;
211 if (found->second < 1 || found->second > std::numeric_limits<int>::max() ||
212 found->second != std::round(found->second))
213 throw std::invalid_argument("Fixed " + std::string(distribution) + " " + std::string(name) +
214 " should be a positive integer");
215 return static_cast<int>(found->second);
216}
217
218inline std::vector<std::vector<double>> inverse(std::vector<std::vector<double>> matrix)
219{
220 const std::size_t n = matrix.size();
221 std::vector<std::vector<double>> result(n, std::vector<double>(n));
222 for (std::size_t i = 0; i < n; ++i)
223 result[i][i] = 1;
224
225 for (std::size_t column = 0; column < n; ++column) {
226 std::size_t pivot = column;
227 for (std::size_t row = column + 1; row < n; ++row)
228 if (std::fabs(matrix[row][column]) > std::fabs(matrix[pivot][column]))
229 pivot = row;
230 if (!std::isfinite(matrix[pivot][column]) ||
231 std::fabs(matrix[pivot][column]) <= std::numeric_limits<double>::epsilon())
232 return {};
233 std::swap(matrix[column], matrix[pivot]);
234 std::swap(result[column], result[pivot]);
235
236 const double diagonal = matrix[column][column];
237 for (std::size_t j = 0; j < n; ++j) {
238 matrix[column][j] /= diagonal;
239 result[column][j] /= diagonal;
240 }
241 for (std::size_t row = 0; row < n; ++row) {
242 if (row == column)
243 continue;
244 const double factor = matrix[row][column];
245 for (std::size_t j = 0; j < n; ++j) {
246 matrix[row][j] -= factor * matrix[column][j];
247 result[row][j] -= factor * result[column][j];
248 }
249 }
250 }
251 return result;
252}
253
254template <class Distribution, class LogLikelihood>
255double negativeLogLikelihood(std::span<const double> parameters, const LogLikelihood &logLikelihood)
256{
257 try {
258 Distribution distribution = FitModel<Distribution>::make(parameters);
259 const double value = -logLikelihood(distribution);
260 return std::isfinite(value) ? value : std::numeric_limits<double>::infinity();
261 } catch (const std::exception &) {
262 return std::numeric_limits<double>::infinity();
263 }
264}
265
266template <class Distribution, class Objective>
267void addUncertainty(FitResult<Distribution> &result, std::span<const FitParameter> specifications,
269{
270 const std::size_t dimension = result.parameters.size();
271 result.covariance.assign(dimension, std::vector<double>(dimension));
272 result.std_errors.assign(dimension, 0);
273 std::vector<std::size_t> freeIndices;
274 std::vector<double> freeParameters;
275 const double nan = std::numeric_limits<double>::quiet_NaN();
276 for (std::size_t i = 0; i < result.parameters.size(); ++i) {
277 if (!result.fixed[i]) {
278 if (specifications[i].integral) {
279 result.std_errors[i] = nan;
280 std::ranges::fill(result.covariance[i], nan);
281 for (auto &row : result.covariance)
282 row[i] = nan;
283 continue;
284 }
285 freeIndices.push_back(i);
286 freeParameters.push_back(result.parameters[i]);
287 }
288 }
289
290 if (freeIndices.empty())
291 return;
292
293 auto objective = [&](std::span<const double> free) {
294 std::vector<double> parameters = result.parameters;
295 for (std::size_t i = 0; i < freeIndices.size(); ++i)
296 parameters[freeIndices[i]] = free[i];
297 return naturalObjective(parameters);
298 };
300 auto covariance = inverse(std::move(information));
301 if (covariance.empty()) {
302 for (std::size_t index : freeIndices) {
303 result.std_errors[index] = nan;
304 for (std::size_t other : freeIndices)
305 result.covariance[index][other] = nan;
306 }
307 return;
308 }
309 for (std::size_t i = 0; i < freeIndices.size(); ++i) {
310 const std::size_t row = freeIndices[i];
311 result.std_errors[row] =
312 covariance[i][i] >= 0 ? std::sqrt(covariance[i][i]) : std::numeric_limits<double>::quiet_NaN();
313 for (std::size_t j = 0; j < freeIndices.size(); ++j)
314 result.covariance[row][freeIndices[j]] = covariance[i][j];
315 }
316}
317
318template <std::floating_point Real>
319struct FitModel<NormalRand<Real>>
320{
322
323 static std::vector<FitParameter> parameters(const Distribution &distribution)
324 {
325 return {{"location", distribution.GetLocation(), std::nullopt, std::nullopt},
326 {"variance", static_cast<double>(distribution.Variance()), 0.0, std::nullopt}};
327 }
328
329 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
330
331 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
332 {
334 Distribution distribution;
335 distribution.Fit(sample, method == Method::UMVU);
336 return distribution;
337 }
338
339 static bool hasClosedForm(Method) { return true; }
340};
341
342template <std::floating_point Real>
344{
346
347 static std::vector<FitParameter> parameters(const Distribution &distribution)
348 {
349 return {{"degree", distribution.GetDegree(), 0.0, std::nullopt},
350 {"location", distribution.GetLocation(), std::nullopt, std::nullopt},
351 {"scale", distribution.GetScale(), 0.0, std::nullopt}};
352 }
353
354 static Distribution make(std::span<const double> parameters)
355 {
356 return Distribution(parameters[0], parameters[1], parameters[2]);
357 }
358
359 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
360 {
361 requireMethod(method, "Student-t", {Method::MLE});
362 const double location = sampleQuantile(sample, 0.5);
363 const double scale = (sampleQuantile(sample, 0.75) - sampleQuantile(sample, 0.25)) / 1.35;
364 if (!(scale > 0))
365 throw fit_error(FitErrorCode::WrongSample, "Student-t distribution: sample spread should be positive");
366 return Distribution(5.0, location, scale);
367 }
368
369 static bool hasClosedForm(Method) { return false; }
370};
371
372template <std::floating_point Real>
374{
376
377 static std::vector<FitParameter> parameters(const Distribution &distribution)
378 {
379 return {{"degree", distribution.GetDegree(), 0.0, std::nullopt},
380 {"noncentrality", distribution.GetNoncentrality(), std::nullopt, std::nullopt}};
381 }
382
383 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
384
385 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &fixed)
386 {
387 requireMethod(method, "Noncentral-t", {Method::MLE});
388 const auto fixedDegree = fixed.find("degree");
389 const double degree = fixedDegree == fixed.end() ? 10.0 : fixedDegree->second;
390 const double median = sampleQuantile(sample, 0.5);
391 if (degree <= 1)
392 return Distribution(degree, median);
393 const long double coefficient =
394 std::exp(0.5L * std::log(0.5L * degree) + std::lgamma(0.5L * (degree - 1)) - std::lgamma(0.5L * degree));
395 return Distribution(degree, static_cast<double>(median / coefficient));
396 }
397
398 static bool hasClosedForm(Method) { return false; }
399};
400
401template <std::floating_point Real>
402struct FitModel<CauchyRand<Real>>
403{
405
406 static std::vector<FitParameter> parameters(const Distribution &distribution)
407 {
408 return {{"location", distribution.GetLocation(), std::nullopt, std::nullopt},
409 {"scale", distribution.GetScale(), 0.0, std::nullopt}};
410 }
411
412 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
413
414 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
415 {
416 requireMethod(method, "Cauchy", {Method::MLE});
417 const double scale = 0.5 * (sampleQuantile(sample, 0.75) - sampleQuantile(sample, 0.25));
418 if (!(scale > 0))
419 throw fit_error(FitErrorCode::WrongSample, "Cauchy distribution: sample spread should be positive");
420 return Distribution(sampleQuantile(sample, 0.5), scale);
421 }
422
423 static bool hasClosedForm(Method) { return false; }
424};
425
426template <std::floating_point Real>
427struct FitModel<StableRand<Real>>
428{
431
432 static std::vector<FitParameter> parameters(const Distribution &distribution)
433 {
434 return {{"exponent", distribution.GetExponent(), 0.1, 2.0, true, true},
435 {"skewness", distribution.GetSkewness(), -1.0, 1.0, true, true},
436 {"scale", distribution.GetScale(), 0.0, std::nullopt},
437 {"location", distribution.GetLocation(), std::nullopt, std::nullopt}};
438 }
439
440 static Distribution make(std::span<const double> parameters)
441 {
442 return Distribution(parameters[0], parameters[1], parameters[2], parameters[3], Parameterization::S0);
443 }
444
445 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
446 {
447 requireMethod(method, "Stable", {Method::MLE, Method::Quantile});
448 const double p05 = sampleQuantile(sample, 0.05);
449 const double p25 = sampleQuantile(sample, 0.25);
450 const double p50 = sampleQuantile(sample, 0.50);
451 const double p75 = sampleQuantile(sample, 0.75);
452 const double p95 = sampleQuantile(sample, 0.95);
453 const double interquartileRange = p75 - p25;
454 const double outerRange = p95 - p05;
455 if (!(interquartileRange > 0) || !(outerRange > 0))
456 throw fit_error(FitErrorCode::WrongSample, "Stable distribution: sample spread should be positive");
457
458 static constexpr std::array nuAlphaGrid = {2.439, 2.5, 2.6, 2.7, 2.8, 3.0, 3.2, 3.5,
459 4.0, 5.0, 6.0, 8.0, 10.0, 15.0, 25.0};
460 static constexpr std::array nuBetaGrid = {0.0, 0.1, 0.2, 0.3, 0.5, 0.7, 1.0};
461 static constexpr std::array alphaTable = {
462 std::array{2.000, 2.000, 2.000, 2.000, 2.000, 2.000, 2.000},
463 std::array{1.916, 1.924, 1.924, 1.924, 1.924, 1.924, 1.924},
464 std::array{1.808, 1.813, 1.829, 1.829, 1.829, 1.829, 1.829},
465 std::array{1.729, 1.730, 1.737, 1.745, 1.745, 1.745, 1.745},
466 std::array{1.664, 1.663, 1.663, 1.668, 1.676, 1.676, 1.676},
467 std::array{1.563, 1.560, 1.553, 1.548, 1.547, 1.547, 1.547},
468 std::array{1.484, 1.480, 1.471, 1.460, 1.448, 1.438, 1.438},
469 std::array{1.391, 1.386, 1.378, 1.364, 1.337, 1.318, 1.318},
470 std::array{1.279, 1.273, 1.266, 1.250, 1.210, 1.184, 1.150},
471 std::array{1.128, 1.121, 1.114, 1.101, 1.067, 1.027, 0.973},
472 std::array{1.029, 1.021, 1.014, 1.004, 0.974, 0.935, 0.874},
473 std::array{0.896, 0.892, 0.884, 0.883, 0.855, 0.823, 0.769},
474 std::array{0.818, 0.812, 0.806, 0.801, 0.780, 0.756, 0.691},
475 std::array{0.698, 0.695, 0.692, 0.689, 0.676, 0.656, 0.597},
476 std::array{0.593, 0.590, 0.588, 0.586, 0.579, 0.563, 0.513},
477 };
478 static constexpr std::array betaTable = {
479 std::array{0.000, 2.160, 1.000, 1.000, 1.000, 1.000, 1.000},
480 std::array{0.000, 1.592, 3.390, 1.000, 1.000, 1.000, 1.000},
481 std::array{0.000, 0.759, 1.800, 1.000, 1.000, 1.000, 1.000},
482 std::array{0.000, 0.482, 1.048, 1.694, 1.000, 1.000, 1.000},
483 std::array{0.000, 0.360, 0.760, 1.232, 2.229, 1.000, 1.000},
484 std::array{0.000, 0.253, 0.518, 0.823, 1.575, 1.000, 1.000},
485 std::array{0.000, 0.203, 0.410, 0.632, 1.244, 1.906, 1.000},
486 std::array{0.000, 0.165, 0.332, 0.499, 0.943, 1.560, 1.000},
487 std::array{0.000, 0.136, 0.271, 0.404, 0.689, 1.230, 2.195},
488 std::array{0.000, 0.109, 0.216, 0.323, 0.539, 0.827, 1.917},
489 std::array{0.000, 0.096, 0.190, 0.284, 0.472, 0.693, 1.759},
490 std::array{0.000, 0.082, 0.163, 0.243, 0.412, 0.601, 1.596},
491 std::array{0.000, 0.074, 0.147, 0.220, 0.377, 0.546, 1.482},
492 std::array{0.000, 0.064, 0.128, 0.191, 0.330, 0.478, 1.362},
493 std::array{0.000, 0.056, 0.112, 0.167, 0.285, 0.428, 1.274},
494 };
495
496 const double nuAlpha = outerRange / interquartileRange;
497 const double nuBeta = (p95 + p05 - 2 * p50) / outerRange;
498 double exponent = 2.0;
499 double skewness = 0.0;
500 if (nuAlpha >= nuAlphaGrid.front()) {
502 const double magnitude =
504 skewness = std::copysign(std::clamp(magnitude, 0.0, 1.0), nuBeta);
505 }
506 exponent = std::clamp(exponent, 0.1, 2.0);
507
508 const Distribution standard(exponent, skewness, 1, 0, Parameterization::S0);
509 const double theoretical25 = standard.Quantile(0.25);
510 const double theoretical50 = standard.Quantile(0.50);
511 const double theoretical75 = standard.Quantile(0.75);
512 const double scale = interquartileRange / (theoretical75 - theoretical25);
513 const double location = p50 - scale * theoretical50;
514 if (!(scale > 0) || !std::isfinite(location))
515 throw fit_error(FitErrorCode::WrongReturn, "Stable distribution: quantile fit did not converge");
516
517 if (method == Method::MLE) {
518 exponent = std::clamp(exponent, 0.100001, 1.999999);
519 skewness = std::clamp(skewness, -0.999999, 0.999999);
520 }
521 return Distribution(exponent, skewness, scale, location, Parameterization::S0);
522 }
523
524 static bool hasClosedForm(Method method) { return method == Method::Quantile; }
525};
526
527template <std::floating_point Real>
529{
533
534 static std::vector<FitParameter> parameters(const Distribution &distribution)
535 {
536 return {{"exponent", distribution.GetExponent(), 0.1, 2.0, true, true},
537 {"skewness", distribution.GetSkewness(), -1.0, 1.0, true, true},
538 {"scale", distribution.GetScale(), 0.0, std::nullopt},
539 {"location", distribution.GetLocation(), std::nullopt, std::nullopt}};
540 }
541
542 static Distribution make(std::span<const double> parameters)
543 {
544 return Distribution(parameters[0], parameters[1], parameters[2], parameters[3], Parameterization::S0);
545 }
546
547 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &fixed)
548 {
549 if (std::ranges::any_of(sample, [](Real value) { return !(value > 0); }))
550 throw fit_error(FitErrorCode::WrongSample, "Log-stable distribution: sample values should be positive");
551 std::vector<Real> logSample;
552 logSample.reserve(sample.size());
553 for (Real value : sample)
554 logSample.push_back(std::log(value));
555 const Stable estimate = FitModel<Stable>::initial(logSample, method, fixed);
556 return Distribution(estimate.GetExponent(), estimate.GetSkewness(), estimate.GetScale(), estimate.GetLocation(),
557 Parameterization::S0);
558 }
559
560 static bool hasClosedForm(Method method) { return FitModel<Stable>::hasClosedForm(method); }
561};
562
563template <std::floating_point Real>
565{
567
568 static std::vector<FitParameter> parameters(const Distribution &distribution)
569 {
570 return {{"exponent", distribution.GetExponent(), 0.0, 1.0},
571 {"scale", distribution.GetScale(), 0.0, std::nullopt},
572 {"location", distribution.GetLocation(), std::nullopt, std::nullopt}};
573 }
574
575 static Distribution make(std::span<const double> parameters)
576 {
577 return Distribution(parameters[0], parameters[1], parameters[2]);
578 }
579
580 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &fixed)
581 {
582 requireMethod(method, "Stable Count", {Method::MLE});
583 const auto [minimum, maximum] = std::ranges::minmax(sample);
584 const double spread = static_cast<double>(maximum - minimum);
585 if (!(spread > 0))
586 throw fit_error(FitErrorCode::WrongSample, "Stable Count distribution: sample spread should be positive");
587 const double exponent = fixed.contains("exponent") ? fixed.at("exponent") : 0.5;
588 const double location =
589 fixed.contains("location") ? fixed.at("location") : static_cast<double>(minimum) - 0.05 * spread;
590 const double mean = static_cast<double>(sampleMeanAndVariance(sample).first);
592 const double scale = fixed.contains("scale")
593 ? fixed.at("scale")
594 : std::max(static_cast<double>((mean - location) / standard.Mean()), spread * 1e-3);
595 return Distribution(exponent, scale, location);
596 }
597
598 static bool hasClosedForm(Method) { return false; }
599};
600
601template <std::floating_point Real>
602struct FitModel<GumbelRand<Real>>
603{
605
606 static std::vector<FitParameter> parameters(const Distribution &distribution)
607 {
608 return {{"location", distribution.GetLocation(), std::nullopt, std::nullopt},
609 {"scale", distribution.GetScale(), 0.0, std::nullopt}};
610 }
611
612 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
613
614 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
615 {
616 requireMethod(method, "Gumbel", {Method::MLE});
617 const auto [mean, variance] = sampleMeanAndVariance(sample);
618 const double scale = std::sqrt(6 * variance) / std::numbers::pi;
619 if (!(scale > 0))
620 throw fit_error(FitErrorCode::WrongSample, "Gumbel distribution: sample variance should be positive");
621 return Distribution(mean - static_cast<double>(randlib::consts::egamma) * scale, scale);
622 }
623
624 static bool hasClosedForm(Method) { return false; }
625};
626
627template <std::floating_point Real>
629{
631
632 static std::vector<FitParameter> parameters(const Distribution &distribution)
633 {
634 return {{"shape", distribution.GetShape(), 0.0, std::nullopt},
635 {"scale", distribution.GetScale(), 0.0, std::nullopt},
636 {"location", distribution.GetLocation(), std::nullopt, std::nullopt}};
637 }
638
639 static Distribution make(std::span<const double> parameters)
640 {
641 return Distribution(parameters[0], parameters[1], parameters[2]);
642 }
643
644 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
645 {
646 requireMethod(method, "Frechet", {Method::MLE});
647 const auto [minimum, maximum] = std::ranges::minmax(sample);
648 const double spread = static_cast<double>(maximum - minimum);
649 if (!(spread > 0))
650 throw fit_error(FitErrorCode::WrongSample, "Frechet distribution: sample spread should be positive");
651 return Distribution(1.0, spread, static_cast<double>(minimum) - 0.1 * spread);
652 }
653
654 static bool hasClosedForm(Method) { return false; }
655};
656
657template <std::floating_point Real>
659{
661
662 static std::vector<FitParameter> parameters(const Distribution &distribution)
663 {
664 return {{"shape", distribution.GetShape(), -0.5, std::nullopt},
665 {"scale", distribution.GetScale(), 0.0, std::nullopt}};
666 }
667
668 static Distribution make(std::span<const double> parameters)
669 {
670 return Distribution(parameters[0], parameters[1], 0);
671 }
672
673 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
674 {
675 requireMethod(method, "Generalized Pareto", {Method::MLE});
676 if (std::ranges::any_of(sample, [](Real value) { return value < 0; }))
678 "Generalized Pareto distribution: exceedances should be non-negative");
679 const auto [mean, variance] = sampleMeanAndVariance(sample);
680 if (!(mean > 0) || !(variance > 0))
682 "Generalized Pareto distribution: sample spread should be positive");
683 const double shape = std::clamp(0.5 * (1 - mean * mean / variance), -0.4, 1.0);
684 return Distribution(shape, mean * (1 - shape), 0);
685 }
686
687 static bool hasClosedForm(Method) { return false; }
688};
689
690template <std::floating_point Real>
692{
694
695 static std::vector<FitParameter> parameters(const Distribution &distribution)
696 {
697 return {{"shape", distribution.GetShape(), std::nullopt, std::nullopt},
698 {"scale", distribution.GetScale(), 0.0, std::nullopt},
699 {"location", distribution.GetLocation(), std::nullopt, std::nullopt}};
700 }
701
702 static Distribution make(std::span<const double> parameters)
703 {
704 return Distribution(parameters[0], parameters[1], parameters[2]);
705 }
706
707 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
708 {
709 requireMethod(method, "Generalized extreme-value", {Method::MLE});
710 const auto [mean, variance] = sampleMeanAndVariance(sample);
711 if (!(variance > 0))
713 "Generalized extreme-value distribution: sample spread should be positive");
714 const double scale = std::sqrt(6 * variance) / std::numbers::pi;
715 return Distribution(0, scale, mean - static_cast<double>(randlib::consts::egamma) * scale);
716 }
717
718 static bool hasClosedForm(Method) { return false; }
719};
720
721template <std::floating_point Real>
723{
725
726 static std::vector<FitParameter> parameters(const Distribution &distribution)
727 {
728 return {{"degree1", static_cast<double>(distribution.GetFirstDegree()), 1.0, std::nullopt, true, false, true},
729 {"degree2", static_cast<double>(distribution.GetSecondDegree()), 1.0, std::nullopt, true, false, true}};
730 }
731
732 static Distribution make(std::span<const double> parameters)
733 {
734 return Distribution(static_cast<int>(parameters[0]), static_cast<int>(parameters[1]));
735 }
736
737 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &fixed)
738 {
739 requireMethod(method, "Fisher-F", {Method::MLE});
740 if (std::ranges::any_of(sample, [](Real value) { return !(value > 0); }))
741 throw fit_error(FitErrorCode::WrongSample, "Fisher-F distribution: sample values should be positive");
742 const auto fixedDegree1 = fixedPositiveInteger(fixed, "degree1", "Fisher-F");
743 const auto fixedDegree2 = fixedPositiveInteger(fixed, "degree2", "Fisher-F");
744 const int minimum1 = fixedDegree1.value_or(1);
745 const int maximum1 = fixedDegree1.value_or(80);
746 const int minimum2 = fixedDegree2.value_or(1);
747 const int maximum2 = fixedDegree2.value_or(80);
748 double bestLogLikelihood = -std::numeric_limits<double>::infinity();
750 for (int degree1 = minimum1; degree1 <= maximum1; ++degree1) {
751 for (int degree2 = minimum2; degree2 <= maximum2; ++degree2) {
753 const double logLikelihood = candidate.LogLikelihoodFunction(sample);
756 best = std::move(candidate);
757 }
758 }
759 }
760 return best;
761 }
762
763 static bool hasClosedForm(Method) { return true; }
764 static bool requiresOptimization(Method, const FixedParameterMap &) { return false; }
765};
766
767template <std::floating_point Real>
769{
771
772 static std::vector<FitParameter> parameters(const Distribution &distribution)
773 {
774 return {{"shape", distribution.GetShape(), 0.0, std::nullopt},
775 {"rate", distribution.GetRate(), 0.0, std::nullopt}};
776 }
777
778 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
779
780 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
781 {
782 requireMethod(method, "Inverse-gamma", {Method::MLE});
783 if (std::ranges::any_of(sample, [](Real value) { return !(value > 0); }))
784 throw fit_error(FitErrorCode::WrongSample, "Inverse-gamma distribution: sample values should be positive");
785 const auto [mean, variance] = sampleMeanAndVariance(sample);
786 if (!(variance > 0))
788 "Inverse-gamma distribution: sample variance should be positive");
789 const double shape = 2 + mean * mean / variance;
790 return Distribution(shape, mean * (shape - 1));
791 }
792
793 static bool hasClosedForm(Method) { return false; }
794};
795
796template <std::floating_point Real>
798{
800
801 static std::vector<FitParameter> parameters(const Distribution &distribution)
802 {
803 return {{"mean", distribution.GetMean(), 0.0, std::nullopt},
804 {"shape", distribution.GetShape(), 0.0, std::nullopt}};
805 }
806
807 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
808
809 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
810 {
811 requireMethod(method, "Inverse-Gaussian", {Method::MLE});
812 if (std::ranges::any_of(sample, [](Real value) { return !(value > 0); }))
814 "Inverse-Gaussian distribution: sample values should be positive");
815 const auto [mean, variance] = sampleMeanAndVariance(sample);
816 if (!(variance > 0))
818 "Inverse-Gaussian distribution: sample variance should be positive");
819 return Distribution(mean, mean * mean * mean / variance);
820 }
821
822 static bool hasClosedForm(Method) { return false; }
823};
824
825template <std::floating_point Real>
827{
829
830 static std::vector<FitParameter> parameters(const Distribution &distribution)
831 {
832 return {{"degree", distribution.GetDegree(), 0.0, std::nullopt},
833 {"noncentrality", distribution.GetNoncentrality(), 0.0, std::nullopt}};
834 }
835
836 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
837
838 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
839 {
840 requireMethod(method, "Noncentral chi-squared", {Method::MLE});
841 if (std::ranges::any_of(sample, [](Real value) { return value < 0; }))
843 "Noncentral chi-squared distribution: sample values should be non-negative");
844 const auto [mean, variance] = sampleMeanAndVariance(sample);
845 const double noncentrality = std::max(0.1, 0.5 * variance - mean);
846 const double degree = std::max(0.1, mean - noncentrality);
848 }
849
850 static bool hasClosedForm(Method) { return false; }
851};
852
853template <std::floating_point Real>
855{
857
858 static std::vector<FitParameter> parameters(const Distribution &distribution)
859 {
860 const double scale = distribution.GetScale();
861 return {{"location", distribution.GetLocation(), std::nullopt, std::nullopt},
862 {"variance", scale * scale, 0.0, std::nullopt}};
863 }
864
865 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
866
867 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
868 {
869 requireMethod(method, "Log-normal", {Method::MLE});
870 Distribution distribution;
871 distribution.Fit(sample);
872 return distribution;
873 }
874
875 static bool hasClosedForm(Method) { return true; }
876};
877
878template <std::floating_point Real>
880{
882
883 static std::vector<FitParameter> parameters(const Distribution &distribution)
884 {
885 return {{"rate", distribution.GetRate(), 0.0, std::nullopt}};
886 }
887
888 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0]); }
889
890 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
891 {
892 requireMethod(method, "Exponential", {Method::MLE, Method::UMVU});
893 Distribution distribution;
894 distribution.FitRate(sample, method == Method::UMVU);
895 return distribution;
896 }
897
898 static bool hasClosedForm(Method) { return true; }
899};
900
901template <std::floating_point Real>
902struct FitModel<GammaRand<Real>>
903{
905
906 static std::vector<FitParameter> parameters(const Distribution &distribution)
907 {
908 return {{"shape", distribution.GetShape(), 0.0, std::nullopt},
909 {"rate", distribution.GetRate(), 0.0, std::nullopt}};
910 }
911
912 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
913
914 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
915 {
916 requireMethod(method, "Gamma", {Method::MLE});
917 Distribution distribution;
918 distribution.Fit(sample);
919 return distribution;
920 }
921
922 static bool hasClosedForm(Method) { return true; }
923};
924
925template <std::floating_point Real>
927{
929
930 static std::vector<FitParameter> parameters(const Distribution &distribution)
931 {
932 return {{"degree", static_cast<double>(distribution.GetDegree()), 1.0, std::nullopt, true, false, true}};
933 }
934
935 static Distribution make(std::span<const double> parameters)
936 {
937 return Distribution(static_cast<std::size_t>(parameters[0]));
938 }
939
940 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &fixed)
941 {
942 requireMethod(method, "Chi-squared", {Method::MLE});
943 if (const auto degree = fixedPositiveInteger(fixed, "degree", "chi-squared"))
944 return Distribution(static_cast<std::size_t>(*degree));
945 const auto [mean, variance] = sampleMeanAndVariance(sample);
946 const double searchLimit = std::clamp(std::ceil(std::max(mean, 0.5 * variance) * 4 + 20), 40.0, 1000.0);
947 const int maximum = static_cast<int>(searchLimit);
948 double bestLogLikelihood = -std::numeric_limits<double>::infinity();
950 for (int degree = 1; degree <= maximum; ++degree) {
951 Distribution candidate(static_cast<std::size_t>(degree));
952 const double logLikelihood = candidate.LogLikelihoodFunction(sample);
955 best = std::move(candidate);
956 }
957 }
958 return best;
959 }
960
961 static bool hasClosedForm(Method) { return true; }
962 static bool requiresOptimization(Method, const FixedParameterMap &) { return false; }
963};
964
965template <std::floating_point Real>
966struct FitModel<ErlangRand<Real>>
967{
969
970 static std::vector<FitParameter> parameters(const Distribution &distribution)
971 {
972 return {{"shape", distribution.GetShape(), 1.0, std::nullopt, true, false, true},
973 {"rate", distribution.GetRate(), 0.0, std::nullopt}};
974 }
975
976 static Distribution make(std::span<const double> parameters)
977 {
978 return Distribution(static_cast<int>(parameters[0]), parameters[1]);
979 }
980
981 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &fixed)
982 {
983 requireMethod(method, "Erlang", {Method::MLE});
984 if (std::ranges::any_of(sample, [](Real value) { return !(value > 0); }))
985 throw fit_error(FitErrorCode::WrongSample, "Erlang distribution: sample values should be positive");
986 const auto [mean, variance] = sampleMeanAndVariance(sample);
987 const auto fixedShape = fixedPositiveInteger(fixed, "shape", "Erlang");
988 const auto fixedRate = fixed.find("rate");
989 if (fixedRate != fixed.end() && !(fixedRate->second > 0))
990 throw std::invalid_argument("Fixed Erlang rate should be positive");
991 const double estimate = variance > 0 ? std::clamp(std::round(mean * mean / variance), 1.0, 245.0) : 1.0;
992 const int maximum = fixedShape ? *fixedShape : std::clamp(static_cast<int>(4 * estimate + 20), 40, 1000);
993 const int minimum = fixedShape ? *fixedShape : 1;
994 double bestLogLikelihood = -std::numeric_limits<double>::infinity();
996 for (int shape = minimum; shape <= maximum; ++shape) {
997 const double rate = fixedRate == fixed.end() ? shape / mean : fixedRate->second;
999 const double logLikelihood = candidate.LogLikelihoodFunction(sample);
1002 best = std::move(candidate);
1003 }
1004 }
1005 return best;
1006 }
1007
1008 static bool hasClosedForm(Method) { return true; }
1009 static bool requiresOptimization(Method, const FixedParameterMap &) { return false; }
1010};
1011
1012template <std::floating_point Real>
1013struct FitModel<BetaRand<Real>>
1014{
1016
1017 static std::vector<FitParameter> parameters(const Distribution &distribution)
1018 {
1019 return {{"alpha", distribution.GetAlpha(), 0.0, std::nullopt},
1020 {"beta", distribution.GetBeta(), 0.0, std::nullopt}};
1021 }
1022
1023 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
1024
1025 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
1026 {
1027 requireMethod(method, "Beta", {Method::MLE});
1028 Distribution distribution;
1029 distribution.FitShapes(sample);
1030 return distribution;
1031 }
1032
1033 static bool hasClosedForm(Method) { return true; }
1034};
1035
1036template <std::floating_point Real>
1038{
1040
1041 static std::vector<FitParameter> parameters(const Distribution &distribution)
1042 {
1043 return {{"shape", distribution.GetShape(), 0.0, std::nullopt}};
1044 }
1045
1046 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0]); }
1047
1048 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
1049 {
1050 requireMethod(method, "Arcsine", {Method::MLE});
1051 Distribution distribution;
1052 distribution.FitShape(sample);
1053 return distribution;
1054 }
1055
1056 static bool hasClosedForm(Method) { return true; }
1057};
1058
1059template <std::floating_point Real>
1061{
1063
1064 static std::vector<FitParameter> parameters(const Distribution &distribution)
1065 {
1066 return {{"alpha", distribution.GetAlpha(), 0.0, std::nullopt},
1067 {"beta", distribution.GetBeta(), 0.0, std::nullopt}};
1068 }
1069
1070 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
1071
1072 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
1073 {
1074 requireMethod(method, "Beta prime", {Method::MLE});
1075 Distribution distribution;
1076 distribution.Fit(sample);
1077 return distribution;
1078 }
1079
1080 static bool hasClosedForm(Method) { return true; }
1081};
1082
1083template <std::floating_point Real>
1085{
1087
1088 static std::vector<FitParameter> parameters(const Distribution &distribution)
1089 {
1090 return {{"shape", distribution.GetShape(), 0.0, std::nullopt},
1091 {"scale", distribution.GetScale(), 0.0, std::nullopt}};
1092 }
1093
1094 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
1095
1096 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
1097 {
1098 requireMethod(method, "Pareto", {Method::MLE, Method::UMVU});
1099 Distribution distribution;
1100 distribution.Fit(sample, method == Method::UMVU);
1101 return distribution;
1102 }
1103
1104 static bool hasClosedForm(Method) { return true; }
1105};
1106
1107template <std::floating_point Real>
1109{
1111
1112 static std::vector<FitParameter> parameters(const Distribution &distribution)
1113 {
1114 return {{"scale", distribution.GetScale(), 0.0, std::nullopt},
1115 {"shape", distribution.GetShape(), 0.0, std::nullopt}};
1116 }
1117
1118 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
1119
1120 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
1121 {
1122 requireMethod(method, "Weibull", {Method::MLE});
1123 if (std::ranges::any_of(sample, [](Real value) { return !(value > 0); }))
1124 throw fit_error(FitErrorCode::WrongSample, "Weibull distribution: sample values should be positive");
1125 const long double sum = std::accumulate(sample.begin(), sample.end(), 0.0L);
1126 return Distribution(static_cast<double>(sum / sample.size()), 1.0);
1127 }
1128
1129 static bool hasClosedForm(Method) { return false; }
1130};
1131
1132template <std::floating_point Real>
1134{
1136
1137 static std::vector<FitParameter> parameters(const Distribution &distribution)
1138 {
1139 return {{"location", distribution.GetShift(), std::nullopt, std::nullopt},
1140 {"scale", distribution.GetScale(), 0.0, std::nullopt}};
1141 }
1142
1143 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
1144
1145 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
1146 {
1147 requireMethod(method, "Laplace", {Method::MLE});
1148 Distribution distribution;
1149 distribution.Fit(sample);
1150 return distribution;
1151 }
1152
1153 static bool hasClosedForm(Method) { return true; }
1154};
1155
1156template <std::floating_point Real>
1158{
1160
1161 static std::vector<FitParameter> parameters(const Distribution &distribution)
1162 {
1163 return {{"location", distribution.GetLocation(), std::nullopt, std::nullopt},
1164 {"scale", distribution.GetScale(), 0.0, std::nullopt}};
1165 }
1166
1167 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
1168
1169 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
1170 {
1171 requireMethod(method, "Logistic", {Method::MLE});
1172 const long double sum = std::accumulate(sample.begin(), sample.end(), 0.0L);
1173 const double location = static_cast<double>(sum / sample.size());
1174 long double squaredDeviation = 0;
1175 for (Real value : sample) {
1176 const long double difference = value - location;
1178 }
1179 const double scale = std::sqrt(static_cast<double>(3 * squaredDeviation / sample.size())) / std::numbers::pi;
1180 if (!(scale > 0))
1181 throw fit_error(FitErrorCode::WrongSample, "Logistic distribution: sample variance should be positive");
1182 return Distribution(location, scale);
1183 }
1184
1185 static bool hasClosedForm(Method) { return false; }
1186};
1187
1188template <std::floating_point Real>
1189struct FitModel<LevyRand<Real>>
1190{
1192
1193 static std::vector<FitParameter> parameters(const Distribution &distribution)
1194 {
1195 return {{"location", distribution.GetLocation(), std::nullopt, std::nullopt},
1196 {"scale", distribution.GetScale(), 0.0, std::nullopt}};
1197 }
1198
1199 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
1200
1201 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &fixed)
1202 {
1203 requireMethod(method, "Levy", {Method::MLE});
1204 const auto [minimum, maximum] = std::ranges::minmax(sample);
1205 const double spread = static_cast<double>(maximum - minimum);
1206 if (!(spread > 0))
1207 throw fit_error(FitErrorCode::WrongSample, "Levy distribution: sample spread should be positive");
1208 const auto fixedLocation = fixed.find("location");
1209 const double location =
1210 fixedLocation == fixed.end() ? static_cast<double>(minimum) - 0.1 * spread : fixedLocation->second;
1211 Distribution distribution(location, spread);
1212 distribution.FitScale(sample);
1213 return distribution;
1214 }
1215
1216 static bool hasClosedForm(Method) { return false; }
1217};
1218
1219template <std::floating_point Real>
1221{
1223
1224 static std::vector<FitParameter> parameters(const Distribution &distribution)
1225 {
1226 return {{"scale", distribution.GetScale(), 0.0, std::nullopt}};
1227 }
1228
1229 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0]); }
1230
1231 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
1232 {
1233 requireMethod(method, "Rayleigh", {Method::MLE, Method::UMVU});
1234 Distribution distribution;
1235 distribution.Fit(sample, method == Method::UMVU);
1236 return distribution;
1237 }
1238
1239 static bool hasClosedForm(Method) { return true; }
1240};
1241
1242template <std::floating_point Real>
1244{
1246
1247 static std::vector<FitParameter> parameters(const Distribution &distribution)
1248 {
1249 return {{"minimum", distribution.MinValue(), std::nullopt, distribution.MaxValue()},
1250 {"maximum", distribution.MaxValue(), distribution.MinValue(), std::nullopt}};
1251 }
1252
1253 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0], parameters[1]); }
1254
1255 static Distribution initial(std::span<const Real> sample, Method method, const FixedParameterMap &)
1256 {
1257 requireMethod(method, "Uniform", {Method::MLE, Method::UMVU});
1258 Distribution distribution;
1259 distribution.Fit(sample, method == Method::UMVU);
1260 return distribution;
1261 }
1262
1263 static bool hasClosedForm(Method) { return true; }
1264};
1265
1266template <>
1268{
1270
1271 static std::vector<FitParameter> parameters(const Distribution &distribution)
1272 {
1273 return {{"value", distribution.GetValue(), std::nullopt, std::nullopt}};
1274 }
1275
1276 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0]); }
1277
1278 static Distribution initial(std::span<const double> sample, Method method, const FixedParameterMap &)
1279 {
1280 requireMethod(method, "Degenerate", {Method::MLE});
1281 Distribution distribution;
1282 distribution.Fit(sample);
1283 return distribution;
1284 }
1285
1286 static bool hasClosedForm(Method) { return true; }
1287};
1288
1289template <std::integral Int>
1291{
1293
1294 static std::vector<FitParameter> parameters(const Distribution &distribution)
1295 {
1296 return {{"rate", distribution.GetRate(), 0.0, std::nullopt}};
1297 }
1298
1299 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0]); }
1300
1301 static Distribution initial(std::span<const Int> sample, Method method, const FixedParameterMap &)
1302 {
1304 Distribution distribution;
1305 distribution.Fit(sample);
1306 return distribution;
1307 }
1308
1309 static bool hasClosedForm(Method) { return true; }
1310};
1311
1312template <>
1314{
1316
1317 static std::vector<FitParameter> parameters(const Distribution &distribution)
1318 {
1319 return {{"probability", distribution.GetProbability(), 0.0, 1.0, true, true}};
1320 }
1321
1322 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0]); }
1323
1324 static Distribution initial(std::span<const int> sample, Method method, const FixedParameterMap &)
1325 {
1326 requireMethod(method, "Bernoulli", {Method::MLE, Method::MethodOfMoments});
1327 if (std::ranges::any_of(sample, [](int value) { return value != 0 && value != 1; }))
1328 throw fit_error(FitErrorCode::WrongSample, "Bernoulli distribution: sample values should be zero or one");
1329 const double sum = std::accumulate(sample.begin(), sample.end(), 0.0);
1330 return Distribution(sum / sample.size());
1331 }
1332
1333 static bool hasClosedForm(Method) { return true; }
1334};
1335
1336template <std::integral Int>
1338{
1340
1341 static std::vector<FitParameter> parameters(const Distribution &distribution)
1342 {
1343 return {{"probability", distribution.GetProbability(), 0.0, 1.0, false, true}};
1344 }
1345
1346 static Distribution make(std::span<const double> parameters) { return Distribution(parameters[0]); }
1347
1348 static Distribution initial(std::span<const Int> sample, Method method, const FixedParameterMap &)
1349 {
1350 requireMethod(method, "Geometric", {Method::MLE, Method::MethodOfMoments});
1351 if (std::ranges::any_of(sample, [](Int value) { return value < 0; }))
1352 throw fit_error(FitErrorCode::WrongSample, "Geometric distribution: sample values should be non-negative");
1353 const double sum = std::accumulate(sample.begin(), sample.end(), 0.0);
1354 return Distribution(1 / (1 + sum / sample.size()));
1355 }
1356
1357 static bool hasClosedForm(Method) { return true; }
1358};
1359
1360template <std::integral Int>
1362{
1364
1365 static std::vector<FitParameter> parameters(const Distribution &distribution)
1366 {
1367 return {{"number", static_cast<double>(distribution.GetNumber()), 1.0,
1368 static_cast<double>(std::numeric_limits<int>::max()), true, true, true},
1369 {"probability", distribution.GetProbability(), 0.0, 1.0, true, true}};
1370 }
1371
1372 static Distribution make(std::span<const double> parameters)
1373 {
1374 return Distribution(static_cast<int>(parameters[0]), parameters[1]);
1375 }
1376
1377 static Distribution initial(std::span<const Int> sample, Method method, const FixedParameterMap &fixed)
1378 {
1379 requireMethod(method, "Binomial", {Method::MLE});
1380 const auto number = fixedPositiveInteger(fixed, "number", "binomial");
1381 if (!number)
1383 "Binomial distribution: fitting currently requires a fixed number");
1384 Distribution distribution(*number);
1385 distribution.FitProbability(sample);
1386 return distribution;
1387 }
1388
1389 static bool hasClosedForm(Method) { return true; }
1390 static bool requiresOptimization(Method, const FixedParameterMap &) { return false; }
1391};
1392
1393template <std::integral Int, std::floating_point Shape>
1395{
1397
1398 static std::vector<FitParameter> parameters(const Distribution &distribution)
1399 {
1400 return {{"number", distribution.GetNumber(), 0.0, std::nullopt},
1401 {"probability", distribution.GetProbability(), 0.0, 1.0}};
1402 }
1403
1404 static Distribution make(std::span<const double> parameters)
1405 {
1406 return Distribution(static_cast<Shape>(parameters[0]), parameters[1]);
1407 }
1408
1409 static Distribution initial(std::span<const Int> sample, Method method, const FixedParameterMap &)
1410 {
1411 requireMethod(method, "Negative binomial", {Method::MLE});
1412 Distribution distribution;
1413 distribution.Fit(sample);
1414 return distribution;
1415 }
1416
1417 static bool hasClosedForm(Method) { return true; }
1418};
1419
1420template <std::integral Int>
1422{
1424
1425 static std::vector<FitParameter> parameters(const Distribution &distribution)
1426 {
1427 return {{"minimum", static_cast<double>(distribution.MinValue()),
1428 static_cast<double>(std::numeric_limits<Int>::lowest()), static_cast<double>(distribution.MaxValue()),
1429 true, false, true},
1430 {"maximum", static_cast<double>(distribution.MaxValue()), static_cast<double>(distribution.MinValue()),
1431 static_cast<double>(std::numeric_limits<Int>::max()), false, true, true}};
1432 }
1433
1434 static Distribution make(std::span<const double> parameters)
1435 {
1436 return Distribution(static_cast<Int>(parameters[0]), static_cast<Int>(parameters[1]));
1437 }
1438
1439 static Distribution initial(std::span<const Int> sample, Method method, const FixedParameterMap &)
1440 {
1441 requireMethod(method, "Uniform discrete", {Method::MLE});
1442 Distribution distribution;
1443 distribution.Fit(sample);
1444 return distribution;
1445 }
1446
1447 static bool hasClosedForm(Method) { return true; }
1448};
1449
1450template <class Distribution, class LogLikelihood>
1451FitResult<Distribution> fitLikelihood(std::span<const typename Distribution::value_type> initializationSample,
1453 bool forceOptimization = false)
1454{
1455 if (initializationSample.empty())
1456 throw fit_error(FitErrorCode::TooFewElements, "Fitting requires a non-empty sample");
1457 if constexpr (std::floating_point<typename Distribution::value_type>) {
1458 if (std::ranges::any_of(initializationSample, [](auto value) { return !std::isfinite(value); }))
1459 throw fit_error(FitErrorCode::WrongSample, "Fitting requires finite sample values");
1460 }
1461 if (!(options.tolerance > 0) || options.max_iterations <= 0)
1462 throw std::invalid_argument("Fitting tolerance and iteration limit should be positive");
1463 if (options.method != Method::MLE && !options.fixed.empty())
1464 throw fit_error(FitErrorCode::NotApplicable, "Fixed parameters are currently supported only for MLE");
1465
1466 Distribution initial = FitModel<Distribution>::initial(initializationSample, options.method, options.fixed);
1468 std::vector<double> parameters;
1469 parameters.reserve(specifications.size());
1470 std::vector<bool> fixed(specifications.size());
1471 for (std::size_t i = 0; i < specifications.size(); ++i) {
1472 auto found = options.fixed.find(specifications[i].name);
1473 if (found != options.fixed.end()) {
1474 if (!withinBounds(found->second, specifications[i]))
1475 throw std::invalid_argument("Fixed parameter is outside its bounds: " +
1476 std::string(specifications[i].name));
1477 specifications[i].value = found->second;
1478 fixed[i] = true;
1479 }
1480 parameters.push_back(specifications[i].value);
1481 }
1482 for (const auto &[name, value] : options.fixed) {
1483 (void)value;
1484 const std::string &fixedName = name;
1485 if (std::ranges::none_of(specifications,
1486 [&fixedName](const FitParameter &parameter) { return parameter.name == fixedName; }))
1487 throw std::invalid_argument("Unknown fixed parameter: " + name);
1488 }
1489
1491 std::vector<std::size_t> freeIndices;
1492 std::vector<double> unconstrained;
1493 for (std::size_t i = 0; i < parameters.size(); ++i) {
1494 if (!fixed[i]) {
1495 if (specifications[i].integral && optimize)
1497 "Free integer parameters are not supported by continuous optimization");
1498 freeIndices.push_back(i);
1499 unconstrained.push_back(toUnconstrained(parameters[i], specifications[i]));
1500 }
1501 }
1502
1503 auto naturalObjective = [&](std::span<const double> candidate) {
1505 };
1508 optimization.converged = true;
1509 if (!freeIndices.empty() && optimize) {
1510 auto objective = [&](std::span<const double> free) {
1511 std::vector<double> candidate = parameters;
1512 for (std::size_t i = 0; i < free.size(); ++i)
1515 };
1517 optimization = math::nelderMead(objective, unconstrained, options.tolerance, options.max_iterations);
1518 else {
1519 optimization = math::bfgs(objective, unconstrained, options.tolerance, options.max_iterations);
1520 if (!optimization.converged)
1521 optimization =
1522 math::nelderMead(objective, std::move(optimization.x), options.tolerance, options.max_iterations);
1523 }
1524 for (std::size_t i = 0; i < optimization.x.size(); ++i)
1526 }
1527
1528 Distribution distribution = FitModel<Distribution>::make(parameters);
1529 const double fittedLogLikelihood = logLikelihood(distribution);
1530 const std::size_t fittedParameters = freeIndices.size();
1531 FitResult<Distribution> result(std::move(distribution));
1532 result.log_likelihood = fittedLogLikelihood;
1534 result.bic =
1535 std::log(static_cast<double>(initializationSample.size())) * fittedParameters - 2 * fittedLogLikelihood;
1536 result.parameters = std::move(parameters);
1537 result.fixed = std::move(fixed);
1538 result.converged = optimization.converged;
1539 result.iterations = optimization.iterations;
1540 result.method = options.method;
1541 for (const auto &parameter : specifications)
1542 result.parameter_names.emplace_back(parameter.name);
1544 return result;
1545}
1546
1547} // namespace detail
1548
1549template <class Distribution>
1551FitResult<Distribution> fit(std::span<const typename Distribution::value_type> sample, FitOptions options = {})
1552{
1553 const auto logLikelihood = [sample](const Distribution &distribution) {
1554 return distribution.LogLikelihoodFunction(sample);
1555 };
1556 return detail::fitLikelihood<Distribution>(sample, std::move(options), logLikelihood);
1557}
1558
1559template <class Distribution>
1561FitResult<Distribution> fit(std::span<const typename Distribution::value_type> sample, Method method)
1562{
1563 FitOptions options;
1564 options.method = method;
1565 return fit<Distribution>(sample, std::move(options));
1566}
1567
1568} // namespace randlib
1569
1570#if defined(__clang__)
1571#pragma clang diagnostic pop
1572#elif defined(__GNUC__)
1573#pragma GCC diagnostic pop
1574#elif defined(_MSC_VER)
1575#pragma warning(pop)
1576#endif
The ArcsineRand class Arcsine distribution.
Definition continuous/beta.hpp:340
void FitShape(long double lnG, long double lnG1m)
set α and β, estimated via maximum likelihood, using sufficient statistics instead of the whole sampl...
Definition continuous/beta.hpp:1185
double GetShape() const noexcept
Definition continuous/beta.hpp:351
The BernoulliRand class Bernoulli distribution.
Definition bernoulli.hpp:25
double GetAlpha() const noexcept
Definition continuous/beta.hpp:128
double GetBeta() const noexcept
Definition continuous/beta.hpp:132
The BetaPrimeRand class Beta-prime distribution.
Definition beta_prime.hpp:32
double GetAlpha() const noexcept
Definition beta_prime.hpp:44
void Fit(std::span< const RealType > sample)
fit shapes by maximum-likelihood
Definition beta_prime.hpp:320
double GetBeta() const noexcept
Definition beta_prime.hpp:45
The BetaRand class Beta distribution.
Definition continuous/beta.hpp:246
void FitShapes(long double lnG, long double lnG1m, long double mean, long double variance)
set α and β, estimated via maximum likelihood, using sufficient statistics instead of the whole sampl...
Definition continuous/beta.hpp:1112
double GetProbability() const noexcept
Definition binomial.hpp:76
void FitProbability(std::span< const IntType > sample)
Fit probability p with maximum-likelihood estimation.
Definition binomial.hpp:722
IntType GetNumber() const noexcept
Definition binomial.hpp:75
The BinomialRand class Binomial distribution.
Definition binomial.hpp:197
The CauchyRand class Cauchy distribution.
Definition cauchy.hpp:26
The ChiSquaredRand class Chi-squared distribution.
Definition continuous/gamma.hpp:355
size_t GetDegree() const
Definition continuous/gamma.hpp:360
The DegenerateRand class Degenerate distribution.
Definition degenerate.hpp:25
double GetValue() const noexcept
Definition degenerate.hpp:38
void Fit(std::span< const double > sample)
Definition degenerate.hpp:163
The ErlangRand class Erlang distibution.
Definition continuous/gamma.hpp:375
The ExponentialRand class Exponential distribution.
Definition exponential.hpp:28
The FisherFRand class F-distribution.
Definition fisher_f.hpp:23
int GetSecondDegree() const noexcept
Definition fisher_f.hpp:47
int GetFirstDegree() const noexcept
Definition fisher_f.hpp:46
The FrechetRand class Frechet distribution.
Definition frechet.hpp:19
double GetShape() const noexcept
Definition frechet.hpp:37
double GetScale() const noexcept
Definition frechet.hpp:38
double GetLocation() const noexcept
Definition frechet.hpp:39
void FitRate(std::span< const RealType > sample, bool unbiased=false)
set rate, estimated via maximum-likelihood method if unbiased = false, otherwise set rate,...
Definition continuous/gamma.hpp:922
double GetShape() const noexcept
Definition continuous/gamma.hpp:78
double GetRate() const noexcept
Definition continuous/gamma.hpp:86
The GammaRand class Gamma distribution.
Definition continuous/gamma.hpp:295
void Fit(std::span< const RealType > sample)
set shape and rate, estimated via maximum-likelihood method
Definition continuous/gamma.hpp:980
double GetScale() const noexcept
Definition geometric_stable.hpp:70
Definition generalized_extreme_value.hpp:17
double GetScale() const noexcept
Definition generalized_extreme_value.hpp:54
double GetLocation() const noexcept
Definition generalized_extreme_value.hpp:55
double GetShape() const noexcept
Definition generalized_extreme_value.hpp:53
Definition generalized_pareto.hpp:16
double GetScale() const noexcept
Definition generalized_pareto.hpp:51
double GetShape() const noexcept
Definition generalized_pareto.hpp:50
The GeometricRand class Geometric distribution.
Definition geometric.hpp:24
The GumbelRand class Gumbel distribution.
Definition gumbel.hpp:22
double GetScale() const noexcept
Definition gumbel.hpp:39
double GetLocation() const noexcept
Definition gumbel.hpp:38
The InverseGammaRand class Inverse-Gamma distribution.
Definition inverse_gamma.hpp:23
double GetRate() const noexcept
Definition inverse_gamma.hpp:49
double GetShape() const noexcept
Definition inverse_gamma.hpp:48
The InverseGaussianRand class Inverse Gaussian (Wald) distribution.
Definition inverse_gaussian.hpp:19
double GetMean() const noexcept
Definition inverse_gaussian.hpp:35
double GetShape() const noexcept
Definition inverse_gaussian.hpp:36
The LaplaceRand class Laplace distribution.
Definition laplace.hpp:150
void Fit(std::span< const RealType > sample)
Definition laplace.hpp:158
The LevyRand class Levy distribution.
Definition levy.hpp:29
void FitScale(std::span< const RealType > sample)
Fit scale using maximum-likelihoood estimator.
Definition levy.hpp:140
The LogNormalRand class Log-Normal distribution.
Definition log_normal.hpp:25
void Fit(std::span< const RealType > sample)
Fit parameters via maximum-likelihood.
Definition log_normal.hpp:334
double GetScale() const
Definition log_normal.hpp:65
double GetLocation() const
Definition log_normal.hpp:64
Exponential transform of a stable distribution.
Definition log_stable.hpp:25
double GetScale() const noexcept
Definition log_stable.hpp:60
double GetSkewness() const noexcept
Definition log_stable.hpp:59
typename StableRand< RealType >::Parameterization Parameterization
Definition log_stable.hpp:34
double GetLocation() const noexcept
Definition log_stable.hpp:61
double GetExponent() const noexcept
Definition log_stable.hpp:58
The LogisticRand class Logistic distribution.
Definition logistic.hpp:24
double GetScale() const noexcept
Definition logistic.hpp:42
double GetLocation() const noexcept
Definition logistic.hpp:41
T GetNumber() const noexcept
Definition negative_binomial.hpp:60
double GetProbability() const noexcept
Definition negative_binomial.hpp:59
The NegativeBinomialRand class Negative binomial distribution.
Definition negative_binomial.hpp:133
void Fit(std::span< const IntType > sample)
set number and probability, estimated via maximum-likelihood method
Definition negative_binomial.hpp:417
The NoncentralChiSquaredRand class Noncentral Chi-Squared distribution.
Definition noncentral_chi_squared.hpp:24
double GetNoncentrality() const noexcept
Definition noncentral_chi_squared.hpp:47
double GetDegree() const noexcept
Definition noncentral_chi_squared.hpp:46
Noncentral Student's t-distribution.
Definition noncentral_t.hpp:28
double GetDegree() const noexcept
Definition noncentral_t.hpp:58
double GetNoncentrality() const noexcept
Definition noncentral_t.hpp:59
The NormalRand class Normal distribution.
Definition normal.hpp:31
void Fit(std::span< const RealType > sample, bool unbiased=false)
set parameters, returned by maximium-likelihood estimator if unbiased = false, otherwise set paramete...
Definition normal.hpp:423
The ParetoRand class Pareto distribution.
Definition pareto.hpp:24
void Fit(std::span< const RealType > sample, bool unbiased=false)
Fit parameters to maximum-likelihood estimators or to UMVU if unbiased == true.
Definition pareto.hpp:350
double GetScale() const noexcept
Definition pareto.hpp:44
double GetShape() const noexcept
Definition pareto.hpp:43
The PoissonRand class Poisson distribution.
Definition poisson.hpp:26
double GetRate() const noexcept
Definition poisson.hpp:53
void Fit(std::span< const IntType > sample)
fit rate λ via maximum-likelihood method
Definition poisson.hpp:474
The RayleighRand class Rayleigh distribution.
Definition nakagami.hpp:191
void Fit(std::span< const RealType > sample, bool unbiased=false)
fit scale via maximum-likelihood method if unbiased = false, otherwise set scale, returned by uniform...
Definition nakagami.hpp:675
double GetScale() const noexcept
Definition nakagami.hpp:207
double GetShift() const noexcept
Definition laplace.hpp:98
Stable Count distribution.
Definition stable_count.hpp:39
double GetExponent() const noexcept
Definition stable_count.hpp:75
double GetLocation() const noexcept
Definition stable_count.hpp:77
double GetScale() const noexcept
Definition stable_count.hpp:76
double GetScale() const noexcept
Definition stable.hpp:164
long double Variance() const override
Definition stable.hpp:1800
double GetExponent() const noexcept
Definition stable.hpp:156
double GetLocation() const noexcept
Definition stable.hpp:168
double GetSkewness() const noexcept
Definition stable.hpp:160
The StableRand class Stable distribution.
Definition stable.hpp:521
typename StableDistribution< RealType >::Parameterization Parameterization
Definition stable.hpp:523
The StudentTRand class Student's t-distribution.
Definition student_t.hpp:22
double GetLocation() const noexcept
Definition student_t.hpp:47
double GetScale() const noexcept
Definition student_t.hpp:48
double GetDegree() const noexcept
Definition student_t.hpp:46
The UniformDiscreteRand class Uniform discrete distribution.
Definition uniform_discrete.hpp:23
IntType MinValue() const override
Definition uniform_discrete.hpp:36
IntType MaxValue() const override
Definition uniform_discrete.hpp:37
void Fit(std::span< const IntType > sample)
fit bounds via maximum-likelihood method
Definition uniform_discrete.hpp:226
The UniformRand class Uniform continuous distribution.
Definition continuous/uniform.hpp:30
void Fit(std::span< const RealType > sample, bool unbiased=false)
fit support with maximum-likelihood estimator if unbiased == false, fit support using UMVU estimator ...
Definition continuous/uniform.hpp:331
RealType MinValue() const override
Definition continuous/uniform.hpp:40
RealType MaxValue() const override
Definition continuous/uniform.hpp:41
The WeibullRand class Weibull distribution.
Definition weibull.hpp:21
double GetScale() const noexcept
Definition weibull.hpp:38
double GetShape() const noexcept
Definition weibull.hpp:39
The fit_error class Thrown when a distribution cannot be fitted to a sample; code() tells why.
Definition errors.hpp:36
Definition mle.hpp:91
constexpr long double egamma
Definition constants.hpp:36
double toUnconstrained(double value, const FitParameter &parameter)
Definition mle.hpp:99
std::map< std::string, double, std::less<> > FixedParameterMap
Definition mle.hpp:85
bool withinBounds(double value, const FitParameter &parameter)
Definition mle.hpp:130
void addUncertainty(FitResult< Distribution > &result, std::span< const FitParameter > specifications, const Objective &naturalObjective)
Definition mle.hpp:267
double bilinearInterpolation(const std::array< double, Rows > &rowGrid, const std::array< double, Columns > &columnGrid, const std::array< std::array< double, Columns >, Rows > &values, double row, double column)
Definition mle.hpp:185
std::vector< std::vector< double > > inverse(std::vector< std::vector< double > > matrix)
Definition mle.hpp:218
FitResult< Distribution > fitLikelihood(std::span< const typename Distribution::value_type > initializationSample, FitOptions options, const LogLikelihood &logLikelihood, bool forceOptimization=false)
Definition mle.hpp:1451
double fromUnconstrained(double value, const FitParameter &parameter)
Definition mle.hpp:112
double sampleQuantile(std::span< const T > sample, double probability)
Definition mle.hpp:173
DoublePair sampleMeanAndVariance(std::span< const T > sample)
Definition mle.hpp:160
std::optional< int > fixedPositiveInteger(const FixedParameterMap &fixed, std::string_view name, std::string_view distribution)
Definition mle.hpp:205
bool requiresOptimization(Method method, const FixedParameterMap &fixed)
Definition mle.hpp:152
double negativeLogLikelihood(std::span< const double > parameters, const LogLikelihood &logLikelihood)
Definition mle.hpp:255
Real standardNormal(G &g)
Ziggurat algorithm of Marsaglia and Tsang with 256 layers and an explicit sign bit.
Definition variates.hpp:45
void requireMethod(Method method, std::string_view distribution, std::initializer_list< Method > supported={Method::MLE, Method::MethodOfMoments})
Definition mle.hpp:143
MinimizeResult< Real > bfgs(const F &f, std::vector< Real > start, Real tolerance=1e-8, int maxIterations=1000)
Quasi-Newton minimization of Broyden, Fletcher, Goldfarb and Shanno with central-difference gradients...
Definition optimize.hpp:437
std::vector< std::vector< Real > > numericalHessian(const F &f, const std::vector< Real > &x)
Definition optimize.hpp:390
MinimizeResult< Real > nelderMead(const F &f, std::vector< Real > start, Real tolerance=1e-10, int maxIterations=20000)
Downhill simplex method of Nelder and Mead with the dimension-dependent coefficients of Gao and Han (...
Definition optimize.hpp:273
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
@ WrongReturn
the method returned invalid parameters
@ TooFewElements
the sample is too small
Method
Definition result.hpp:12
Pair< double > DoublePair
Definition fwd.hpp:39
FitResult< Distribution > fit(std::span< const typename Distribution::value_type > sample, FitOptions options={})
Definition mle.hpp:1551
Definition result.hpp:27
Method method
Definition result.hpp:28
Definition result.hpp:36
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1048
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1041
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1046
static bool hasClosedForm(Method)
Definition mle.hpp:1056
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1317
static bool hasClosedForm(Method)
Definition mle.hpp:1333
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1322
static Distribution initial(std::span< const int > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1324
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1064
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1070
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1072
static bool hasClosedForm(Method)
Definition mle.hpp:1080
static bool hasClosedForm(Method)
Definition mle.hpp:1033
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1017
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1023
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1025
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1372
static bool requiresOptimization(Method, const FixedParameterMap &)
Definition mle.hpp:1390
static bool hasClosedForm(Method)
Definition mle.hpp:1389
static Distribution initial(std::span< const Int > sample, Method method, const FixedParameterMap &fixed)
Definition mle.hpp:1377
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1365
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:414
static bool hasClosedForm(Method)
Definition mle.hpp:423
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:412
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:406
static bool requiresOptimization(Method, const FixedParameterMap &)
Definition mle.hpp:962
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:935
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &fixed)
Definition mle.hpp:940
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:930
static bool hasClosedForm(Method)
Definition mle.hpp:961
static bool hasClosedForm(Method)
Definition mle.hpp:1286
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1276
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1271
static Distribution initial(std::span< const double > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1278
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:976
static bool requiresOptimization(Method, const FixedParameterMap &)
Definition mle.hpp:1009
static bool hasClosedForm(Method)
Definition mle.hpp:1008
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &fixed)
Definition mle.hpp:981
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:970
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:888
static bool hasClosedForm(Method)
Definition mle.hpp:898
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:890
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:883
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &fixed)
Definition mle.hpp:737
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:732
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:726
static bool requiresOptimization(Method, const FixedParameterMap &)
Definition mle.hpp:764
static bool hasClosedForm(Method)
Definition mle.hpp:763
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:632
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:639
static bool hasClosedForm(Method)
Definition mle.hpp:654
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:644
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:912
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:914
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:906
static bool hasClosedForm(Method)
Definition mle.hpp:922
static bool hasClosedForm(Method)
Definition mle.hpp:718
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:707
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:702
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:695
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:668
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:673
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:662
static bool hasClosedForm(Method)
Definition mle.hpp:687
static Distribution initial(std::span< const Int > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1348
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1346
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1341
static bool hasClosedForm(Method)
Definition mle.hpp:1357
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:614
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:612
static bool hasClosedForm(Method)
Definition mle.hpp:624
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:606
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:780
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:778
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:772
static bool hasClosedForm(Method)
Definition mle.hpp:793
static bool hasClosedForm(Method)
Definition mle.hpp:822
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:809
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:807
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:801
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1145
static bool hasClosedForm(Method)
Definition mle.hpp:1153
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1137
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1143
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &fixed)
Definition mle.hpp:1201
static bool hasClosedForm(Method)
Definition mle.hpp:1216
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1199
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1193
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:858
static bool hasClosedForm(Method)
Definition mle.hpp:875
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:867
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:865
typename Distribution::Parameterization Parameterization
Definition mle.hpp:532
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:534
static bool hasClosedForm(Method method)
Definition mle.hpp:560
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &fixed)
Definition mle.hpp:547
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:542
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1169
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1167
static bool hasClosedForm(Method)
Definition mle.hpp:1185
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1161
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1398
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1404
static bool hasClosedForm(Method)
Definition mle.hpp:1417
static Distribution initial(std::span< const Int > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1409
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:830
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:836
static bool hasClosedForm(Method)
Definition mle.hpp:850
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:838
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &fixed)
Definition mle.hpp:385
static bool hasClosedForm(Method)
Definition mle.hpp:398
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:383
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:377
static bool hasClosedForm(Method)
Definition mle.hpp:339
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:331
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:329
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:323
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1088
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1096
static bool hasClosedForm(Method)
Definition mle.hpp:1104
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1094
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1294
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1299
static Distribution initial(std::span< const Int > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1301
static bool hasClosedForm(Method)
Definition mle.hpp:1309
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1229
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1231
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1224
static bool hasClosedForm(Method)
Definition mle.hpp:1239
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &fixed)
Definition mle.hpp:580
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:575
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:568
static bool hasClosedForm(Method)
Definition mle.hpp:598
typename Distribution::Parameterization Parameterization
Definition mle.hpp:430
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:432
static bool hasClosedForm(Method method)
Definition mle.hpp:524
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:445
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:440
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:359
static bool hasClosedForm(Method)
Definition mle.hpp:369
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:347
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:354
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1425
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1434
static bool hasClosedForm(Method)
Definition mle.hpp:1447
static Distribution initial(std::span< const Int > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1439
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1255
static bool hasClosedForm(Method)
Definition mle.hpp:1263
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1247
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1253
static Distribution make(std::span< const double > parameters)
Definition mle.hpp:1118
static bool hasClosedForm(Method)
Definition mle.hpp:1129
static std::vector< FitParameter > parameters(const Distribution &distribution)
Definition mle.hpp:1112
static Distribution initial(std::span< const Real > sample, Method method, const FixedParameterMap &)
Definition mle.hpp:1120
Definition mle.hpp:88
Definition mle.hpp:75
double value
Definition mle.hpp:77
bool upperInclusive
Definition mle.hpp:81
std::optional< double > lower
Definition mle.hpp:78
std::optional< double > upper
Definition mle.hpp:79
bool integral
Definition mle.hpp:82
bool lowerInclusive
Definition mle.hpp:80
std::string_view name
Definition mle.hpp:76
The MinimizeResult struct Outcome of a multivariate minimization.
Definition optimize.hpp:255
std::vector< Real > x
point of the minimum
Definition optimize.hpp:256