1function [mmap,fB,fS,exact] = mamap22_fit_bs_multiclass(
map,p,B,S,classWeights,bsWeights,adjust)
2% Performs approximate fitting of a
MMAP given the underlying AMAP(2),
3% the
class probabilities (always fitted exactly), the backward moments,
4% and the one-step
class transition probabilities.
6% -
map: second-order AMAP underlying the MAMAP[2]
7% - p: vector of
class probabilities
8% - B: vector of backward moments
9% - S: matrix of the
class transition probabilities
10% - classWeights: optional vector of weights
for each class
11% - bsWeights: optional 2-vector of weights of backward moments and the
12%
class transition probabilities
14% - mmap: fitted MAMAP[2]
15% - fB: vector of optimal feasible backward moments
16% - fS: vector of optimal feasible
class transition probabilities
18if (size(
map{1},1) ~= 2)
19 error('Underlying MAP must be of second-order.');
22 error('Underlying MAP must be acyclic');
26elseif (
map{2}(1,1) == 0)
29 error('Underlying MAP must be in canonical acyclic form');
32fprintf('Fitting MAMAP(2,2) B+S: form = %d\n', form);
37 error('Fitting MAMAP(2,2) B+S: fitting backward and transition probabilities only supports two
classes');
40% default weights to use in the objective function
41if nargin < 5 || isempty(classWeights)
42 classWeights = ones(k,1);
44if nargin < 6 || isempty(bsWeights)
45 bsWeights = ones(2,1);
56% get parameters of the underlying AMAP(2)
62% set tolerance constants
67% generate required coefficients
69 [G,U,Y] = mamap2m_can1_coefficients(h1,h2,r1,r2);
72 [E,V,Z] = mamap2m_can2_coefficients(h1,h2,r1,r2);
78if (form == 1 && (r1 < degentol || r2 > 1-degentol || abs(h2-h1*r2) < degentol )) || ...
79 (form == 2 && (r2 > 1-degentol || abs(h1 - h2 - h1*r1 + h1*r1*r2) < degentol ))
81 % TODO: transform into a valid second-order Poisson process
83 % DEGENERATE PHASE_TYPE
84 fprintf(
'Fitting MAMAP(2,2) B+S: detected Poisson process\n');
86 %
return marked poisson process
92 mmap{2+c} = mmap{2} * p(c);
95 fB = mmap_backward_moment(mmap,1);
96 fS = mmap_sigma(mmap);
100elseif form == 2 && r2 < degentol && abs(1-r1) < degentol
102 % DEGENERATE PHASE_TYPE
103 fprintf(
'Fitting MAMAP(2,2) B+S: detected degenerate phase-type form\n');
105 % only one degree of freedom: match
class probabilites
110elseif form == 1 && r2 < degentol
112 % CANONICAL PHASE_TYPE
113 fprintf(
'Fitting MAMAP(2,2) B+S: detected canonical phase-type form\n');
115 % convert to phase-type
120 mmap = maph2m_fit_multiclass(
aph, p, B, classWeights);
122 fB = mmap_backward_moment(mmap, 1);
123 fS = mmap_sigma(mmap);
127elseif (form == 1 && abs(1-r1) < degentol) || ...
128 (form == 2 && abs(1-r1) < degentol)
130 % NON-CANONICAL PHASE_TYPE
131 fprintf(
'Fitting MAMAP(2,2) B+S: detected non-canonical phase-type form, converting to canonical form\n');
135 mmap = maph2m_fit_multiclass(
aph, p, B, classWeights);
137 fB = mmap_backward_moment(mmap, 1);
138 fS = mmap_sigma(mmap);
142elseif form == 2 && r2 < degentol
144 % DEGENERATE CASE FOR gamma < 0
145 fprintf(
'Fitting MAMAP(2,2) B+S: detected degenerate MMAP form\n');
147 if bsWeights(1) > bsWeights(2)
148 fprintf('Fitting MAMAP(2,2) B+S: fitting B\n');
149 [q1,q2,q3] = fit_can2_degen_backward(B(1));
150 if ~(isfeasible(q1) && isfeasible(q2) && isfeasible(q3))
151 % four inequalities, one variable
155 q1_B = - p(1) * (r1-2)/((r1-1)*(h2-h1+h1*r1));
156 q1_0 = + p(1) * (r1-2)*(h2+h1*r1)/((r1-1)*(h2-h1+h1*r1));
157 q2_B = - p(1) * (r1-2)/(h2-h1+h1*r1);
158 q2_0 = + p(1) * (r1-2)*h1/(h2-h1+h1*r1);
175 fB1 = solve_quadprog();
176 % compute coefficient
177 [q1,q2,q3] = fit_can2_degen_backward(fB1);
180 fprintf('Fitting MAMAP(2,2) B+S: fitting S\n');
181 [q1,q2,q3] = fit_can2_degen_transition(S(1,1));
182 if ~(isfeasible(q1) && isfeasible(q2) && isfeasible(q3))
183 % bound constraints on S11
185 q1lb = p(1)^2 * (1 - (1-r1)^2);
186 q2ub = p(1)^2 - (1 - p(1))^2;
188 fS11 = q1lb + safetytol;
189 elseif S(1,1) >= q2ub
190 fS11 = q2ub - safetytol;
195 [q1,q2,q3] = fit_can2_degen_transition(fS11);
201 % FULL FORM or "GOOD" poisson process
203 if (form == 1 && abs(h1 - h2 + h2*r1) < degentol) || (form == 2 && abs(h1 - h2 + h2*r1) < degentol)
204 fprintf('Fitting MAMAP(2,2) B+S: detected fittable Poisson process\n')
207 [q1,q2,q3] = fit(B(1), S(1,1));
209 % options used to solve nonlinear problem
212 yalmip_nonlinear_opt = sdpsettings(...
214 'solver','bmibnb',...
218 'bmibnb.relgaptol',1e-3,...
219 'bmibnb.absgaptol',1e-6,...
220 'bmibnb.maxiter',1000,...
221 'bmibnb.lowrank', 1,...
222 'bmibnb.lpreduce',1,... % without this, crappy lower bounds for some problems
223 'bmibnb.pdtol',-1e-8,... % x >= if x > -pdtol
224 'bmibnb.eqtol',+1e-10,... % x == 0 if abs(x) < +eqtol
225 'bmibnb.roottight',1,...
226 'bmibnb.uppersolver','fmincon-standard',...
227 'fmincon.Algorithm','sqp',...
228 'fmincon.TolCon',1e-6,...
229 'fmincon.TolX',1e-6,...
230 'fmincon.GradObj','on',...
231 'fmincon.GradConstr','on',...
232 'fmincon.Hessian','off',...
233 'fmincon.LargeScale','off');
238 if isfeasible(q1) && isfeasible(q2) && isfeasible(q3)
239 fprintf('Fitting MAMAP(2,2) B+S: exact fit found\n');
242 [q1,q2,q3] = solve_nonlinear();
245end % end if/then/else for degenerate forms
247% check parameter feasibility
248if adjust && ~(isfeasible(q1) && isfeasible(q2) && isfeasible(q3))
249 error('Fitting MAMAP(2,2) B+S: Feasibility could not be restored');
251% parameters feasible within feastol: restrict to [0,1]
256% compute D11,D12,...,D1k
258 mmap{2+1} = mmap{2} .* [q1 0; q2 q3];
259 mmap{2+2} = mmap{2} .* [1-q1 0; 1-q2 1-q3];
261 mmap{2+1} = mmap{2} .* [0 q1; q2 q3];
262 mmap{2+2} = mmap{2} .* [0 1-q1; 1-q2 1-q3];
265fB = mmap_backward_moment(mmap, 1);
266fS = mmap_sigma(mmap);
268 function feas = isfeasible(q)
269 feas = q >= -feastol && q <= (1+feastol);
272 function qfix = fix(q)
273 qfix = max(min(q,1),0);
276 function [q1,q2,q3] = fit_can1(vB1,vS11)
277 denum = U(11) * vB1 * p(1) + U(12) * p(1);
278 if abs(denum) < denumtol
283 q2 = (U(7) * vB1^2 * p(1)^2 + U(8) * vB1 * p(1)^2 + U(9) * vS11 + U(10) * p(1)^2)/denum;
284 q1 = -(G(12)*p(1) - vB1*G(3)*p(1) + (G(3)*G(11) - G(2)*G(12))*q2)/Y(3);
285 q3 = +(G(10)*p(1) - vB1*G(1)*p(1) + (G(1)*G(11) - G(2)*G(10))*q2)/Y(3);
289 function [q1,q2,q3] = fit_can2(vB1,vS11)
290 denum = (V(11) * vB1 * p(1) + V(12) * p(1));
291 if abs(denum) < denumtol
296 q3 = (V(7) * vB1^2 * p(1)^2 + V(8) * vB1 * p(1)^2 + V(9) * p(1)^2 + V(10) * vS11)/denum;
297 q1 = +(E(10)*p(1) - vB1*E(2)*p(1) + (E(2)*E(11) - E(3)*E(10))*q3)/Z(3);
298 q2 = -(E(9)*p(1) - vB1*E(1)*p(1) + (E(1)*E(11) - E(3)*E(9))*q3)/Z(3);
302 function [q1,q2,q3] = fit_can2_degen_backward(vB1)
303 q1 = (p(1)*(r1 - 2)*(h2 - vB1 + h1*r1))/((r1 - 1)*(h2 - h1 + h1*r1));
304 q2 = -(p(1)*(vB1 - h1)*(r1 - 2))/(h2 - h1 + h1*r1);
305 q3 = p(1); % meangingless
if really degenerate
308 function [q1,q2,q3] = fit_can2_degen_transition(vS11)
309 q1 = p(1) + (p(1)^2 - vS11)^(1/2)/(r1 - 1);
310 q2 = p(1) + (p(1)^2 - vS11)^(1/2);
311 q3 = p(1); % meangingless
if really degenerate
314 function [q1,q2,q3,obj] = solve_nonlinear()
316 fprintf('Fitting MAMAP(2,2) B+S: running approximate fitting (nonlinear)\n');
318 % for now we support only one of the formulations
319 solve_nonlinear_func = @solve_nonlinear_hybrid;
322 eobj = make_objective(M1, p(1)^2);
323 fprintf('Fitting MAMAP(2,2) B+S: B1 == M1, objective = %e\n', eobj);
325 [lfeas,lobj,lF1,lS11] = solve_nonlinear_func('left');
327 fprintf('Fitting MAMAP(2,2) B+S: B1 < M1, objective = %e\n', lobj);
329 fprintf('Fitting MAMAP(2,2) B+S: B1 < M1 infeasible\n');
332 [rfeas,robj,rF1,rS11] = solve_nonlinear_func('right');
334 fprintf('Fitting MAMAP(2,2) B+S: B1 > M1, objective = %e\n', robj);
336 fprintf('Fitting MAMAP(2,2) B+S: B1 > M1 infeasible\n');
339 if (~lfeas && ~rfeas) || (eobj < lobj && eobj < robj)
344 elseif ~rfeas || lobj < robj
346 [q1,q2,q3] = fit(lF1, lS11);
349 [q1,q2,q3] = fit(rF1, rS11);
354% used for non-degenerate cases: hybrid space
355 function [feas,bobj,bB1,bS11] = solve_nonlinear_hybrid(side)
361 q1exp = -(G(12)*p(1) - vB1*G(3)*p(1) + vq2*(G(11)*G(3) - G(12)*G(2)))/Y(3);
362 q2num = U(7)*vB1^2*p(1)^2 + U(8)*vB1*p(1)^2 + U(9)*vS11 + U(10)*p(1)^2;
363 q2den = p(1)*(U(11)*vB1 + U(12));
364 q3exp = +(G(10)*p(1) - vB1*G(1)*p(1) + vq2*(G(11)*G(1) - G(10)*G(2)))/Y(3);
365 hcstr = [0 <= q1exp <= 1, 0 <= vq2 <= 1, vq2*q2den/scale == q2num/scale, 0 <= q3exp <= 1, 0 <= vS11 <= 1, vB1 >= 0];
369 q1exp = +(E(10)*p(1) - vB1*E(2)*p(1) + vq3*(E(11)*E(2) - E(10)*E(3)))/Z(3);
370 q2exp = -(E(9)*p(1) - vB1*E(1)*p(1) + vq3*(E(11)*E(1) - E(3)*E(9)))/Z(3);
371 q3num = V(7)*vB1^2*p(1)^2 + V(8)*vB1*p(1)^2 + V(9)*p(1)^2 + V(10)*vS11;
372 q3den = p(1)*(V(11)*vB1 + V(12));
373 hcstr = [0 <= q1exp <= 1, 0 <= q2exp <= 1, vq3*q3den/scale == q3num/scale, 0 <= vq3 <= 1, 0 <= vS11 <= 1, vB1 >= 0];
375 if strcmp(side,'left')
376 hcstr = [hcstr, vB1 <= M1-tol];
378 hcstr = [hcstr, vB1 >= M1+tol];
380 hobj = make_objective(vB1,vS11);
381 hsol = solvesdp(hcstr, hobj, yalmip_nonlinear_opt);
383 fprintf('Fitting MAMAP(2,2) B+S: solver (hybrid space) objective = %f\n',
double(hobj));
388 elseif hsol.problem == 1 || hsol.problem == 12
389 fprintf('Fitting MAMAP(2,2) B+S: program
is infeasible\n');
396 save(fname,'
map','p','B','S');
397 error('Fitting MAMAP(2,2) B+S: solver (hybrid space) error: %s, input saved to %s\n', hsol.info, fname);
401 function obj = make_objective(vB1, vS11)
402 obj = classWeights(1) * bsWeights(1) * (vB1/B(1) - 1)^2 + ...
403 classWeights(1) * bsWeights(2) * (vS11/S(1,1) - 1)^2;
406 function x = solve_quadprog()
407 fprintf('Fitting MAMAP(2,2) B+S: running quadratic programming solver...\n');
408 options = optimset('Algorithm','interior-point-convex','Display','none');
409 %[x,fx,xflag] = quadprog(H, h, A, b, [], [], [], [], [], options);
410 lb = 1e-6*ones( size(A,2),1);
411 ub = 1e6*ones( size(A,2),1);
412 [x,fx,xflag]=QP(H, h, A, b, Aeq, beq, lb, ub, options);
415 error('Quadratic programming solver failed: %d\n', exit);
417 fit_error = fx + length(x);
418 fprintf('Fitting MAMAP(2,2) B+S: error = %f\n', fit_error);