LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
fj_quorum_moments.m
1function [m, v] = fj_quorum_moments(branchMeans, branchVars, k)
2% FJ_QUORUM_MOMENTS Mean and variance of a k-of-n (quorum) join completion time.
3%
4% [M, V] = FJ_QUORUM_MOMENTS(BRANCHMEANS, BRANCHVARS, K) returns the first two
5% moments of the K-th smallest of N independent branch completion times, each
6% branch being given by its mean and variance.
7%
8% Each branch is expanded into a discrete step CDF by a two-moment fit. At each
9% time the number of completed branches is Poisson-binomial, so its distribution
10% is built by the recurrence q_j <- q_{j-1}*F_i + q_j*(1-F_i) over branches i, and
11% the K-th order statistic is the upper tail sum_{j>=K} q_j. For K = N this
12% reduces to the product of the branch CDFs, i.e. the ordinary AND-join, and for
13% K = 1 to 1 - prod(1 - F_i), i.e. the minimum.
14%
15% This is the same quantity as the inclusion-exclusion identity
16% F_(k)(t) = sum_{i=k..n} (-1)^(i-k) * C(i-1, k-1) * e_i(F_1(t), ..., F_n(t))
17% used by LQNS, but every term here is a probability in [0,1] and none is
18% subtracted, so it avoids the catastrophic cancellation the alternating binomial
19% sum incurs as N grows.
20%
21% Follows the formulation of Omari, Franks, Woodside and Pan, as implemented in
22% LQNS 6.x (randomvar.cc). Mirrors jline.api.fj.FJ_quorum in the Java runtime.
23
24MAX_BRANCHES = 512;
25
26n = numel(branchMeans);
27if n ~= numel(branchVars)
28 line_error(mfilename, 'branchMeans and branchVars must have the same number of elements.');
29end
30if n == 0
31 m = 0; v = 0; return;
32end
33if k < 1 || k > n
34 line_error(mfilename, sprintf('k must satisfy 1 <= k <= n. Got k=%d, n=%d.', k, n));
35end
36if n > MAX_BRANCHES
37 % Evaluation is cubic in the branch count; fail immediately rather than hang.
38 line_error(mfilename, sprintf(['quorum join has %d branches, above the supported maximum ' ...
39 'of %d; evaluation is cubic in the branch count.'], n, MAX_BRANCHES));
40end
41
42% Fit each branch to a three-point discrete distribution matching its moments.
43branches = cell(1, n);
44for i = 1:n
45 branches{i} = local_threePointFit(branchMeans(i), branchVars(i));
46 if isempty(branches{i}.t)
47 % A branch with no mass completes instantly, so that it neither delays the
48 % join nor suppresses the completion counts below.
49 branches{i} = struct('t', 0, 'A', 1);
50 end
51end
52
53% Evaluate the order statistic pointwise on the union of the branch grids.
54grid = [];
55for i = 1:n
56 grid = [grid, branches{i}.t]; %#ok<AGROW>
57end
58grid = unique(grid);
59
60ngrid = numel(grid);
61
62% Tabulate each branch along the merged grid by a single monotone walk, so the
63% evaluation below is linear rather than quadratic in the grid size.
64fv = zeros(n, ngrid);
65for i = 1:n
66 bt = branches{i}.t; bA = branches{i}.A;
67 p = 1; cur = 0;
68 for g = 1:ngrid
69 while p <= numel(bt) && bt(p) <= grid(g)
70 cur = bA(p);
71 p = p + 1;
72 end
73 fv(i, g) = cur;
74 end
75end
76
77total = struct('t', grid, 'A', zeros(1, ngrid));
78for g = 1:ngrid
79 q = zeros(1, n + 1);
80 q(1) = 1; % q(j+1) holds P(exactly j branches complete)
81 for i = 1:n
82 f = fv(i, g);
83 for j = min(i, n):-1:1
84 q(j + 1) = q(j) * f + q(j + 1) * (1 - f);
85 end
86 q(1) = q(1) * (1 - f);
87 end
88 total.A(g) = sum(q((k + 1):(n + 1)));
89end
90
91m = local_mean(total);
92v = local_variance(total, m);
93end
94
95function v = local_at(f, x)
96% Value of the step function at time x.
97v = 0;
98for i = 1:numel(f.t)
99 if f.t(i) > x
100 break;
101 end
102 v = f.A(i);
103end
104end
105
106function f = local_threePointFit(mu, var)
107% Two-moment fit to a three-point discrete distribution.
108if mu < 0 || var < 0
109 line_error(mfilename, 'mean and variance must be non-negative.');
110end
111if mu == 0
112 f = struct('t', [], 'A', []); return;
113end
114sd = sqrt(var);
115if sd == 0
116 f = struct('t', mu, 'A', 1); return;
117end
118if mu > sd
119 t1 = mu - sd;
120else
121 t1 = 0;
122end
123t2 = mu;
124if sd >= mu
125 t3 = mu + 2 * var / mu;
126else
127 t3 = mu + 2 * sd;
128end
129delta = t1^2 * (t3 - t2) + t2^2 * (t1 - t3) + t3^2 * (t2 - t1);
130if delta == 0
131 % The abscissae are not distinct, so the fit is undetermined.
132 f = struct('t', mu, 'A', 1); return;
133end
134temp = var + mu^2;
135a1 = (temp * (t3 - t2) + t2^2 * (mu - t3) + t3^2 * (t2 - mu)) / delta;
136a3 = (t1^2 * (mu - t2) + t2^2 * (t1 - mu) + temp * (t2 - t1)) / delta;
137f = struct('t', [t1, t2, t3], 'A', [a1, 1 - a3, 1]);
138end
139
140function m = local_mean(f)
141m = 0; prev = 0;
142for i = 1:numel(f.t)
143 m = m + (f.A(i) - prev) * f.t(i);
144 prev = f.A(i);
145end
146end
147
148function v = local_variance(f, m)
149v = 0; prev = 0;
150for i = 1:numel(f.t)
151 v = v + (f.A(i) - prev) * (f.t(i) - m)^2;
152 prev = f.A(i);
153end
154v = max(v, 0);
155end
156
Definition Station.m:245