35double standard_logistic_moment(
unsigned k)
37 if (k % 2 == 1)
return 0.0;
38 const double pi2 = M_PI * M_PI;
41 case 2:
return pi2 / 3.0;
42 case 4:
return 7.0 * pi2 * pi2 / 15.0;
43 case 6:
return 31.0 * pi2 * pi2 * pi2 / 21.0;
49double sigmoid(
double z)
51 if (z >= 0.0)
return 1.0 / (1.0 + std::exp(-z));
52 const double e = std::exp(z);
60 const DistributionFamily &family()
const override;
61 double mean()
const override {
return p1_; }
62 bool meanIsAffine()
const override {
return true; }
63 double variance()
const override {
64 return M_PI * M_PI * p2_ * p2_ / 3.0;
66 double rawMoment(
unsigned k)
const override {
67 if (k == 0)
return 1.0;
68 if (k == 1)
return mean();
71 for (
unsigned i = 0; i <= k; ++i) {
72 const double ez = standard_logistic_moment(i);
73 if (std::isnan(ez))
return kNaN;
74 if (ez == 0.0)
continue;
76 * std::pow(p1_,
static_cast<double>(k - i))
77 * std::pow(p2_,
static_cast<double>(i))
82 double pdf(
double c)
const override {
83 if (!(p2_ > 0.0))
return kNaN;
85 const double z = (c - p1_) / p2_;
86 const double e = std::exp(-std::fabs(z));
87 return e / ((1.0 + e) * (1.0 + e) * p2_);
89 double cdf(
double c)
const override {
90 if (!(p2_ > 0.0))
return kNaN;
91 return sigmoid((c - p1_) / p2_);
93 DistSupport support()
const override {
return {-
kInf,
kInf}; }
94 bool integrationRange(
double &lo,
double &hi)
const override {
95 if (!(p2_ > 0.0))
return false;
97 lo = p1_ - 30.0 * p2_;
98 hi = p1_ + 30.0 * p2_;
101 std::pair<double, double> plotRange(
double trunc_lo,
102 double trunc_hi)
const override {
103 double lo = trunc_lo, hi = trunc_hi;
104 if (!std::isfinite(lo)) lo = p1_ - 6.0 * p2_;
105 if (!std::isfinite(hi)) hi = p1_ + 6.0 * p2_;
108 double sample(std::mt19937_64 &rng)
const override {
109 std::uniform_real_distribution<double> U01(0.0, 1.0);
111 static constexpr double EPS = 1e-15;
112 if (u < EPS) u = EPS;
113 if (u > 1.0 - EPS) u = 1.0 - EPS;
114 return p1_ + p2_ * std::log(u / (1.0 - u));
116 std::optional<double> quantile(
double p)
const override {
117 if (!(p2_ > 0.0))
return std::nullopt;
118 return p1_ + p2_ * std::log(p / (1.0 - p));
120 std::optional<std::vector<double>> sampleTruncated(
121 std::mt19937_64 &rng,
double lo,
double hi,
unsigned n)
const override {
122 if (!(p2_ > 0.0))
return std::nullopt;
123 const double Fa = std::isfinite(lo) ? sigmoid((lo - p1_) / p2_) : 0.0;
124 const double Fb = std::isfinite(hi) ? sigmoid((hi - p1_) / p2_) : 1.0;
125 if (!(Fa < Fb))
return std::nullopt;
126 static constexpr double EPS = 1e-15;
127 std::uniform_real_distribution<double> U01(0.0, 1.0);
128 std::vector<double> out;
130 for (
unsigned i = 0; i < n; ++i) {
131 double u = Fa + U01(rng) * (Fb - Fa);
132 if (u < EPS) u = EPS;
133 if (u > 1.0 - EPS) u = 1.0 - EPS;
134 out.push_back(p1_ + p2_ * std::log(u / (1.0 - u)));
138 std::unique_ptr<Distribution> affine(
double a,
double b)
const override {
139 if (a == 0.0)
return nullptr;
141 if (b != 0.0) mu += b;
142 return std::make_unique<LogisticDistribution>(mu, std::fabs(a) * p2_);
144 std::string serialise()
const override {
147 std::optional<double> asDirac()
const override {
148 if (p2_ == 0.0)
return p1_;
154 "logistic", 2,
"Lgs", {
"μ",
"s"},
155 +[](
double p1,
double p2) -> std::unique_ptr<Distribution> {
156 return std::make_unique<LogisticDistribution>(p1, p2);
161 return logistic_family;
164[[maybe_unused]]
const DistributionFamilyRegistrar logistic_family_registrar(
Internal helpers shared by the per-family Distribution implementations under src/distributions/.
Base holding the two parameters; subclasses add closed forms.
BaseDistribution(double p1, double p2)
double binomial_coeff(unsigned n, unsigned k)
C(n, k) as a double (exact for the small moment orders used here).
std::string double_to_text(double v)
Format a double back into the canonical text form used by gate_value extras and gate_rv distribution ...
A registered family's descriptor: its complete identity.