LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
aph2_adjust.m
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.
4% 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)
12% Output:
13% M2a: adjusted value for M2
14% M3a: adjusted value for M3
15
16if nargin < 4
17 method = 'simple';
18end
19
20% tolerance for strict inequalities
21tol = 1e-4;
22
23if strcmp(method, 'simple')
24 M1sq = M1^2;
25 scv = ((M2-M1sq)/M1sq);
26 if scv < 1/2
27 % (M2 - M1^2)/M1^2 = 1/2
28 % M2 - M1^2 = 1/2 M1^2
29 % M2 = 3/2 M1^2
30 M2a = 3/2 * M1^2;
31 scva = ((M2a-M1sq)/M1sq);
32 else
33 M2a = M2;
34 scva = scv;
35 end
36 if scva <= 1
37 lb = 3*M1^3*(3*scva-1+sqrt(2)*(1-scva)^(3/2));
38 ub = 6*M1^3*scva;
39 if M3 < lb
40 M3a = lb;
41 elseif M3 > ub
42 M3a = ub;
43 else
44 M3a = M3;
45 end
46 else
47 lb = 3/2*M1^3*(1+scva)^2;
48 if M3 <= lb
49 M3a = lb * (1 + tol);
50 else
51 M3a = M3;
52 end
53 end
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, ...
59 'x0', [M2 M3], ...
60 'lb', [0 0], ...
61 'ub', [inf inf], ...
62 'nonlcon', @nonlcon1);
63 prob2 = createOptimProblem('fmincon', ...
64 'objective', @fun, ...
65 'x0', [M2 M3], ...
66 'lb', [0 0], ...
67 'ub', [inf inf], ...
68 'nonlcon', @nonlcon2);
69 % adjustment to make the first fit feasible
70 x = run(GlobalSearch('Display','iter'), prob1);
71 M2a1 = x(1);
72 M3a1 = x(2);
73 % adjustment to make the second fit feasible
74 x = run(GlobalSearch('Display','iter'), prob2);
75 M2a2 = x(1);
76 M3a2 = x(2);
77 else
78 options = optimset('Display', 'off','Algorithm','active-set');
79 x = fmincon(@fun, [M2 M3], [], [], [], [], [0 0], [], @nonlcon1, options);
80 M2a1 = x(1);
81 M3a1 = x(2);
82 x = fmincon(@fun, [M2 M3], [], [], [], [], [0 0], [], @nonlcon2, options);
83 M2a2 = x(1);
84 M3a2 = x(2);
85 end
86 % pick the smallest adjustment
87 if norm([M2a1 M3a1] - [M2 M3]) < norm([M2a2 M3a2] - [M2 M3])
88 M2a = M2a1;
89 M3a = M3a1;
90 else
91 M2a = M2a2;
92 M3a = M3a2;
93 end
94elseif strcmp(method, 'opt_param') || strcmp(method, 'opt_param_gads')
95 % optimize in parameter space
96 feastol = 1e-6;
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, ...
104 'x0', x0, ...
105 'lb', lb, ...
106 'ub', ub, ...
107 'nonlcon', @nonlcon3);
108 x = run(GlobalSearch('Display','none'), prob);
109 else
110 options = optimset('Display', 'none','Algorithm','active-set');
111 x = fmincon(@fun2, x0, [], [], [], [], lb, ub, @nonlcon3, options);
112 end
113 l2 = x(1);
114 r1 = x(2);
115 l1 = M1 - l2*r1;
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;
118else
119 error('Invalid method: %s', method);
120end
121
122 function obj = fun2(x)
123 l2 = x(1);
124 r1 = x(2);
125 l1 = M1 - l2*r1;
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]);
129 end
130
131 function [c, ceq] = nonlcon3(x)
132 l2 = x(1);
133 r1 = x(2);
134 c = l2*r1 - M1 + feastol;
135 ceq = [];
136 end
137
138 % minimize euclidean distance to sample values of M2 and M3
139 function obj = fun(x)
140 obj = norm(x - [M2 M3]);
141 end
142
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;
155 p1 = (M1 - l1)/l2;
156 end
157
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;
170 p1 = (M1 - l1)/l2;
171 end
172
173 % check feasibility for the first fit
174 function [c, ceq] = nonlcon1(x)
175 xM2 = x(1);
176 xM3 = x(2);
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);
181 c = zeros(7,1);
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;
193 c(5) = 1/2 - scv;
194 if scv <= 1
195 c(6) = 3*M1^3*(3*scv-1+sqrt(2)*(1-scv)^(3/2)) - xM3;
196 c(7) = xM3 - 6*M1^3*scv;
197 else
198 c(6) = 3/2*M1^3*(1+scv)^2 - xM3;
199 c(7) = 0;
200 end
201 ceq = [];
202 end
203
204 % check feasibility for the second fit
205 function [c, ceq] = nonlcon2(x)
206 xM2 = x(1);
207 xM3 = x(2);
208 [p1,l1,l2,tmp0] = aph2_fit2(xM2,xM3);
209 c = zeros(7,1);
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;
221 c(5) = 1/2 - scv;
222 if scv <= 1
223 c(6) = 3*M1^3*(3*scv-1+sqrt(2)*(1-scv)^(3/2)) - xM3;
224 c(7) = xM3 - 6*M1^3*scv;
225 else
226 c(6) = 3/2*M1^3*(1+scv)^2 - xM3;
227 c(7) = 0;
228 end
229 ceq = [];
230 end
231
232end