LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
fj_ordstat_exp.h
Go to the documentation of this file.
1
/*
2
* Copyright (c) 2012-2026, QORE Lab, Imperial College London
3
* All rights reserved.
4
*/
5
#ifndef LINE_API_FJ_ORDSTAT_EXP_H
6
#define LINE_API_FJ_ORDSTAT_EXP_H
7
8
/**
9
* @file
10
* @ingroup api_fj
11
* Mean of the k-th smallest of n independent EXPONENTIAL branch completion
12
* times, i.e. the instant a k-of-n (quorum) join fires. k = n is the ordinary
13
* AND-join, the maximum, and k = 1 the minimum.
14
*
15
* Templated port of matlab/src/api/fj/fj_ordstat_exp.m, cross-checked against
16
* jar/src/main/java/jline/api/fj/FJ_ordstat_exp.java.
17
*
18
* With lambda_i = 1/ri(i) and m = n-k stragglers allowed,
19
*
20
* E[X_(k)] = sum_{j=m+1..n} (-1)^(j-m-1) C(j-1,m) e_j,
21
* e_j = sum_{|S|=j} 1 / sum_{i in S} lambda_i
22
*
23
* the inclusion-exclusion identity for the order statistics of independent
24
* exponentials. At m = 0 it collapses to sum_j (-1)^(j-1) e_j, the classical
25
* expression for the maximum, TERM BY TERM: a full join therefore evaluates
26
* exactly as it did before this header existed, which is what keeps the MMT
27
* fork-join fixed point bit-identical on a standard join.
28
*
29
* The exact path is RATIONAL -- reciprocals, sums and integer binomials only --
30
* so it holds in an exact field and is not gated on has_transcendental. Only
31
* the large-n fallback is, since fj_quorum_moments fits a standard deviation.
32
*
33
* The sum has 2^n terms and its signs alternate, so it is evaluated exactly
34
* only while the branch count is small. Beyond FJ_ORDSTAT_MAX_EXACT branches a
35
* genuine quorum (k < n) is evaluated by fj_quorum_moments instead, whose
36
* Poisson-binomial recurrence adds no cancellation; a full join keeps the exact
37
* path at every n so that no existing result moves.
38
*
39
* Reference: A. Thomasian, "Analysis of Fork/Join and Related Queueing
40
* Systems", ACM Computing Surveys 47(2), Article 17, 2014, Sec. 3 (Eq. 18-19).
41
*/
42
43
#include <cmath>
44
#include <cstddef>
45
#include <vector>
46
47
#include "
line/api/fj/fj_quorum_moments.h
"
48
#include "
line/api/fj/fj_types.h
"
49
#include "
line/num/number.h
"
50
#include "
line/util/error.h
"
51
52
namespace
line
{
53
namespace
fj
{
54
55
/** Branch count above which a genuine quorum leaves the exact alternating sum. */
56
static
const
std::size_t
FJ_ORDSTAT_MAX_EXACT
= 15;
57
58
namespace
detail {
59
60
/**
61
* The large-n quorum fallback, compiled only where the arithmetic supports it.
62
* An exact field has no sqrt, so there it refuses rather than silently
63
* degrading to the alternating sum it was reached to avoid.
64
*/
65
template <class T, bool HasTranscendental = num_traits<T>::has_transcendental>
66
struct
OrdstatExpFallback {
67
static
T eval(
const
std::vector<T>& ri, std::size_t k) {
68
// Branch times are taken as exponential, so the variance is the square
69
// of the mean.
70
std::vector<T> var(ri.size());
71
for
(std::size_t i = 0; i < ri.size(); ++i) var[i] = ri[i] * ri[i];
72
return
fj_quorum_moments
(ri, var, k).m;
73
}
74
};
75
76
template
<
class
T>
77
struct
OrdstatExpFallback<T, false> {
78
static
T eval(
const
std::vector<T>&, std::size_t) {
79
throw
InputError
(
80
"fj_ordstat_exp: a quorum over more than FJ_ORDSTAT_MAX_EXACT branches needs the "
81
"two-moment fallback, which requires transcendental arithmetic"
);
82
}
83
};
84
85
}
// namespace detail
86
87
/**
88
* @brief Mean of the k-th smallest of n independent EXPONENTIAL branch
89
* completion times, i.e. the instant a k-of-n (quorum) join fires.
90
*
91
* @param ri branch completion time means
92
* @param k quorum, 1 <= k <= ri.size()
93
* @return the mean instant the k-of-n join fires
94
*/
95
template
<
class
T>
96
T
fj_ordstat_exp
(
const
std::vector<T>& ri, std::size_t k) {
97
const
T zero =
num_traits<T>::from_int
(0), one =
num_traits<T>::from_int
(1);
98
std::vector<T> r;
99
r.reserve(ri.size());
100
for
(std::size_t i = 0; i < ri.size(); ++i)
101
// an exact field carries no NaN or infinity, so the test goes through
102
// the double image, which is where a branch time can be either
103
if
(std::isfinite(
num_traits<T>::to_double
(ri[i]))) r.push_back(ri[i]);
104
std::size_t n = r.size();
105
if
(n == 0)
return
zero;
106
if
(k < 1 || k > n)
throw
InputError
(
"fj_ordstat_exp: k must satisfy 1 <= k <= n"
);
107
// A branch of zero mean completes instantly: it never delays the join and it
108
// counts toward the quorum at once. Removing it here keeps the reciprocal
109
// below finite, which the exact field requires and IEEE only tolerates.
110
std::size_t nzero = 0;
111
for
(std::size_t i = 0; i < n; ++i)
112
if
(!(r[i] > zero)) ++nzero;
113
if
(nzero > 0) {
114
if
(k <= nzero)
return
zero;
115
k -= nzero;
116
std::vector<T> pos;
117
pos.reserve(n - nzero);
118
for
(std::size_t i = 0; i < n; ++i)
119
if
(r[i] > zero) pos.push_back(r[i]);
120
r.swap(pos);
121
n = r.size();
122
}
123
if
(n == 1)
return
r[0];
124
125
if
(k < n && n >
FJ_ORDSTAT_MAX_EXACT
)
return
detail::OrdstatExpFallback<T>::eval(r, k);
126
127
std::vector<T> lambdai(n);
128
for
(std::size_t i = 0; i < n; ++i) lambdai[i] = one / r[i];
129
130
const
std::size_t nstrag = n - k;
131
T total = zero;
132
// Walk the subsets of each size j by an index vector, so no 2^n table is
133
// materialised and the exact field never leaves the loop.
134
for
(std::size_t j = nstrag + 1; j <= n; ++j) {
135
T ej = zero;
136
std::vector<std::size_t> idx(j);
137
for
(std::size_t i = 0; i < j; ++i) idx[i] = i;
138
while
(
true
) {
139
T s = zero;
140
for
(std::size_t i = 0; i < j; ++i) s = s + lambdai[idx[i]];
141
ej = ej + one / s;
142
// next combination in lexicographic order
143
std::size_t p = j;
144
while
(p > 0 && idx[p - 1] == n - j + (p - 1)) --p;
145
if
(p == 0)
break
;
146
++idx[p - 1];
147
for
(std::size_t i = p; i < j; ++i) idx[i] = idx[i - 1] + 1;
148
}
149
// C(j-1, nstrag) is an integer and stays one in the field
150
long
long
binom = 1;
151
for
(std::size_t i = 0; i < nstrag; ++i)
152
binom = binom *
static_cast<
long
long
>
(j - 1 - i) /
static_cast<
long
long
>
(i + 1);
153
const
T term =
num_traits<T>::from_int
(
static_cast<
int
>
(binom)) * ej;
154
if
(((j - nstrag - 1) % 2) == 0)
155
total = total + term;
156
else
157
total = total - term;
158
}
159
return
total;
160
}
161
162
}
// namespace fj
163
}
// namespace line
164
165
#endif
// LINE_API_FJ_ORDSTAT_EXP_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
error.h
The exception types the port throws.
fj_quorum_moments.h
Mean and variance of a k-of-n (quorum) join completion time, from the mean and variance of each branc...
fj_types.h
Shared return types and arithmetic helpers for the templated fork-join port.
line::fj
Definition
fj_amva.h:34
line::fj::fj_ordstat_exp
T fj_ordstat_exp(const std::vector< T > &ri, std::size_t k)
Mean of the k-th smallest of n independent EXPONENTIAL branch completion times, i....
Definition
fj_ordstat_exp.h:96
line::fj::FJ_ORDSTAT_MAX_EXACT
static const std::size_t FJ_ORDSTAT_MAX_EXACT
Branch count above which a genuine quorum leaves the exact alternating sum.
Definition
fj_ordstat_exp.h:56
line::fj::fj_quorum_moments
FJQuorumMomentsResult< T > fj_quorum_moments(const std::vector< T > &branchMeans, const std::vector< T > &branchVars, std::size_t k)
Mean and variance of a k-of-n (quorum) join completion time, from the mean and variance of each branc...
Definition
fj_quorum_moments.h:107
line
Definition
aoi_dist2ph.h:52
number.h
Number-type abstraction for the templated API port.
line::num_traits
Definition
number.h:111
include
line
api
fj
fj_ordstat_exp.h
Generated by
1.18.0