1function [M2a, M3a] = aph2_adjust(M1, M2, M3, method)
2% Finds feasible values of M2 and M3
for an APH(2) distribution as close as
3% possible to the values provided as an input.
5% M1, M2, M3: the first three moments of the distribution
6% method:
string with the method name, can be one of the following
7% simple (default): [Telek and Heindl, 2002]
8% opt_param: optimize in parameter space
9% opt_param_gads: optimize in parameter space (global)
10% opt_char: optimize in characteristics space
11% opt_char_gads: optimize in characteristics space (global)
13% M2a: adjusted value for M2
14% M3a: adjusted value for M3
20% tolerance for strict inequalities
23if strcmp(method, 'simple')
25 scv = ((M2-M1sq)/M1sq);
27 % (M2 - M1^2)/M1^2 = 1/2
28 % M2 - M1^2 = 1/2 M1^2
31 scva = ((M2a-M1sq)/M1sq);
37 lb = 3*M1^3*(3*scva-1+sqrt(2)*(1-scva)^(3/2));
47 lb = 3/2*M1^3*(1+scva)^2;
54elseif strcmp(method, 'opt_char') || strcmp(method, 'opt_char_gads')
55 % optimize in moment space
56 if strcmp(method, 'opt_char_gads')
57 prob1 = createOptimProblem('fmincon', ...
58 'objective', @fun, ...
62 'nonlcon', @nonlcon1);
63 prob2 = createOptimProblem('fmincon', ...
64 'objective', @fun, ...
68 'nonlcon', @nonlcon2);
69 % adjustment to make the first fit feasible
70 x = run(GlobalSearch('Display','iter'), prob1);
73 % adjustment to make the second fit feasible
74 x = run(GlobalSearch('Display','iter'), prob2);
78 options = optimset('Display', 'off','Algorithm','active-set');
79 x = fmincon(@fun, [M2 M3], [], [], [], [], [0 0], [], @nonlcon1, options);
82 x = fmincon(@fun, [M2 M3], [], [], [], [], [0 0], [], @nonlcon2, options);
86 % pick the smallest adjustment
87 if norm([M2a1 M3a1] - [M2 M3]) < norm([M2a2 M3a2] - [M2 M3])
94elseif strcmp(method, 'opt_param') || strcmp(method, 'opt_param_gads')
95 % optimize in parameter space
97 degentol = 1e-8; % set to zero to allow exponential
98 x0 = [M1 1/2]; % initial solution that fits M1 exactly
99 lb = [feastol degentol];
100 ub = [inf 1-degentol];
101 if strcmp(method, 'opt_param_gads')
102 prob = createOptimProblem('fmincon', ...
103 'objective', @fun2, ...
107 'nonlcon', @nonlcon3);
108 x = run(GlobalSearch('Display','none'), prob);
110 options = optimset('Display', 'none','Algorithm','active-set');
111 x = fmincon(@fun2, x0, [], [], [], [], lb, ub, @nonlcon3, options);
116 M2a = 2*l1^2 + 2*r1*l1*l2 + 2*r1*l2^2;
117 M3a = 6*l1^3 + 6*r1*l1^2*l2 + 6*r1*l1*l2^2 + 6*r1*l2^3;
119 error('Invalid method: %s', method);
122 function obj = fun2(x)
126 xM2 = 2*l1^2 + 2*r1*l1*l2 + 2*r1*l2^2;
127 xM3 = 6*l1^3 + 6*r1*l1^2*l2 + 6*r1*l1*l2^2 + 6*r1*l2^3;
128 obj = norm([M2 M3] - [xM2 xM3]);
131 function [c, ceq] = nonlcon3(x)
134 c = l2*r1 - M1 + feastol;
138 % minimize euclidean distance to sample values of M2 and M3
139 function obj = fun(x)
140 obj = norm(x - [M2 M3]);
143 function [p1,l1,l2,tmp0] = aph2_fit1(xM2,xM3)
144 tmp0 = (8*M1^3*xM3)/3 - 3*M1^2*xM2^2 - 2*M1*xM2*xM3 + 2*xM2^3 + xM3^2/9;
145 % Clamped: tmp0 < 0 means this inversion has no real solution, which
146 %
is exactly what constraint c(1) = -tmp0 reports. Taking the raw
147 % sqrt there made l1, l2 and p1 COMPLEX, so the whole constraint
148 % vector was complex and fmincon stopped at its (infeasible) start
149 % point, returning the input moments unchanged.
150 tmp1 = 3*sqrt(max(tmp0,0));
151 tmp2 = xM3 - 3*M1*xM2;
152 tmp3 = (6*xM2 - 12*M1^2);
153 l1 = (tmp2 + tmp1)/tmp3;
154 l2 = (tmp2 - tmp1)/tmp3;
158 function [p1,l1,l2,tmp0] = aph2_fit2(xM2,xM3)
159 tmp0 = (8*M1^3*xM3)/3 - 3*M1^2*xM2^2 - 2*M1*xM2*xM3 + 2*xM2^3 + xM3^2/9;
160 % Clamped: tmp0 < 0 means this inversion has no real solution, which
161 %
is exactly what constraint c(1) = -tmp0 reports. Taking the raw
162 % sqrt there made l1, l2 and p1 COMPLEX, so the whole constraint
163 % vector was complex and fmincon stopped at its (infeasible) start
164 % point, returning the input moments unchanged.
165 tmp1 = 3*sqrt(max(tmp0,0));
166 tmp2 = xM3 - 3*M1*xM2;
167 tmp3 = (6*xM2 - 12*M1^2);
168 l1 = (tmp2 - tmp1)/tmp3;
169 l2 = (tmp2 + tmp1)/tmp3;
173 % check feasibility for the first fit
174 function [c, ceq] = nonlcon1(x)
177 % tmp0
is returned explicitly: it
is assigned inside aph2_fit1, a
178 % SIBLING nested function, so it
is not in scope here otherwise and
179 % every call used to raise an undefined-variable error.
180 [p1,l1,l2,tmp0] = aph2_fit1(xM2,xM3);
182 c(1) = -tmp0; % non-negative square root argument
183 c(2) = -l1 + 1e-6; % non-negative l1
184 c(3) = -l2 + 1e-6; % non-negative l2
185 c(4) = -p1; % non-negative p1
186 % CLOSED-FORM APH(2) FEASIBILITY, redundant with the inversion rows
187 % above but not implied by them at solver tolerance: the inversion
188 % constraints alone are satisfied to ~1e-9 by driving p1 to a tiny
189 % negative value with l2 growing without bound, which fmincon's
190 % default ConstraintTolerance = 1e-6 accepts although the pair admits
191 % no APH(2). scv >= 1/2, plus the Telek-Heindl third-moment interval.
192 scv = (xM2 - M1^2)/M1^2;
195 c(6) = 3*M1^3*(3*scv-1+sqrt(2)*(1-scv)^(3/2)) - xM3;
196 c(7) = xM3 - 6*M1^3*scv;
198 c(6) = 3/2*M1^3*(1+scv)^2 - xM3;
204 % check feasibility for the second fit
205 function [c, ceq] = nonlcon2(x)
208 [p1,l1,l2,tmp0] = aph2_fit2(xM2,xM3);
210 c(1) = -tmp0; % non-negative square root argument
211 c(2) = -l1 + 1e-6; % non-negative l1
212 c(3) = -l2 + 1e-6; % non-negative l2
213 c(4) = -p1; % non-negative p1
214 % CLOSED-FORM APH(2) FEASIBILITY, redundant with the inversion rows
215 % above but not implied by them at solver tolerance: the inversion
216 % constraints alone are satisfied to ~1e-9 by driving p1 to a tiny
217 % negative value with l2 growing without bound, which fmincon's
218 % default ConstraintTolerance = 1e-6 accepts although the pair admits
219 % no APH(2). scv >= 1/2, plus the Telek-Heindl third-moment interval.
220 scv = (xM2 - M1^2)/M1^2;
223 c(6) = 3*M1^3*(3*scv-1+sqrt(2)*(1-scv)^(3/2)) - xM3;
224 c(7) = xM3 - 6*M1^3*scv;
226 c(6) = 3/2*M1^3*(1+scv)^2 - xM3;