0001 function img= inv_solve_core( inv_model, data0, data1);
0002 
0003 
0004 
0005 
0006 
0007 
0008 
0009 
0010 
0011 
0012 
0013 
0014 
0015 
0016 
0017 
0018 
0019 
0020 
0021 
0022 
0023 
0024 
0025 
0026 
0027 
0028 
0029 
0030 
0031 
0032 
0033 
0034 
0035 
0036 
0037 
0038 
0039 
0040 
0041 
0042 
0043 
0044 
0045 
0046 
0047 
0048 
0049 
0050 
0051 
0052 
0053 
0054 
0055 
0056 
0057 
0058 
0059 
0060 
0061 
0062 
0063 
0064 
0065 
0066 
0067 
0068 
0069 
0070 
0071 
0072 
0073 
0074 
0075 
0076 
0077 
0078 
0079 
0080 
0081 
0082 
0083 
0084 
0085 
0086 
0087 
0088 
0089 
0090 
0091 
0092 
0093 
0094 
0095 
0096 
0097 
0098 
0099 
0100 
0101 
0102 
0103 
0104 
0105 
0106 
0107 
0108 
0109 
0110 
0111 
0112 
0113 
0114 
0115 
0116 
0117 
0118 
0119 
0120 
0121 
0122 
0123 
0124 
0125 
0126 
0127 
0128 
0129 
0130 
0131 
0132 
0133 
0134 
0135 
0136 
0137 
0138 
0139 
0140 
0141 
0142 
0143 
0144 
0145 
0146 
0147 
0148 
0149 
0150 
0151 
0152 
0153 
0154 
0155 
0156 
0157 
0158 
0159 
0160 
0161 
0162 
0163 
0164 
0165 
0166 
0167 
0168 
0169 
0170 
0171 
0172 
0173 
0174 
0175 
0176 
0177 
0178 
0179 
0180 
0181 
0182 
0183 
0184 
0185 
0186 
0187 
0188 
0189 
0190 
0191 
0192 
0193 
0194 
0195 
0196 
0197 
0198 
0199 
0200 
0201 
0202 
0203 
0204 
0205 
0206 
0207 
0208 
0209 
0210 
0211 
0212 
0213 
0214 
0215 
0216 
0217 
0218 
0219 
0220 
0221 
0222 
0223 
0224 
0225 
0226 
0227 
0228 
0229 
0230 
0231 
0232 
0233 
0234 
0235 
0236 
0237 
0238 
0239 
0240 
0241 
0242 
0243 
0244 if ischar(inv_model) && strcmp(inv_model,'UNIT_TEST') && (nargin == 1); do_unit_test; return; end
0245 if ischar(inv_model) && strcmp(inv_model,'UNIT_TEST') && (nargin == 2); do_unit_test(data0); return; end
0246 
0247 
0248 if nargin == 3 
0249    n_frames = max(count_data_frames(data0),count_data_frames(data1));
0250 else 
0251    n_frames = count_data_frames(data0);
0252 end
0253 opt = parse_options(inv_model, n_frames);
0254 
0255 
0256 
0257 
0258 
0259 
0260 [inv_model, opt] = append_c2f_background(inv_model, opt);
0261 
0262 
0263 
0264 
0265 
0266 if isfield(inv_model, 'jacobian_bkgnd')
0267   inv_model = rmfield(inv_model,'jacobian_bkgnd');
0268 end
0269 inv_model.jacobian_bkgnd.value = 1;
0270 img = mk_image( inv_model );
0271 img.inv_model = inv_model; 
0272 img = data_mapper(img); 
0273 
0274 img = init_elem_data(img, opt);
0275 
0276 
0277 
0278 img = map_img(img, opt.elem_working);
0279 
0280 
0281 if nargin == 3
0282    test = strcmp(opt.meas_working{1}, {'apparent_resistivity','log_apparent_resistivity', 'log10_apparent_resitivity'});
0283    errstr= ['meas_working = ''' opt.meas_working{1} ''' not yet supported for difference solutions'];
0284    assert(all(~test), errstr);
0285    for i = 1:length(opt.elem_output)
0286       assert(any(strcmp(opt.elem_output{i}, {'conductivity','resistivity','movement'})), ...
0287              ['elem_output = {' strjoin(opt.elem_output,', ') '} but difference solver log normal outputs are not supported']);
0288    end
0289    
0290    nil = struct;
0291    if isstruct(data0)
0292       nil.meas = data0(1).meas(:,1)*0;
0293    else
0294       nil.meas = data0(:,1)*0;
0295    end
0296    nil.type = 'data';
0297    nil.current_params = opt.meas_input;
0298    [dv, opt, err] = update_dv_core(img, nil, 1, opt);
0299    
0300    
0301    data0 = bsxfun(@plus,calc_difference_data( data0, data1, inv_model.fwd_model ), dv);
0302    
0303    assert(strcmp(inv_model.reconst_type, 'difference'), ...
0304           ['expected inv_model.reconst_type = ''difference'' not ' inv_model.reconst_type]);
0305 else
0306    assert(strcmp(inv_model.reconst_type, 'absolute'), ...
0307           ['expected inv_model.reconst_type = ''absolute'' not ' inv_model.reconst_type]);
0308 end
0309 
0310 
0311 if ~isstruct(data0)
0312    d = data0;
0313    data0 = struct;
0314    data0.meas = d;
0315    data0.type = 'data';
0316 end
0317 data0.current_params = opt.meas_input;
0318 
0319 
0320 W  = init_meas_icov(inv_model, opt);
0321 [N, dN] = init_normalization(inv_model.fwd_model, data0, opt);
0322 
0323 
0324 img0 = img;
0325 hps2RtR = 0; alpha = 0; k = 0; sx = 0; r = 0; stop = 0; 
0326 hpt2LLt = update_hpt2LLt(inv_model, data0, k, opt);
0327 residuals = zeros(opt.max_iterations,3); 
0328 dxp = 0; 
0329 [dv, opt] = update_dv([], img, data0, N, opt);
0330 de = update_de([], img, img0, opt);
0331 if opt.verbose >= 5 
0332   dvall = ones(size(data0.meas,1),opt.max_iterations+1)*NaN;
0333 end
0334 while 1
0335   if opt.verbose >= 1
0336      if k == 0
0337         fprintf('  inv_solve_core: start up\n');
0338      else
0339         fprintf('  inv_solve_core: iteration %d (residual = %g)\n', k, r(k,1));
0340      end
0341   end
0342 
0343   
0344   
0345   [J, opt] = update_jacobian(img, dN, k, opt);
0346 
0347   
0348   hps2RtR = update_hps2RtR(inv_model, J, k, img, opt);
0349 
0350   
0351   
0352   dx = update_dx(J, W, hps2RtR, hpt2LLt, dv, de, opt);
0353   
0354   beta = update_beta(dx, dxp, sx, opt);
0355   beta_all(k+1)=beta; 
0356   
0357   sx = update_sx(dx, beta, sx, opt);
0358   if k ~= 0
0359      dxp = dx; 
0360   end
0361 
0362   
0363   plot_dx_and_svd_elem(J, W, hps2RtR, k, sx, dx, img, opt);
0364 
0365   
0366   
0367   
0368   
0369   [alpha, img, dv, opt] = update_alpha(img, sx, data0, img0, N, W, hps2RtR, hpt2LLt, k, dv, opt);
0370   alpha_all(k+1) = alpha;
0371   
0372   
0373   [img, dv] = update_img_using_limits(img, img0, data0, N, dv, opt);
0374 
0375   
0376   
0377   [dv, opt] = update_dv(dv, img, data0, N, opt);
0378   de = update_de(de, img, img0, opt);
0379   if opt.verbose >= 5
0380     dvall(:,k+1) = dv;
0381     show_meas_err(dvall, data0, k, N, W, opt);
0382   end
0383   show_fem_iter(k, img, inv_model, stop, opt);
0384 
0385   
0386   [stop, k, r, img] = update_residual(dv, img, de, W, hps2RtR, hpt2LLt, k, r, alpha, sx, opt);
0387   if stop
0388      if stop == -1
0389         alpha_all(k) = 0;
0390      end
0391      break;
0392   end
0393 end
0394 img0 = strip_c2f_background(img0, opt);
0395 [img, opt] = strip_c2f_background(img, opt);
0396 
0397 if isfield(inv_model, 'rec_model')
0398   img.fwd_model = inv_model.rec_model;
0399 end
0400 if opt.verbose >= 1
0401    if k==1; itrs=''; else itrs='s'; end
0402    fprintf('  %d fwd_solves required for this solution in %d iteration%s\n', ...
0403            opt.fwd_solutions, k, itrs);
0404 end
0405 
0406 img = map_img(img, opt.elem_output);
0407 if strcmp(inv_model.reconst_type, 'difference')
0408    img0 = map_img(img0, opt.elem_output);
0409    img.elem_data = img.elem_data - img0.elem_data;
0410 end
0411 img.meas_err = dv;
0412 if opt.return_working_variables
0413   img.inv_solve_core.J = J;
0414   img.inv_solve_core.dx = dx;
0415   img.inv_solve_core.sx = sx;
0416   img.inv_solve_core.alpha = alpha_all;
0417   img.inv_solve_core.beta = beta_all;
0418   img.inv_solve_core.k = k;
0419   img.inv_solve_core.r = r(1:(k+1),:); 
0420   img.inv_solve_core.N = N;
0421   img.inv_solve_core.W = W;
0422   img.inv_solve_core.hps2RtR = hps2RtR;
0423   img.inv_solve_core.dv = dv;
0424   img.inv_solve_core.de = de;
0425   if opt.verbose >= 5
0426     img.inv_solve_core.dvall = dvall;
0427   end
0428 end
0429 
0430 
0431 function show_meas_err(dvall, data0, k, N, W, opt)
0432    clf;
0433    assert(length(opt.meas_working) == 1,'TODO meas_working len > 1');
0434    subplot(211); bar(dvall(:,k+1)); ylabel(sprintf('dv_k [%s]',opt.meas_working{1})); xlabel('meas #'); title(sprintf('measurement error @ iter=%d',k));
0435    subplot(212); bar(map_meas(dvall(:,k+1),N,opt.meas_working{1}, 'voltage')); ylabel('dv_k [V]'); xlabel('meas #'); title('');
0436    drawnow;
0437    if isfield(opt,'fig_prefix')
0438       print('-dpdf',sprintf('%s-meas_err%d',opt.fig_prefix,k));
0439       print('-dpng',sprintf('%s-meas_err%d',opt.fig_prefix,k));
0440       saveas(gcf,sprintf('%s-meas_err%d.fig',opt.fig_prefix,k));
0441    end
0442    drawnow;
0443 
0444 
0445 function n_frames = count_data_frames(data1)
0446    if isnumeric(data1)
0447       n_frames = size(data1,2);
0448    else
0449       n_frames = size(horzcat(data1(:).meas),2);
0450    end
0451 
0452 function img = init_elem_data(img, opt)
0453   if opt.verbose > 1
0454     fprintf('  setting prior elem_data\n');
0455   end
0456   ne2 = 0; 
0457   img.elem_data = zeros(sum([opt.elem_len{:}]),sum([opt.n_frames{:}])); 
0458   for i=1:length(opt.elem_prior)
0459     ne1 = ne2+1; 
0460     ne2 = ne1+opt.elem_len{i}-1; 
0461     if opt.verbose > 1
0462       if length(opt.prior_data{i}) == 1
0463         fprintf('    %d x %s: %0.1f\n',opt.elem_len{i},opt.elem_prior{i}, opt.prior_data{i});
0464       else
0465         fprintf('    %d x %s: ...\n',opt.elem_len{i},opt.elem_prior{i});
0466         if length(opt.prior_data{i}) ~= opt.elem_len{i}
0467            error(sprintf('expected %d elem, got %d elem in elem_prior', ...
0468                          opt.elem_len{i}, length(opt.prior_data{i})));
0469         end
0470       end
0471     end
0472     img.params_sel(i) = {ne1:ne2};
0473     img.elem_data(img.params_sel{i},:) = opt.prior_data{i};
0474   end
0475   img.current_params = opt.elem_prior;
0476 
0477 function W = init_meas_icov(inv_model, opt)
0478    W = 1;
0479    if opt.calc_meas_icov
0480       if opt.verbose > 1
0481          disp('  calc measurement inverse covariance W');
0482       end
0483       W   = calc_meas_icov( inv_model );
0484    end
0485    err_if_inf_or_nan(W, 'init_meas_icov');
0486 
0487 function [N, dN] = init_normalization(fmdl, data0, opt)
0488    
0489    N = 1;
0490    dN = 1;
0491    vh1.meas = 1;
0492    if ~iscell(opt.meas_input) || ~iscell(opt.meas_working)
0493       error('expected cell array for meas_input and meas_working');
0494    end
0495    
0496    assert(length(opt.meas_input) == length(opt.meas_working), 'meas_input and meas_working lengths must match');
0497    assert(length(opt.meas_working) == 1, 'TODO only supports a single type of measurements');
0498    go =       any(strcmp({opt.meas_input{1}, opt.meas_working{1}},'apparent_resistivity'));
0499    go = go || any(strcmp({opt.meas_input{1}, opt.meas_working{1}},'log_apparent_resistivity'));
0500    go = go || any(strcmp({opt.meas_input{1}, opt.meas_working{1}},'log10_apparent_resistivity'));
0501    if go
0502       if opt.verbose > 1
0503          disp(['  calc measurement normalization matrix N (voltage -> ' opt.meas_working{1} ')']);
0504       end
0505       
0506       img1 = mk_image(fmdl,1);
0507       vh1  = fwd_solve(img1);
0508       N    = spdiag(1./vh1.meas);
0509       err_if_inf_or_nan(N,  'init_normalization: N');
0510    end
0511    if go && (opt.verbose > 1)
0512       disp(['  calc Jacobian normalization matrix   dN (voltage -> ' opt.meas_working{1} ')']);
0513    end
0514    
0515    assert(length(opt.meas_working)==1, 'only supports single measurement type at a time');
0516    data0 = map_meas_struct(data0, N, 'voltage'); 
0517    switch opt.meas_working{1}
0518       case 'apparent_resistivity'
0519          dN = da_dv(data0.meas, vh1.meas);
0520       case 'log_apparent_resistivity'
0521          dN = dloga_dv(data0.meas, vh1.meas);
0522       case 'log10_apparent_resistivity'
0523          dN = dlog10a_dv(data0.meas, vh1.meas);
0524       case 'voltage'
0525          dN = dv_dv(data0.meas, vh1.meas);
0526       case 'log_voltage'
0527          dN = dlogv_dv(data0.meas, vh1.meas);
0528       case 'log10_voltage'
0529          dN = dlog10v_dv(data0.meas, vh1.meas);
0530       otherwise
0531          error('hmm');
0532    end
0533    err_if_inf_or_nan(dN, 'init_normalization: dN');
0534 
0535 
0536 
0537 function [stop, k, r, img] = update_residual(dv, img, de, W, hps2RtR, hpt2LLt, k, r, alpha, sx, opt)
0538   stop = 0;
0539 
0540   
0541   if k == 0
0542      r = ones(opt.max_iterations, 3)*NaN;
0543      r_km1 = inf;
0544      r_1   = inf;
0545   else
0546      r_km1 = r(k, 1);
0547      r_1   = r(1, 1);
0548   end
0549   [r_k m_k e_k] = feval(opt.residual_func, dv, de, W, hps2RtR, hpt2LLt);
0550   
0551   r(k+1,1:3) = [r_k m_k e_k];
0552 
0553   
0554   if opt.verbose > 1
0555      fprintf('    calc residual\n');
0556      if k == 0
0557         fprintf('    stop @ max iter = %d, tol = %0.3g (%0.3g%%), dtol = %0.3g%% (after %d iter)\n', ...
0558                 opt.max_iterations, opt.tol, opt.tol/r_k*100, opt.dtol*100, opt.dtol_iter);
0559         fprintf('      r =%0.3g = %0.3g meas + %0.3g elem\n', r_k, m_k, e_k);
0560      else
0561         fprintf('      r =%0.3g (%0.03g%%) = %0.3g meas (%0.03g%%) + %0.3g elem (%0.3g%%)\n', ...
0562                 r_k, r_k/r(1,1)*100, m_k, m_k/r(1,2)*100, e_k, e_k/r(1,3)*100);
0563         dr = (r_k - r_km1);
0564         fprintf('      dr=%0.3g (%0.3g%%)\n', dr, dr/r_1*100);
0565      end
0566   end
0567   if opt.plot_residuals
0568      
0569      r(k+1,2:3) = [sum(sum(dv.^2,1))/2 sum(sum(de.^2,1))/2];
0570      if k > 0
0571         clf;
0572         x = 1:(k+1);
0573         y = r(x, :);
0574         y = y ./ repmat(max(y,[],1),size(y,1),1) * 100;
0575         plot(x-1, y, 'o-', 'linewidth', 2, 'MarkerSize', 10);
0576         title('residuals');
0577         axis tight;
0578         ylabel('residual (% of max)');
0579         xlabel('iteration');
0580         set(gca, 'xtick', x);
0581         set(gca, 'xlim', [0 max(x)-1]);
0582         legend('residual','meas. misfit','prior misfit');
0583         legend('Location', 'EastOutside');
0584         drawnow;
0585         if isfield(opt,'fig_prefix')
0586            print('-dpdf',sprintf('%s-r',opt.fig_prefix));
0587            print('-dpng',sprintf('%s-r',opt.fig_prefix));
0588            saveas(gcf,sprintf('%s-r.fig',opt.fig_prefix));
0589         end
0590      end
0591   end
0592 
0593   
0594   if r_k > r_km1 
0595      if opt.verbose > 1
0596         fprintf('  terminated at iteration %d (bad step, returning previous iteration''s result)\n',k);
0597      end
0598      img.elem_data = img.elem_data - alpha * sx; 
0599      stop = -1;
0600   elseif k >= opt.max_iterations
0601      if opt.verbose > 1
0602         fprintf('  terminated at iteration %d (max iterations)\n',k);
0603      end
0604      stop = 1;
0605   elseif r_k < opt.tol + opt.ntol
0606      if opt.verbose > 1
0607         fprintf('  terminated at iteration %d\n',k);
0608         fprintf('    residual tolerance (%0.3g) achieved\n', opt.tol + opt.ntol);
0609      end
0610      stop = 1;
0611   elseif (k >= opt.dtol_iter) && ((r_k - r_km1)/r_1 > opt.dtol - 2*opt.ntol)
0612      if opt.verbose > 1
0613         fprintf('  terminated at iteration %d (iterations not improving)\n', k);
0614         fprintf('    residual slope tolerance (%0.3g%%) exceeded\n', (opt.dtol - 2*opt.ntol)*100);
0615      end
0616      stop = 1;
0617   end
0618   if ~stop
0619      
0620      k = k+1;
0621   end
0622 
0623 
0624 
0625 function beta = update_beta(dx_k, dx_km1, sx_km1, opt);
0626    if isfield(opt, 'beta_func')
0627       if opt.verbose > 1
0628          try beta_str = func2str(opt.beta_func);
0629          catch
0630             try
0631                 beta_str = opt.beta_func;
0632             catch
0633                 beta_str = 'unknown';
0634             end
0635          end
0636       end
0637       beta= feval(opt.beta_func, dx_k, dx_km1, sx_km1);
0638    else
0639      beta_str = '<none>';
0640      beta = 0;
0641    end
0642    if opt.verbose > 1
0643       str = sprintf('    calc beta (%s)=%0.3f\n', beta_str, beta);
0644    end
0645 
0646 
0647 
0648 
0649 
0650 
0651 function sx = update_sx(dx, beta, sx_km1, opt);
0652    sx = dx + beta * sx_km1;
0653    if(opt.verbose > 1)
0654       nsx = norm(sx);
0655       nsxk = norm(sx_km1);
0656       fprintf( '    update step dx, beta=%0.3g, ||dx||=%0.3g\n', beta, nsx);
0657       if nsxk ~= 0
0658          fprintf( '      acceleration     d||dx||=%0.3g\n', nsx-nsxk);
0659          
0660          ddx = norm(sx/nsx-sx_km1/nsxk);
0661          fprintf( '      direction change ||ddx||=%0.3g (%0.3g°)\n', ddx, 2*asind(ddx/2));
0662       end
0663    end
0664 
0665 
0666 function hps2RtR = update_hps2RtR(inv_model, J, k, img, opt)
0667    if k==0 
0668       k=1;
0669    end
0670    
0671    
0672    
0673    if ~opt.calc_RtR_prior
0674       error('no RtR calculation mechanism, set imdl.inv_solve_core.RtR_prior or imdl.RtR_prior');
0675    end
0676    if opt.verbose > 1
0677       disp('    calc hp^2 R^t R');
0678    end
0679    hp2 = calc_hyperparameter( inv_model )^2; 
0680    net = sum([opt.elem_len{:}]); 
0681    RtR = sparse(net,net); 
0682    esi = 0; eei = 0; 
0683    for i = 1:size(opt.RtR_prior,1) 
0684       esi = eei + 1;
0685       eei = eei + opt.elem_len{i};
0686       esj = 0; eej = 0; 
0687       for j = 1:size(opt.RtR_prior,2) 
0688          esj = eej + 1;
0689          eej = eej + opt.elem_len{j};
0690          if isempty(opt.RtR_prior{i,j}) 
0691             continue; 
0692          end
0693 
0694          
0695          
0696          hp=opt.hyperparameter_spatial{i,j};
0697          if length(hp) > k
0698             hp=hp(k);
0699          else
0700             hp=hp(end);
0701          end
0702 
0703          try RtR_str = func2str(opt.RtR_prior{i,j});
0704          catch
0705             try
0706                 RtR_str = opt.RtR_prior{i,j};
0707             catch
0708                 RtR_str = 'unknown';
0709             end
0710          end
0711          if opt.verbose > 1
0712             fprintf('      {%d,%d} regularization RtR (%s), ne=%dx%d, hp=%0.4g\n', i,j,RtR_str,eei-esi+1,eej-esj+1,hp*sqrt(hp2));
0713          end
0714          imgt = map_img(img, opt.elem_working{i});
0715          inv_modelt = inv_model;
0716          inv_modelt.RtR_prior = opt.RtR_prior{i,j};
0717          if strcmp(RtR_str, 'prior_noser') 
0718             if i ~= j
0719                error('noser for off diagonal prior (RtR) is undefined')
0720             end
0721             exponent= 0.5; try exponent = inv_model.prior_noser.exponent; end; try exponent = inv_model.prior_noser.exponent{j}; end
0722             ss = sum(J(:,esj:eej).^2,1)';
0723             Reg = spdiags( ss.^exponent, 0, opt.elem_len{j}, opt.elem_len{j}); 
0724             RtR(esi:eei, esj:eej) = hp.^2 * Reg;
0725          else
0726             RtR(esi:eei, esj:eej) = hp.^2 * calc_RtR_prior_wrapper(inv_modelt, imgt, opt);
0727          end
0728       end
0729    end
0730    hps2RtR = hp2*RtR;
0731 
0732 
0733 function hpt2LLt = update_hpt2LLt(inv_model, data0, k, opt)
0734    if k==0 
0735       k=1;
0736    end
0737    if ~opt.calc_LLt_prior
0738       error('no LLt calculation mechanism, set imdl.inv_solve_core.LLt_prior or imdl.LLt_prior');
0739    end
0740    if opt.verbose > 1
0741       disp('    calc hp^2 L L^t');
0742    end
0743    hp2 = 1;
0744    nmt = sum([opt.n_frames{:}]); 
0745    LLt = sparse(nmt,nmt); 
0746    msi = 0; mei = 0; 
0747    for i = 1:size(opt.LLt_prior,1) 
0748       msi = mei + 1;
0749       mei = mei + opt.n_frames{i};
0750       msj = 0; mej = 0; 
0751       for j = 1:size(opt.LLt_prior,2) 
0752          msj = mej + 1;
0753          mej = mej + opt.n_frames{j};
0754          if isempty(opt.LLt_prior{i,j}) 
0755             continue; 
0756          end
0757 
0758          
0759          
0760          hp=opt.hyperparameter_temporal{i,j};
0761          if length(hp) > k
0762             hp=hp(k);
0763          else
0764             hp=hp(end);
0765          end
0766 
0767          try LLt_str = func2str(opt.LLt_prior{i,j});
0768          catch
0769             try
0770                 LLt_str = opt.LLt_prior{i,j};
0771                 if isnumeric(LLt_str)
0772                    if LLt_str == eye(size(LLt_str))
0773                       LLt_str = 'eye';
0774                    else
0775                       LLt_str = sprintf('array, norm=%0.1g',norm(LLt_str));
0776                    end
0777                 end
0778             catch
0779                 LLt_str = 'unknown';
0780             end
0781          end
0782          if opt.verbose > 1
0783             fprintf('      {%d,%d} regularization LLt (%s), frames=%dx%d, hp=%0.4g\n', i,j,LLt_str,mei-msi+1,mej-msj+1,hp*sqrt(hp2));
0784          end
0785          inv_modelt = inv_model;
0786          inv_modelt.LLt_prior = opt.LLt_prior{i,j};
0787          LLt(msi:mei, msj:mej) = hp.^2 * calc_LLt_prior( data0, inv_modelt );
0788       end
0789    end
0790    hpt2LLt = hp2*LLt;
0791 
0792 function plot_dx_and_svd_elem(J, W, hps2RtR, k, sx, dx, img, opt)
0793    if(opt.verbose >= 5)
0794       
0795       clf;
0796       imgb=img;
0797       imgb.elem_data = dx;
0798       imgb.current_params = opt.elem_working;
0799       imgb.is_dx_plot = 1; 
0800       if isfield(imgb.inv_model,'rec_model')
0801          imgb.fwd_model = imgb.inv_model.rec_model;
0802       end
0803       feval(opt.show_fem,imgb,1);
0804       title(sprintf('dx @ iter=%d',k));
0805       drawnow;
0806       if isfield(opt,'fig_prefix')
0807          print('-dpng',sprintf('%s-dx%d',opt.fig_prefix,k));
0808          print('-dpdf',sprintf('%s-dx%d',opt.fig_prefix,k));
0809          saveas(gcf,sprintf('%s-dx%d.fig',opt.fig_prefix,k));
0810       end
0811    end
0812    if opt.verbose < 8
0813       return; 
0814    end
0815    
0816    if ~isfield(img, 'params_sel')
0817       img.params_sel = {1:length(img.elem_data)};
0818    end
0819    if ~isfield(img, 'current_params')
0820       img.current_params = 'conductivity';
0821    end
0822    if ~iscell(img.current_params)
0823       img.current_params = {img.current_params};
0824    end
0825    
0826    cols=length(opt.elem_working);
0827    if norm(sx - dx) < range(dx)/max(dx)*0.01 
0828       rows=2;
0829    else
0830       rows=3;
0831    end
0832    clf; 
0833    for i=1:cols
0834       if 1 
0835          hp=opt.hyperparameter_spatial{i};
0836          if k ~= 0 && k < length(hp)
0837             hp = hp(k);
0838          else
0839             hp = hp(end);
0840          end
0841       else
0842          hp = [];
0843       end
0844       sel=img.params_sel{i};
0845       str=strrep(img.current_params{i},'_',' ');
0846       plot_svd(J(:,sel), W, hps2RtR(sel,sel), k, hp); xlabel(str);
0847       drawnow;
0848       if isfield(opt,'fig_prefix')
0849          print('-dpdf',sprintf('%s-svd%d-%s',opt.fig_prefix,k,img.current_params{i}));
0850          print('-dpng',sprintf('%s-svd%d-%s',opt.fig_prefix,k,img.current_params{i}));
0851          saveas(gcf,sprintf('%s-svd%d-%s.fig',opt.fig_prefix,k,img.current_params{i}));
0852       end
0853    end
0854    clf; 
0855    for i=1:cols
0856       if 1 
0857          hp=opt.hyperparameter_spatial{i};
0858          if k ~= 0 && k < length(hp)
0859             hp = hp(k);
0860          else
0861             hp = hp(end);
0862          end
0863       else
0864          hp = [];
0865       end
0866       subplot(rows,cols,i);
0867       sel=img.params_sel{i};
0868       str=strrep(img.current_params{i},'_',' ');
0869       plot_svd(J(:,sel), W, hps2RtR(sel,sel), k, hp); xlabel(str);
0870       subplot(rows,cols,cols+i);
0871       bar(dx(sel)); ylabel(['dx: ' str]);
0872       if rows > 2
0873          subplot(rows,cols,2*cols+i);
0874          bar(sx(sel)); ylabel(['sx: ' str]);
0875       end
0876    end
0877    drawnow;
0878    if isfield(opt,'fig_prefix')
0879       print('-dpdf',sprintf('%s-svd%d',opt.fig_prefix,k));
0880       print('-dpng',sprintf('%s-svd%d',opt.fig_prefix,k));
0881       saveas(gcf,sprintf('%s-svd%d.fig',opt.fig_prefix,k));
0882    end
0883 
0884 function plot_svd(J, W, hps2RtR, k, hp)
0885    if nargin < 5
0886       hp = [];
0887    end
0888    
0889    [~,s1,~]=svd(J'*W*J); s1=sqrt(diag(s1));
0890    [~,s2,~]=svd(J'*W*J + hps2RtR); s2=sqrt(diag(s2));
0891    h=semilogy(s1,'bx'); axis tight; set(h,'LineWidth',2);
0892    hold on; h=semilogy(s2,'go'); axis tight; set(h,'LineWidth',2); hold off;
0893    xlabel('k'); ylabel('value \sigma');
0894    title(sprintf('singular values of J at iteration %d',k));
0895    legend('J^T J', 'J^T J + \lambda^2 R^T R'); legend location best;
0896    
0897 
0898 
0899 
0900 
0901 
0902 
0903    if length(hp)==1
0904         h=line([1 length(s1)],[hp hp]);
0905         ly=10^(log10(hp)-0.05*range(log10([s1;s2])));
0906         text(length(s1)/2,ly,sprintf('\\lambda = %0.4g', hp));
0907 
0908      end
0909      set(h,'LineStyle','-.'); set(h,'LineWidth',2);
0910    set(gca,'YMinorTick','on', 'YMinorGrid', 'on', 'YGrid', 'on');
0911 
0912 
0913 
0914 function RtR = calc_RtR_prior_wrapper(inv_model, img, opt)
0915    RtR = calc_RtR_prior( inv_model );
0916    if size(RtR,1) < length(img.elem_data)
0917      ne = length(img.elem_data) - size(RtR,1);
0918      
0919      for i=1:ne
0920        RtR(end+1:end+1, end+1:end+1) = RtR(1,1);
0921      end
0922      if opt.verbose > 1
0923         fprintf('      c2f: adjusting RtR by appending %d rows/cols\n', ne);
0924         disp(   '      TODO move this fix, or something like it to calc_RtR_prior -- this fix is a quick HACK to get things to run...');
0925      end
0926    end
0927 
0928 
0929 function [J, opt] = update_jacobian(img, dN, k, opt)
0930    img.elem_data = img.elem_data(:,1);
0931    base_types = map_img_base_types(img);
0932    imgb = map_img(img, base_types);
0933    imgb = feval(opt.update_img_func, imgb, opt);
0934    
0935    
0936    if any(strcmp(map_img_base_types(img), 'movement')) && any(strcmp(opt.meas_working, 'apparent_resistivity'))
0937       imgh = map_img(imgb, 'conductivity'); 
0938       imgh.elem_data = imgh.elem_data*0 +1; 
0939       vh = fwd_solve(imgh); vh = vh.meas;
0940       dN = da_dv(1,vh); 
0941       opt.fwd_solutions = opt.fwd_solutions +1;
0942    end
0943    ee = 0; 
0944    pp = fwd_model_parameters(imgb.fwd_model);
0945    J = zeros(pp.n_meas,sum([opt.elem_len{:}]));
0946    for i=1:length(opt.jacobian)
0947       if(opt.verbose > 1)
0948          if isnumeric(opt.jacobian{i})
0949             J_str = '[fixed]';
0950          elseif isa(opt.jacobian{i}, 'function_handle')
0951             J_str = func2str(opt.jacobian{i});
0952          else
0953             J_str = opt.jacobian{i};
0954          end
0955          if i == 1 fprintf('    calc Jacobian J(x) = ');
0956          else      fprintf('                       + '); end
0957          fprintf('(%s,', J_str);
0958       end
0959       
0960       es = ee+1;
0961       ee = es+opt.elem_len{i}-1;
0962       
0963       S = feval(opt.calc_jacobian_scaling_func{i}, imgb.elem_data(es:ee)); 
0964       
0965       
0966       
0967       imgt = imgb;
0968       if  strcmp(base_types{i}, 'conductivity') 
0969          imgt = map_img(img, 'conductivity');
0970       end
0971       imgt.fwd_model.jacobian = opt.jacobian{i};
0972       Jn = calc_jacobian( imgt ); 
0973       J(:,es:ee) = dN * Jn * S; 
0974       if opt.verbose > 1
0975          tmp = zeros(1,size(J,2));
0976          tmp(es:ee) = 1;
0977          tmp(opt.elem_fixed) = 0;
0978          fprintf(' %d DoF, %d meas, %s)\n', sum(tmp), size(J,1), func2str(opt.calc_jacobian_scaling_func{i}));
0979       end
0980       if opt.verbose >= 5
0981          clf;
0982          t=axes('Position',[0 0 1 1],'Visible','off'); 
0983          text(0.03,0.1,sprintf('update\\_jacobian (%s), iter=%d', strrep(J_str,'_','\_'), k),'FontSize',20,'Rotation',90);
0984          for y=0:1
0985             if y == 0; D = Jn; else D = J(:,es:ee); end
0986             axes('units', 'normalized', 'position', [ 0.13 0.62-y/2 0.8 0.3 ]);
0987             imagesc(D);
0988             if y == 0; ylabel('meas (1)'); xlabel(['elem (' strrep(base_types{i},'_','\_') ')']);
0989             else       ylabel('meas (dN)'); xlabel(['elem (' strrep(opt.elem_working{i},'_','\_') ')']);
0990             end
0991             os = get(gca, 'Position'); c=colorbar('southoutside'); 
0992             set(gca, 'Position', os); 
0993             
0994             cP = get(c,'Position'); set(c,'Position', [0.13    0.54-y/2    0.8    0.010]);
0995             axes('units', 'normalized', 'position', [ 0.93 0.62-y/2 0.05 0.3 ]);
0996             barh(sqrt(sum(D.^2,2))); axis tight; axis ij; set(gca, 'ytick', [], 'yticklabel', []);
0997             axes('units', 'normalized', 'position', [ 0.13 0.92-y/2 0.8 0.05 ]);
0998             bar(sqrt(sum(D.^2,1))); axis tight; set(gca, 'xtick', [], 'xticklabel', []);
0999          end
1000          drawnow;
1001          if isfield(opt,'fig_prefix')
1002             print('-dpng',sprintf('%s-J%d-%s',opt.fig_prefix,k,strrep(J_str,'_','')));
1003             print('-dpdf',sprintf('%s-J%d-%s',opt.fig_prefix,k,strrep(J_str,'_','')));
1004             saveas(gcf,sprintf('%s-J%d-%s.fig',opt.fig_prefix,k,strrep(J_str,'_','')));
1005          end
1006       end
1007    end
1008    if opt.verbose >= 4
1009       
1010       try
1011          clf;
1012          imgb.elem_data = log(sqrt(sum(J.^2,1)));
1013          for i = 1:length(imgb.current_params)
1014             imgb.current_params{i} = [ 'log_sensitivity_' imgb.current_params{i} ];
1015          end
1016          if isfield(imgb.inv_model,'rec_model')
1017             imgb.fwd_model = imgb.inv_model.rec_model;
1018          end
1019          feval(opt.show_fem,imgb,1);
1020          title(sprintf('sensitivity @ iter=%d',k));
1021          drawnow;
1022          if isfield(opt,'fig_prefix')
1023             print('-dpng',sprintf('%s-Js%d-%s',opt.fig_prefix,k,strrep(J_str,'_','')));
1024             print('-dpdf',sprintf('%s-Js%d-%s',opt.fig_prefix,k,strrep(J_str,'_','')));
1025             saveas(gcf,sprintf('%s-Js%d-%s.fig',opt.fig_prefix,k,strrep(J_str,'_','')));
1026          end
1027       catch 
1028       end
1029    end
1030 
1031 
1032 
1033 
1034 
1035 
1036 
1037 
1038 
1039 
1040 
1041 function S = dx_dlogx(x);
1042    S = spdiag(x);
1043 function S = dx_dlog10x(x);
1044    S = spdiag(x * log(10));
1045 
1046 
1047 
1048 
1049 function S = dx_dy(x);
1050    S = spdiag(-(x.^2));
1051 
1052 function S = dx_dlogy(x);
1053 
1054 
1055    S = spdiag(-x);
1056 function S = dx_dlog10y(x);
1057 
1058 
1059    S = spdiag(-x/log(10));
1060 
1061 
1062 
1063 
1064 
1065 
1066 
1067 
1068 
1069 
1070 
1071 
1072 
1073 
1074 function dN = da_dv(v,vh)
1075    dN = spdiag(1./vh); 
1076 function dN = dloga_dv(v,vh)
1077    dN = spdiag(1./v);
1078 function dN = dlog10a_dv(v,vh)
1079    dN = spdiag( 1./(v * log(10)) );
1080 function dN = dv_dv(v,vh)
1081    dN = 1;
1082 function dN = dlogv_dv(v,vh) 
1083    dN = dloga_dv(v,vh);
1084 function dN = dlog10v_dv(v,vh) 
1085    dN = dlog10a_dv(v, vh);
1086 
1087 
1088 
1089 function [alpha, img, dv, opt] = update_alpha(img, sx, data0, img0, N, W, hps2RtR, hpt2LLt, k, dv, opt)
1090   if k == 0 
1091      alpha = 0;
1092      return;
1093   end
1094 
1095   if(opt.verbose > 1)
1096      try
1097          ls_str = func2str(opt.line_search_func);
1098      catch
1099          ls_str = opt.line_search_func;
1100      end
1101      fprintf('    line search, alpha = %s\n', ls_str);
1102   end
1103 
1104   
1105   err_if_inf_or_nan(sx, 'sx (pre-line search)');
1106   err_if_inf_or_nan(img.elem_data, 'img.elem_data (pre-line search)');
1107 
1108   if any(size(img.elem_data) ~= size(sx))
1109      error(sprintf('mismatch on elem_data[%d,%d] vs. sx[%d,%d] vector sizes, check c2f_background_fixed',size(img.elem_data), size(sx)));
1110   end
1111 
1112   optt = opt;
1113   if isfield(optt,'fig_prefix') 
1114     optt.fig_prefix = [opt.fig_prefix '-k' num2str(k)];
1115   end
1116   [alpha, imgo, dv, opto] = feval(opt.line_search_func, img, sx, data0, img0, N, W, hps2RtR, hpt2LLt, dv, optt);
1117   if isfield(opt,'fig_prefix') 
1118     opto.fig_prefix = opt.fig_prefix;
1119   end
1120   if ~isempty(imgo)
1121      img = imgo;
1122   else
1123      img.elem_data = img.elem_data + alpha*sx;
1124   end
1125   if ~isempty(opto)
1126      opt = opto;
1127   end
1128 
1129   if(opt.verbose > 1)
1130      fprintf('      selected alpha=%0.3g\n', alpha);
1131   end
1132 
1133   if (alpha == 0) && (k == 1)
1134     error('first iteration failed to advance solution');
1135   end
1136 
1137 function err_if_inf_or_nan(x, str);
1138   if any(any(isnan(x) | isinf(x)))
1139       error(sprintf('bad %s (%d NaN, %d Inf of %d)', ...
1140                     str, ...
1141                     length(find(isnan(x))), ...
1142                     length(find(isinf(x))), ...
1143                     length(x(:))));
1144   end
1145 
1146 
1147 function [img, dv] = update_img_using_limits(img, img0, data0, N, dv, opt)
1148   
1149   if opt.max_value ~= +inf
1150      lih = find(img.elem_data > opt.max_value);
1151      img.elem_data(lih) = opt.max_value;
1152      if opt.verbose > 1
1153         fprintf('    limit max(x)=%g for %d elements\n', opt.max_value, length(lih));
1154      end
1155      dv = []; 
1156   end
1157   if opt.min_value ~= -inf
1158      lil = find(img.elem_data < opt.min_value);
1159      img.elem_data(lil) = opt.min_value;
1160      if opt.verbose > 1
1161         fprintf('    limit min(x)=%g for %d elements\n', opt.min_value, length(lil));
1162      end
1163      dv = []; 
1164   end
1165   
1166   [dv, opt] = update_dv(dv, img, data0, N, opt, '(dv out-of-date)');
1167 
1168 function  de = update_de(de, img, img0, opt)
1169    img0 = map_img(img0, opt.elem_working);
1170    img  = map_img(img,  opt.elem_working);
1171    err_if_inf_or_nan(img0.elem_data, 'de img0');
1172    err_if_inf_or_nan(img.elem_data,  'de img');
1173    
1174    
1175    
1176    if isempty(de) 
1177       
1178       de = zeros(size(img0.elem_data));
1179    else
1180       de = img.elem_data - img0.elem_data;
1181    end
1182    de(opt.elem_fixed) = 0; 
1183    err_if_inf_or_nan(de, 'de out');
1184 
1185 function [dv, opt] = update_dv(dv, img, data0, N, opt, reason)
1186    
1187    if ~isempty(dv) 
1188       return;
1189    end
1190    if nargin < 7
1191       reason = '';
1192    end
1193    if opt.verbose > 1
1194       disp(['    fwd_solve b=Ax ', reason]);
1195    end
1196    [dv, opt, err] = update_dv_core(img, data0, N, opt);
1197 
1198 
1199 
1200 function data = map_meas_struct(data, N, out)
1201    try
1202        current_meas_params = data.current_params;
1203    catch
1204        current_meas_params = 'voltage';
1205    end
1206    data.meas = map_meas(data.meas, N, current_meas_params, out);
1207    data.current_params = out;
1208    err_if_inf_or_nan(data.meas, 'dv meas');
1209 
1210 
1211 function [dv, opt, err] = update_dv_core(img, data0, N, opt)
1212    data0 = map_meas_struct(data0, N, 'voltage');
1213    img = map_img(img, map_img_base_types(img));
1214    img = feval(opt.update_img_func, img, opt);
1215    img = map_img(img, 'conductivity'); 
1216    
1217    if any(any(N ~= 1)) && any(strcmp(map_img_base_types(img), 'movement'))
1218       
1219       imgh=img; imgh.elem_data = imgh.elem_data(:,1)*0 +1; 
1220       vh = fwd_solve(imgh); vh = vh.meas;
1221       N = spdiag(1./vh);
1222       opt.fwd_solutions = opt.fwd_solutions +1;
1223    end
1224    data = fwd_solve(img);
1225    opt.fwd_solutions = opt.fwd_solutions +1;
1226    dv = calc_difference_data(data0, data, img.fwd_model);
1227 
1228    if nargout >= 3
1229       err = norm(dv)/norm(data0.meas);
1230    else
1231       err = NaN;
1232    end
1233    dv = map_meas(dv, N, 'voltage', opt.meas_working);
1234    err_if_inf_or_nan(dv, 'dv out');
1235 
1236 function show_fem_iter(k, img, inv_model, stop, opt)
1237   if (opt.verbose < 4) || (stop == -1)
1238      return; 
1239   end
1240   if opt.verbose > 1
1241      str=opt.show_fem;
1242      if isa(str,'function_handle')
1243         str=func2str(str);
1244      end
1245      disp(['    ' str '()']);
1246   end
1247   if isequal(opt.show_fem,@show_fem) 
1248      img = map_img(img, 'resistivity'); 
1249      [img, opt] = strip_c2f_background(img, opt, '    ');
1250      
1251      if isfield(inv_model, 'rec_model')
1252        img.fwd_model = inv_model.rec_model;
1253      end
1254    
1255    
1256    
1257      img.calc_colours.cb_shrink_move = [0.3,0.6,0.02]; 
1258      if size(img.elem_data,1) ~= size(img.fwd_model.elems,1)
1259         warning(sprintf('img.elem_data has %d elements, img.fwd_model.elems has %d elems\n', ...
1260                         size(img.elem_data,1), ...
1261                         size(img.fwd_model.elems,1)));
1262      end
1263   else 
1264      img = map_img(img, opt.elem_output);
1265      [img, opt] = strip_c2f_background(img, opt, '    ');
1266      if isfield(inv_model, 'rec_model')
1267        img.fwd_model = inv_model.rec_model;
1268      end
1269   end
1270   clf; feval(opt.show_fem, img, 1);
1271   title(sprintf('x @ iter=%d',k));
1272   drawnow;
1273   if isfield(opt,'fig_prefix')
1274      print('-dpdf',sprintf('%s-x%d',opt.fig_prefix,k));
1275      print('-dpng',sprintf('%s-x%d',opt.fig_prefix,k));
1276      saveas(gcf,sprintf('%s-x%d.fig',opt.fig_prefix,k));
1277   end
1278 
1279 function [ residual meas elem ] = GN_residual(dv, de, W, hps2RtR, hpt2LLt)
1280    
1281    
1282    
1283    
1284    Wdv = W*dv;
1285    meas = 0.5 * dv(:)' * Wdv(:);
1286    Rde = hps2RtR * de * hpt2LLt';
1287    elem = 0.5 * de(:)' * Rde(:);
1288    residual = meas + elem;
1289 
1290 function residual = meas_residual(dv, de, W, hps2RtR)
1291    residual = norm(dv);
1292 
1293 
1294 
1295 
1296 
1297 
1298 
1299 
1300 
1301 
1302 
1303 
1304 
1305 
1306 
1307 
1308 
1309 
1310 
1311 
1312 
1313 
1314 
1315 
1316 
1317 
1318 
1319 
1320 
1321 
1322 
1323 
1324 
1325 
1326 
1327 
1328 
1329 
1330 
1331 
1332 
1333 
1334 
1335 
1336 
1337 
1338 
1339 
1340 
1341 
1342 
1343 
1344 
1345 
1346 
1347 
1348 
1349 
1350 
1351 
1352 
1353 
1354 
1355 
1356 
1357 
1358 
1359 
1360 
1361 function opt = parse_options(imdl,n_frames)
1362    
1363 
1364 
1365 
1366    
1367    if isfield(imdl, 'inv_solve_core')
1368       opt = imdl.inv_solve_core;
1369    else
1370       opt = struct;
1371    end
1372 
1373    
1374    
1375    
1376    
1377    if ~isfield(opt,'verbose')
1378       opt.verbose = 1;
1379       fprintf('  selecting inv_model.inv_solve_core.verbose=1\n');
1380    end
1381    if opt.verbose > 1
1382       fprintf('  verbose = %d\n', opt.verbose);
1383       fprintf('  setting default parameters\n');
1384    end
1385    
1386    opt.fwd_solutions = 0;
1387 
1388    if ~isfield(opt, 'show_fem')
1389       opt.show_fem = @show_fem;
1390    end
1391 
1392    if ~isfield(opt, 'residual_func') 
1393       opt.residual_func = @GN_residual; 
1394       
1395       
1396       
1397       
1398    end
1399 
1400    
1401    if ~isfield(opt, 'update_func')
1402       opt.update_func = @GN_update; 
1403    end
1404    if ~isfield(opt, 'update_method')
1405       opt.update_method = 'cholesky';
1406    end
1407 
1408    
1409    if ~isfield(opt, 'calc_meas_icov')
1410       opt.calc_meas_icov = 0; 
1411    end
1412    if ~isfield(opt, 'calc_RtR_prior')
1413       opt.calc_RtR_prior = 0; 
1414    end
1415    if ~isfield(opt, 'calc_LLt_prior')
1416       opt.calc_LLt_prior = 0; 
1417    end
1418 
1419    if ~isfield(opt, 'calc_hyperparameter')
1420       opt.calc_hyperparameter = 0; 
1421    end
1422 
1423 
1424       if opt.verbose > 1
1425          fprintf('    examining function %s(...) for required arguments\n', func2str(opt.update_func));
1426       end
1427       
1428       
1429 
1430       args = ones(5,1); 
1431       if args(2) == 1
1432          opt.calc_meas_icov = 1;
1433       end
1434       if args(3) == 1
1435          opt.calc_hyperparameter = 1;
1436       end
1437       if args(4) == 1
1438          opt.calc_RtR_prior = 1;
1439       end
1440       if args(5) == 1
1441          opt.calc_LLt_prior = 1;
1442       end
1443 
1444 
1445 
1446 
1447    
1448    if ~isfield(opt, 'max_iterations')
1449       opt.max_iterations = 10;
1450    end
1451    if ~isfield(opt, 'ntol')
1452       opt.ntol = eps; 
1453    end
1454    if ~isfield(opt, 'tol')
1455       opt.tol = 0; 
1456    end
1457    if ~isfield(opt, 'dtol')
1458       
1459       
1460       
1461       opt.dtol = -1e-4; 
1462    end
1463    if opt.dtol > 0
1464       error('dtol must be less than 0 (residual decreases at every iteration)');
1465       
1466    end
1467    if ~isfield(opt, 'dtol_iter')
1468       
1469       opt.dtol_iter = 0; 
1470    end
1471    if ~isfield(opt, 'min_value')
1472       opt.min_value = -inf; 
1473    end
1474    if ~isfield(opt, 'max_value')
1475       opt.max_value = +inf; 
1476    end
1477    
1478    if ~isfield(opt, 'plot_residuals')
1479       if opt.verbose > 2
1480          opt.plot_residuals = 1;
1481       else
1482          opt.plot_residuals = 0;
1483       end
1484    end
1485    if opt.plot_residuals ~= 0
1486       disp('  residual plot (updated per iteration) are enabled, to disable them set');
1487       disp('    inv_model.inv_solve_core.plot_residuals=0');
1488    end
1489 
1490    
1491    if ~isfield(opt,'line_search_func')
1492       
1493       opt.line_search_func = @line_search_onm2;
1494    end
1495    if ~isfield(opt,'line_search_dv_func')
1496       opt.line_search_dv_func = @update_dv_core;
1497       
1498    end
1499    if ~isfield(opt,'line_search_de_func')
1500       
1501       
1502       opt.line_search_de_func = @(img, img0, opt) update_de(1, img, img0, opt);
1503       
1504    end
1505    
1506    
1507    if ~isfield(opt,'line_search_args') || ...
1508       ~isfield(opt.line_search_args, 'perturb')
1509       fmin = 1/4; 
1510       opt.line_search_args.perturb = [0 fmin/4 fmin/2 fmin fmin*2 fmin*4];
1511       
1512       
1513       
1514       
1515       
1516    end
1517    
1518    if ~isfield(opt,'line_search_args') || ...
1519       ~isfield(opt.line_search_args, 'plot')
1520       if opt.verbose >= 5
1521          opt.line_search_args.plot = 1;
1522       else
1523          opt.line_search_args.plot = 0;
1524       end
1525    end
1526    
1527    if isfield(opt,'fig_prefix') && ...
1528       isfield(opt,'line_search_args') && ...
1529       ~isfield(opt.line_search_args, 'fig_prefix')
1530       opt.line_search_args.fig_prefix = opt.fig_prefix;
1531    end
1532    
1533    if opt.line_search_args.plot ~= 0
1534       disp('  line search plots (per iteration) are enabled, to disable them set');
1535       disp('    inv_model.inv_solve_core.line_search_args.plot=0');
1536    end
1537 
1538    
1539    
1540    
1541    if ~isfield(opt, 'c2f_background')
1542      if isfield(imdl, 'fwd_model') && isfield(imdl.fwd_model, 'coarse2fine')
1543         opt.c2f_background = -1; 
1544      else
1545         opt.c2f_background = 0;
1546      end
1547    end
1548    if ~isfield(opt, 'c2f_background_fixed')
1549       opt.c2f_background_fixed = 1; 
1550    end
1551 
1552 
1553    
1554    
1555    if ~isfield(opt, 'elem_working')
1556       opt.elem_working = {'conductivity'};
1557    end
1558    if ~isfield(opt, 'elem_prior')
1559       opt.elem_prior = {'conductivity'};
1560    end
1561    if ~isfield(opt, 'elem_output')
1562       opt.elem_output = {'conductivity'};
1563    end
1564    if ~isfield(opt, 'meas_input')
1565       opt.meas_input = 'voltage';
1566    end
1567    if ~isfield(opt, 'meas_working')
1568       opt.meas_working = 'voltage';
1569    end
1570    
1571    
1572    
1573    for i = {'elem_working', 'elem_prior', 'elem_output', 'meas_input', 'meas_working'}
1574      
1575      
1576      x = opt.(i{1});
1577      if ~iscell(x)
1578         opt.(i{1}) = {x};
1579      end
1580    end
1581    if length(opt.meas_input) > 1
1582       error('imdl.inv_solve_core.meas_input: multiple measurement types not yet supported');
1583    end
1584    if length(opt.meas_working) > 1
1585       error('imdl.inv_solve_core.meas_working: multiple measurement types not yet supported');
1586    end
1587 
1588    if ~isfield(opt, 'prior_data')
1589       if isfield(imdl, 'jacobian_bkgnd') && ...
1590          isfield(imdl.jacobian_bkgnd, 'value') && ...
1591          length(opt.elem_prior) == 1
1592          opt.prior_data = {imdl.jacobian_bkgnd.value};
1593       else
1594          error('requires inv_model.inv_solve_core.prior_data');
1595       end
1596    end
1597 
1598    if ~isfield(opt, 'elem_len')
1599       if length(opt.elem_working) == 1
1600          if isfield(imdl.fwd_model, 'coarse2fine')
1601             c2f = imdl.fwd_model.coarse2fine; 
1602             opt.elem_len = { size(c2f,2) };
1603          else
1604             opt.elem_len = { size(imdl.fwd_model.elems,1) };
1605          end
1606       else
1607         error('requires inv_model.inv_solve_core.elem_len');
1608       end
1609    end
1610 
1611    if ~isfield(opt, 'n_frames')
1612       opt.n_frames = {n_frames};
1613    end
1614 
1615    
1616    
1617    
1618    if ~isfield(opt, 'elem_fixed') 
1619       opt.elem_fixed = [];
1620    elseif iscell(opt.elem_fixed) 
1621      
1622      
1623       offset=0;
1624       ef=[];
1625       for i=1:length(opt.elem_fixed)
1626          ef = [ef, opt.elem_fixed{i} + offset];
1627          offset = offset + opt.elem_len{i};
1628       end
1629       opt.elem_fixed = ef;
1630    end
1631 
1632    
1633    if ~isfield(opt, 'jacobian')
1634       if ~iscell(imdl.fwd_model.jacobian)
1635          opt.jacobian = {imdl.fwd_model.jacobian};
1636       else
1637          opt.jacobian = imdl.fwd_model.jacobian;
1638       end
1639    elseif isfield(imdl.fwd_model, 'jacobian')
1640       imdl.fwd_model
1641       imdl
1642       error('inv_model.fwd_model.jacobian and inv_model.inv_solve_core.jacobian should not both exist: it''s ambiguous');
1643    end
1644 
1645    if isfield(opt, 'hyperparameter')
1646       opt.hyperparameter_spatial = opt.hyperparameter; 
1647       opt = rmfield(opt, 'hyperparameter');
1648    end
1649    
1650    if ~isfield(opt, 'hyperparameter_spatial')
1651       opt.hyperparameter_spatial = {[]};
1652       for i=1:length(opt.elem_working)
1653          opt.hyperparameter_spatial{i} = 1;
1654       end
1655    end
1656    if ~isfield(opt, 'hyperparameter_temporal')
1657       opt.hyperparameter_temporal = {[]};
1658       for i=1:length(opt.meas_working)
1659          opt.hyperparameter_temporal{i} = 1;
1660       end
1661    end
1662    
1663    
1664    
1665    for i = {'elem_len', 'prior_data', 'jacobian', 'hyperparameter_spatial', 'hyperparameter_temporal'}
1666      
1667      
1668      x = opt.(i{1});
1669      if ~iscell(x)
1670         opt.(i{1}) = {x};
1671      end
1672    end
1673    
1674    if opt.verbose > 1
1675       fprintf('  hyperparameters\n');
1676       try
1677           hp_global = imdl.hyperparameter.value;
1678           hp_global_str = sprintf(' x %0.4g',hp_global);
1679       catch
1680           hp_global = 1;
1681           hp_global_str = '';
1682       end
1683       for i=1:length(opt.elem_working)
1684          if isnumeric(opt.hyperparameter_spatial{i}) && length(opt.hyperparameter_spatial{i}) == 1
1685             fprintf('    %s: %0.4g\n',opt.elem_working{i}, opt.hyperparameter_spatial{i}*hp_global);
1686          elseif isa(opt.hyperparameter_spatial{i}, 'function_handle')
1687             fprintf('    %s: @%s%s\n',opt.elem_working{i}, func2str(opt.hyperparameter_spatial{i}), hp_global_str);
1688          elseif ischar(opt.hyperparameter_spatial{i})
1689             fprintf('    %s: @%s%s\n',opt.elem_working{i}, opt.hyperparameter_spatial{i}, hp_global_str);
1690          else
1691             fprintf('    %s: ...\n',opt.elem_working{i});
1692          end
1693       end
1694       for i=1:length(opt.meas_working)
1695          if isnumeric(opt.hyperparameter_temporal{i}) && length(opt.hyperparameter_temporal{i}) == 1
1696             fprintf('    %s: %0.4g\n',opt.meas_working{i}, opt.hyperparameter_temporal{i}*hp_global);
1697          elseif isa(opt.hyperparameter_temporal{i}, 'function_handle')
1698             fprintf('    %s: @%s%s\n',opt.meas_working{i}, func2str(opt.hyperparameter_temporal{i}), hp_global_str);
1699          elseif ischar(opt.hyperparameter_temporal{i})
1700             fprintf('    %s: @%s%s\n',opt.meas_working{i}, opt.hyperparameter_temporal{i}, hp_global_str);
1701          else
1702             fprintf('    %s: ...\n',opt.meas_working{i});
1703          end
1704       end
1705    end
1706 
1707    
1708    
1709    
1710    
1711    if ~isfield(opt, 'RtR_prior')
1712       if isfield(imdl, 'RtR_prior')
1713          opt.RtR_prior = {imdl.RtR_prior};
1714       else
1715          opt.RtR_prior = {[]}; 
1716          warning('missing imdl.inv_solve_core.RtR_prior or imdl.RtR_prior: assuming NO spatial regularization RtR=0');
1717       end
1718    end
1719    
1720    
1721    if isnumeric(opt.RtR_prior)
1722       if size(opt.RtR_prior, 1) ~= size(opt.RtR_prior, 2)
1723          error('expecting square matrix for imdl.RtR_prior or imdl.inv_solve_core.RtR_prior');
1724       end
1725       if length(opt.RtR_prior) == 1
1726          opt.RtR_prior = {opt.RtR_prior}; 
1727       else
1728          RtR = opt.RtR_prior;
1729          opt.RtR_prior = {[]};
1730          esi = 0; eei = 0;
1731          for i=1:length(opt.elem_len)
1732             esi = eei +1;
1733             eei = eei +opt.elem_len{i};
1734             esj = 0; eej = 0;
1735             for j=1:length(opt.elem_len)
1736                esj = eej +1;
1737                eej = eej +opt.elem_len{j};
1738                opt.RtR_prior(i,j) = RtR(esi:eei, esj:eej);
1739             end
1740          end
1741       end
1742    elseif ~iscell(opt.RtR_prior) 
1743       opt.RtR_prior = {opt.RtR_prior};
1744    end
1745    
1746    
1747    if any(size(opt.RtR_prior) ~= ([1 1]*length(opt.elem_len)))
1748       if (size(opt.RtR_prior, 1) ~= 1) && ...
1749          (size(opt.RtR_prior, 2) ~= 1)
1750          error('odd imdl.RtR_prior or imdl.inv_solve_core.RtR_prior, cannot figure out how to expand it blockwise');
1751       end
1752       if (size(opt.RtR_prior, 1) ~= length(opt.elem_len)) && ...
1753          (size(opt.RtR_prior, 2) ~= length(opt.elem_len))
1754          error('odd imdl.RtR_prior or imdl.inv_solve_core.RtR_prior, not enough blockwise components vs. elem_working types');
1755       end
1756       RtR_diag = opt.RtR_prior;
1757       opt.RtR_prior = {[]}; 
1758       for i=1:length(opt.elem_len)
1759          opt.RtR_prior(i,i) = RtR_diag(i);
1760       end
1761    end
1762    
1763    hp=opt.hyperparameter_spatial;
1764    if size(hp,1) == 1 
1765       hp = hp'; 
1766    end
1767    if iscell(hp)
1768       
1769       if all(size(hp) == size(opt.RtR_prior))
1770          opt.hyperparameter_spatial = hp;
1771       
1772       elseif length(hp) == length(opt.RtR_prior)
1773          opt.hyperparameter_spatial = opt.RtR_prior;
1774          [opt.hyperparameter_spatial{:}] = deal(1); 
1775          opt.hyperparameter_spatial(logical(eye(size(opt.RtR_prior)))) = hp; 
1776       else
1777          error('hmm, don''t understand this opt.hyperparameter_spatial cellarray');
1778       end
1779    
1780    elseif isnumeric(hp)
1781       opt.hyperparameter_spatial = opt.RtR_prior;
1782       [opt.hyperparameter_spatial{:}] = deal({hp});
1783    else
1784       error('don''t understand this opt.hyperparameter_spatial');
1785    end
1786    
1787    
1788    
1789    
1790    
1791    if ~isfield(opt, 'LLt_prior')
1792       if isfield(imdl, 'LLt_prior')
1793          opt.LLt_prior = {imdl.LLt_prior};
1794       else
1795          opt.LLt_prior = {eye(n_frames)};
1796          if n_frames ~= 1
1797             fprintf('warning: missing imdl.inv_solve_core.LLt_prior or imdl.LLt_prior: assuming NO temporal regularization LLt=I\n');
1798          end
1799       end
1800    end
1801    
1802    
1803    if isnumeric(opt.LLt_prior)
1804       if size(opt.LLt_prior, 1) ~= size(opt.LLt_prior, 2)
1805          error('expecting square matrix for imdl.LLt_prior or imdl.inv_solve_core.LLt_prior');
1806       end
1807       if length(opt.LLt_prior) == 1
1808          opt.LLt_prior = {opt.LLt_prior}; 
1809       else
1810          LLt = opt.LLt_prior;
1811          opt.LLt_prior = {[]};
1812          msi = 0; mei = 0;
1813          for i=1:length(opt.n_frames)
1814             msi = mei +1;
1815             mei = mei +opt.n_frames{i};
1816             msj = 0; mej = 0;
1817             for j=1:length(opt.n_frames)
1818                msj = mej +1;
1819                mej = mej +opt.n_frames{j};
1820                opt.LLt_prior(i,j) = LLt(msi:mei, msj:mej);
1821             end
1822          end
1823       end
1824    elseif ~iscell(opt.LLt_prior) 
1825       opt.LLt_prior = {opt.LLt_prior};
1826    end
1827    
1828    
1829    if any(size(opt.LLt_prior) ~= ([1 1]*length(opt.n_frames)))
1830       if (size(opt.LLt_prior, 1) ~= 1) && ...
1831          (size(opt.LLt_prior, 2) ~= 1)
1832          error('odd imdl.LLt_prior or imdl.inv_solve_core.LLt_prior, cannot figure out how to expand it blockwise');
1833       end
1834       if (size(opt.LLt_prior, 1) ~= length(opt.n_frames)) && ...
1835          (size(opt.LLt_prior, 2) ~= length(opt.n_frames))
1836          error('odd imdl.LLt_prior or imdl.inv_solve_core.LLt_prior, not enough blockwise components vs. meas_working types');
1837       end
1838       LLt_diag = opt.LLt_prior;
1839       opt.LLt_prior = {[]}; 
1840       for i=1:length(opt.n_frames)
1841          opt.LLt_prior(i,i) = LLt_diag(i);
1842       end
1843    end
1844    
1845    hp=opt.hyperparameter_temporal;
1846    if size(hp,1) == 1 
1847       hp = hp'; 
1848    end
1849    if iscell(hp)
1850       
1851       if all(size(hp) == size(opt.LLt_prior))
1852          opt.hyperparameter_temporal = hp;
1853       
1854       elseif length(hp) == length(opt.LLt_prior)
1855          opt.hyperparameter_temporal = opt.LLt_prior;
1856          [opt.hyperparameter_temporal{:}] = deal(1); 
1857          opt.hyperparameter_temporal(logical(eye(size(opt.LLt_prior)))) = hp; 
1858       else
1859          error('hmm, don''t understand this opt.hyperparameter_temporal cellarray');
1860       end
1861    
1862    elseif isnumeric(hp)
1863       opt.hyperparameter_temporal = opt.LLt_prior;
1864       [opt.hyperparameter_temporal{:}] = deal({hp});
1865    else
1866       error('don''t understand this opt.hyperparameter_temporal');
1867    end
1868 
1869    
1870    
1871    
1872    
1873    
1874    if ~isfield(opt, 'calc_jacobian_scaling_func')
1875       pinv = strfind(opt.elem_working, 'resistivity');
1876       plog = strfind(opt.elem_working, 'log_');
1877       plog10 = strfind(opt.elem_working, 'log10_');
1878       for i = 1:length(opt.elem_working)
1879         prefix = '';
1880         if plog{i}
1881            prefix = 'log';
1882         elseif plog10{i}
1883            prefix = 'log10';
1884         else
1885            prefix = '';
1886         end
1887         if pinv{i}
1888            prefix = [prefix '_inv'];
1889         end
1890         switch(prefix)
1891           case ''
1892              opt.calc_jacobian_scaling_func{i} = @ret1_func;  
1893           case 'log'
1894              opt.calc_jacobian_scaling_func{i} = @dx_dlogx;   
1895           case 'log10'
1896              opt.calc_jacobian_scaling_func{i} = @dx_dlog10x; 
1897           case '_inv'
1898              opt.calc_jacobian_scaling_func{i} = @dx_dy;      
1899           case 'log_inv'
1900              opt.calc_jacobian_scaling_func{i} = @dx_dlogy;   
1901           case 'log10_inv'
1902              opt.calc_jacobian_scaling_func{i} = @dx_dlog10y; 
1903           otherwise
1904              error('oops');
1905        end
1906      end
1907    end
1908 
1909    if ~isfield(opt, 'update_img_func')
1910       opt.update_img_func = @null_func; 
1911    end
1912 
1913    if ~isfield(opt, 'return_working_variables')
1914       opt.return_working_variables = 0;
1915    end
1916 
1917 function check_matrix_sizes(J, W, hps2RtR, hpt2LLt, dv, de, opt)
1918    
1919    
1920    
1921    ne = size(de,1);
1922    nv = size(dv,1);
1923    nf = size(dv,2);
1924    if size(de,2) ~= size(dv,2)
1925       error('de cols (%d) not equal to dv cols (%d)', size(de,2), size(dv,2));
1926    end
1927    if opt.calc_meas_icov && ...
1928       any(size(W) ~= [nv nv])
1929       error('W size (%d rows, %d cols) is incorrect (%d rows, %d cols)', size(W), nv, nv);
1930    end
1931    if any(size(J) ~= [nv ne])
1932       error('J size (%d rows, %d cols) is incorrect (%d rows, %d cols)', size(J), nv, ne);
1933    end
1934    if opt.calc_RtR_prior && ...
1935       any(size(hps2RtR) ~= [ne ne])
1936       error('hps2RtR size (%d rows, %d cols) is incorrect (%d rows, %d cols)', size(hps2RtR), ne, ne);
1937    end
1938    if opt.calc_LLt_prior && ...
1939       any(size(hpt2LLt) ~= [nf nf])
1940       error('hpt2LLt size (%d rows, %d cols) is incorrect (%d rows, %d cols)', size(hpt2LLt), nf, nf);
1941    end
1942 
1943 function dx = update_dx(J, W, hps2RtR, hpt2LLt, dv, de, opt)
1944    if(opt.verbose > 1)
1945       fprintf( '    calc step size dx\n');
1946    end
1947    
1948    de(opt.elem_fixed) = 0;
1949 
1950    
1951    check_matrix_sizes(J, W, hps2RtR, hpt2LLt, dv, de, opt)
1952 
1953    
1954    J(:,opt.elem_fixed) = 0;
1955    de(opt.elem_fixed,:) = 0;
1956    hps2RtR(opt.elem_fixed,:) = 0;
1957    V=opt.elem_fixed;
1958    N=size(hps2RtR,1)+1;
1959    hps2RtR(N*(V-1)+1) = 1; 
1960    
1961    dx = feval(opt.update_func, J, W, hps2RtR, hpt2LLt, dv, de, opt);
1962 
1963    
1964    if any(dx(opt.elem_fixed) ~= 0)
1965       error('elem_fixed did''t give dx=0 at update_dx')
1966    end
1967 
1968    if(opt.verbose > 1)
1969       fprintf('      ||dx||=%0.3g\n', norm(dx));
1970       es = 0; ee = 0;
1971       for i=1:length(opt.elem_working)
1972           es = ee +1; ee = ee + opt.elem_len{i};
1973           nd = norm(dx(es:ee));
1974           fprintf( '      ||dx_%d||=%0.3g (%s)\n',i, nd, opt.elem_working{i});
1975       end
1976    end
1977 
1978 function dx = GN_update(J, W, hps2RtR, hpt2LLt, dv, de, opt)
1979    if ~any(strcmp(opt.update_method, {'cholesky','pcg'}))
1980       error(['unsupported update_method: ',opt.update_method]);
1981    end
1982    if strcmp(opt.update_method, 'cholesky')
1983       try
1984          
1985          if any(any(hpt2LLt ~= eye(size(hpt2LLt))))
1986             
1987             nf = size(dv,2); 
1988             JtWJ = kron(eye(nf),J'*W*J);
1989             RtR = kron(hpt2LLt,hps2RtR);
1990             JtW = kron(eye(nf),J'*W);
1991             
1992             
1993             dx = -left_divide( (JtWJ + RtR), (JtW*dv(:) + RtR*de(:)) ); 
1994             
1995             dx = reshape(dx,length(dx)/nf,nf);
1996          else
1997             
1998             dx = -left_divide( (J'*W*J + hps2RtR), (J'*W*dv + hps2RtR*de) ); 
1999          end
2000       catch ME 
2001          fprintf('      Cholesky failed: ');
2002          disp(ME.message)
2003          fprintf('      try opt.update_method = ''pcg'' on future runs\n');
2004          opt.update_method = 'pcg';
2005       end
2006    end
2007    if strcmp(opt.update_method, 'pcg')
2008       tol = 1e-6; 
2009       maxit = []; 
2010       M = []; 
2011       x0 = []; 
2012 
2013       
2014       
2015       nm = size(de,1); nf = size(de,2);
2016       X = @(x) reshape(x,nm,nf); 
2017 
2018       LHS = @(X) (J'*(W*(J*X)) + hps2RtR*(X*hpt2LLt));
2019       RHS = -(J'*(W*dv) + hps2RtR*(de*hpt2LLt));
2020 
2021       LHSv = @(x) reshape(LHS(X(x)),nm*nf,1); 
2022       RHSv = reshape(RHS,nm*nf,1);
2023 
2024       tol=100*eps*size(J,2)^2; 
2025 
2026 
2027       [dx, flag, relres, iter, resvec] = pcg(LHSv, RHSv, tol, maxit, M', M', x0);
2028       dx = X(dx);
2029       
2030       switch flag
2031          case 0
2032             if opt.verbose > 1
2033                fprintf('      PCG: relres=%g < tol=%g @ iter#%d\n',relres,tol,iter);
2034             end
2035          case 1
2036             if opt.verbose > 1
2037                fprintf('      PCG: relres=%g > tol=%g @ iter#%d (max#%d) [max iter]\n',relres,tol,iter,maxit);
2038             end
2039          case 2
2040             error('error: PCG ill-conditioned preconditioner M');
2041          case 3
2042             if opt.verbose > 1
2043                fprintf('      PCG: relres=%g > tol=%g @ iter#%d (max#%d) [stagnated]\n',relres,tol,iter,maxit);
2044             end
2045          case 4
2046             error('error: PCG a scalar quantity became too large or small to continue');
2047          otherwise
2048             error(sprintf('error: PCG unrecognized flag=%d',flag));
2049       end
2050 
2051 
2052 
2053 
2054 
2055 
2056 
2057 
2058 
2059 
2060 
2061 
2062 
2063 
2064 
2065 
2066 
2067 
2068 
2069 
2070       
2071       
2072 
2073       
2074    end
2075 
2076 
2077 
2078 function args = function_depends_upon(func, argn)
2079    
2080    str = sprintf('%s(',func2str(func));
2081    args = zeros(argn,1);
2082    for i = 1:argn-1
2083       str = [str sprintf('a(%d),',i)];
2084    end
2085    str = [str sprintf('a(%d))',argn)];
2086    
2087    a = ones(argn,1)*2;
2088    x = eval(str);
2089    
2090    for i = 1:argn
2091       a = ones(argn,1)*2;
2092       a(i) = 0;
2093       y = eval(str);
2094       if any(x ~= y)
2095          args(i) = 1;
2096       end
2097    end
2098 
2099 
2100 function out = null_func(in, varargin);
2101    out = in;
2102 
2103 
2104 function [out, x, y, z] = ret1_func(varargin);
2105    out = 1;
2106    x = [];
2107    y = [];
2108    z = [];
2109 
2110 
2111 
2112 function [inv_model, opt] = append_c2f_background(inv_model, opt)
2113     
2114     
2115     if opt.c2f_background >= 0
2116       return
2117     end
2118     
2119     
2120     c2f = inv_model.fwd_model.coarse2fine; 
2121     nf = size(inv_model.fwd_model.elems,1); 
2122     nc = size(c2f,2); 
2123     
2124     
2125     
2126     
2127     
2128     
2129     fel = sum(c2f,2); 
2130     n = find(fel < 1 - (1e3+nc)*eps);
2131     
2132     
2133     
2134     
2135     if length(n) ~= 0
2136       if(opt.verbose > 1)
2137         fprintf('  c2f: adding background conductivity to %d\n    fwd_model elements not covered by rec_model\n', length(n));
2138       end
2139       c2f(n,nc+1) = 1 - fel(n);
2140       inv_model.fwd_model.coarse2fine = c2f;
2141       opt.c2f_background = nc+1;
2142       if opt.c2f_background_fixed
2143          
2144          opt.elem_fixed(end+1) = nc+1;
2145       end
2146     end
2147     
2148     opt.elem_len(1) = {size(c2f,2)}; 
2149 
2150 function [img, opt] = strip_c2f_background(img, opt, indent)
2151     if nargin < 3
2152        indent = '';
2153     end
2154     
2155     if opt.c2f_background <= 0
2156       return;
2157     end
2158 
2159     
2160     
2161     
2162     in = img.current_params;
2163     out = opt.elem_output;
2164     if iscell(in)
2165        in = in{1};
2166     end
2167     if iscell(out)
2168        out = out{1};
2169     end
2170 
2171     
2172     e = opt.c2f_background;
2173     
2174     
2175     bg = map_data(img.elem_data(e), in, out);
2176     img.elem_data_background = bg;
2177     
2178     img.elem_data(e) = [];
2179     img.fwd_model.coarse2fine(:,e) = [];
2180     
2181     opt.c2f_background = 0;
2182     ri = find(opt.elem_fixed == e);
2183     opt.elem_fixed(ri) = [];
2184     if isfield(img, 'params_sel')
2185        for i = 1:length(img.params_sel)
2186           t = img.params_sel{i};
2187           ti = find(t == e);
2188           t(ti) = []; 
2189           ti = find(t > e);
2190           t(ti) = t(ti)-1; 
2191           img.params_sel{i} = t;
2192        end
2193     end
2194 
2195     
2196     if(opt.verbose > 1)
2197        bg = img.elem_data_background;
2198        bg = map_data(bg, in, 'resistivity');
2199        fprintf('%s  background conductivity: %0.1f Ohm.m\n', indent, bg);
2200     end
2201 
2202 function b = has_params(s)
2203 b = false;
2204 if isstruct(s)
2205    b = any(ismember(fieldnames(s),supported_params));
2206 end
2207 
2208 
2209 function out = map_img_base_types(img)
2210   out = to_base_types(img.current_params);
2211 
2212 
2213 
2214 
2215 function type = to_base_types(type)
2216   if ~iscell(type)
2217      type = {type};
2218   end
2219   for i = 1:length(type);
2220      type(i) = {strrep(type{i}, 'log_', '')};
2221      type(i) = {strrep(type{i}, 'log10_', '')};
2222      type(i) = {strrep(type{i}, 'resistivity', 'conductivity')};
2223      type(i) = {strrep(type{i}, 'apparent_resistivity', 'voltage')};
2224   end
2225 
2226 function img = map_img(img, out);
2227    err_if_inf_or_nan(img.elem_data, 'img-pre');
2228    try
2229        in = img.current_params;
2230    catch
2231        in = {'conductivity'};
2232    end
2233    
2234    if ischar(in)
2235       in = {in};
2236       img.current_params = in;
2237    end
2238    if ischar(out)
2239       out = {out};
2240    end
2241 
2242    
2243    if ~isfield(img, 'params_sel')
2244       if length(in(:)) == 1
2245          img.params_sel = {1:size(img.elem_data,1)};
2246       else
2247          error('found multiple parametrizations (params) but no params_sel cell array in img');
2248       end
2249    end
2250 
2251    
2252    if length(out(:)) > length(in(:))
2253       error('missing data (more out types than in types)');
2254    elseif length(out(:)) < length(in(:))
2255       
2256       
2257       
2258       
2259       if length(out(:)) ~= 1
2260          error('map_img can convert ALL parameters or select a SINGLE output type from multiple input types');
2261       end
2262       inm  = to_base_types(in);
2263       outm = to_base_types(out);
2264       del = sort(find(~strcmp(outm(1), inm(:))), 'descend'); 
2265       if length(del)+1 ~= length(in)
2266          error('Confused about what to remove from the img. You''ll need to sort the parametrizations out yourself when removing data.');
2267       end
2268       for i = del(:)' 
2269          ndel = length(img.params_sel{i}); 
2270          for j = i+1:length(img.params_sel)
2271             img.params_sel{j} = img.params_sel{j} - ndel;
2272          end
2273          img.elem_data(img.params_sel{i}) = []; 
2274          img.params_sel(i) = []; 
2275          img.current_params(i) = []; 
2276       end
2277       in = img.current_params;
2278    end
2279 
2280    
2281    for i = 1:length(out(:))
2282       
2283       x = img.elem_data(img.params_sel{i});
2284       x = map_data(x, in{i}, out{i});
2285       img.elem_data(img.params_sel{i}) = x;
2286       img.current_params{i} = out{i};
2287    end
2288    err_if_inf_or_nan(img.elem_data, 'img-post');
2289 
2290    
2291    if length(img.current_params(:)) == 1
2292       img.current_params = img.current_params{1};
2293       img = rmfield(img, 'params_sel'); 
2294    end
2295 
2296 function x = map_data(x, in, out)
2297    
2298    if ~ischar(in)
2299       if iscell(in) && (length(in(:)) == 1)
2300          in = in{1};
2301       else
2302          error('expecting single string for map_data() "in" type');
2303       end
2304    end
2305    if ~ischar(out)
2306       if iscell(out) && (length(out(:)) == 1)
2307          out = out{1};
2308       else
2309          error('expecting single string for map_data() "out" type');
2310       end
2311    end
2312 
2313    
2314    if strcmp(in, out) 
2315       return; 
2316    end
2317 
2318    
2319    
2320    
2321    if any(strcmp(in,  {'resistivity', 'conductivity'})) && ...
2322       any(strcmp(out, {'resistivity', 'conductivity'}))
2323       x = 1./x; 
2324    
2325    elseif any(strcmp({in(1:3), out(1:3)}, 'log'))
2326       
2327       if strcmp(in(1:6), 'log10_')
2328          if any(x >= log10(realmax)-eps) warning('loss of precision -> inf'); end
2329          x = map_data(10.^x, in(7:end), out);
2330       
2331       elseif strcmp(in(1:4), 'log_')
2332          if any(x >= log(realmax)-eps) warning('loss of precision -> inf'); end
2333          x = map_data(exp(x), in(5:end), out);
2334       
2335       elseif strcmp(out(1:6), 'log10_')
2336          if any(x <= 0 + eps) warning('loss of precision -> -inf'); end
2337          x = log10(map_data(x, in, out(7:end)));
2338       
2339       elseif strcmp(out(1:4), 'log_')
2340          if any(x <= 0 + eps) warning('loss of precision -> -inf'); end
2341          x = log(map_data(x, in, out(5:end)));
2342       else
2343          error(sprintf('unknown conversion (log conversion?) %s - > %s', in, out));
2344       end
2345    else
2346       error('unknown conversion %s -> %s', in, out);
2347    end
2348    x(x == +inf) = +realmax;
2349    x(x == -inf) = -realmax;
2350    err_if_inf_or_nan(x, 'map_data-post');
2351 
2352 function b = map_meas(b, N, in, out)
2353    err_if_inf_or_nan(b, 'map_meas-pre');
2354    if strcmp(in, out) 
2355       return; 
2356    end
2357 
2358    
2359    
2360    if     strcmp(in, 'voltage') && strcmp(out, 'apparent_resistivity')
2361       if N == 1
2362          error('missing apparent resistivity conversion factor N');
2363       end
2364       b = N * b; 
2365    elseif strcmp(in, 'apparent_resistivity') && strcmp(out, 'voltage')
2366       if N == 1
2367          error('missing apparent resistivity conversion factor N');
2368       end
2369       b = N \ b; 
2370    
2371    elseif any(strcmp({in(1:3), out(1:3)}, 'log'))
2372       
2373       if strcmp(in(1:6), 'log10_')
2374          if any(b > log10(realmax)-eps) warning('loss of precision -> inf'); end
2375          b = map_meas(10.^b, N, in(7:end), out);
2376       
2377       elseif strcmp(in(1:4), 'log_')
2378          if any(b > log(realmax)-eps) warning('loss of precision -> inf'); end
2379          b = map_meas(exp(b), N, in(5:end), out);
2380       
2381       elseif strcmp(out(1:6), 'log10_')
2382          if any(b <= 0+eps) warning('loss of precision -> -inf'); end
2383          b = log10(map_meas(b, N, in, out(7:end)));
2384       
2385       elseif strcmp(out(1:4), 'log_')
2386          if any(b <= 0+eps) warning('loss of precision -> -inf'); end
2387          b = log(map_meas(b, N, in, out(5:end)));
2388       else
2389          error(sprintf('unknown conversion (log conversion?) %s - > %s', in, out));
2390       end
2391    else
2392       error('unknown conversion %s -> %s', in, out);
2393    end
2394    err_if_inf_or_nan(b, 'map_meas-post');
2395 
2396 function x=range(y)
2397 x=max(y)-min(y);
2398 
2399 function pass=do_unit_test(solver)
2400    if nargin == 0
2401       solver = 'inv_solve_core';
2402       test = 'all'
2403    elseif ((length(solver) < 9) || ~strcmp(solver(1:9),'inv_solve'))
2404       test = solver;
2405       solver = 'inv_solve_core';
2406    else
2407       test = 'all'
2408    end
2409    switch(test)
2410       case 'all'
2411          do_unit_test_sub;
2412          do_unit_test_diff;
2413          do_unit_test_rec1(solver);
2414          do_unit_test_rec2(solver);
2415          do_unit_test_rec_mv(solver);
2416          do_unit_test_img0_bg();
2417          do_unit_test_diffseq(solver);
2418          do_unit_test_absseq(solver);
2419       case 'sub'
2420          do_unit_test_sub;
2421       case 'diff'
2422          do_unit_test_diff;
2423       case 'rec1'
2424          do_unit_test_rec1(solver);
2425       case 'rec2'
2426          do_unit_test_rec2(solver);
2427       case 'rec_mv'
2428          do_unit_test_rec_mv(solver);
2429       case 'diffseq'
2430          do_unit_test_diffseq(solver);
2431       case 'absseq'
2432          do_unit_test_absseq(solver);
2433       otherwise
2434          error(['unrecognized solver or tests: ' test]);
2435    end
2436 
2437 function [imdl, vh, imgi, vi] = unit_test_imdl()
2438    imdl = mk_geophysics_model('h22e',32);
2439    imdl.solve = 'inv_solve_core';
2440    imdl.inv_solve_core.max_iterations = 1; 
2441    imdl.inv_solve_core.verbose = 0;
2442    imdl.reconst_type = 'difference';
2443    imgh = mk_image(imdl,1);
2444    vh = fwd_solve(imgh);
2445 
2446    if nargout > 2
2447       imgi = imgh;
2448       ctrs = interp_mesh(imdl.fwd_model);
2449       x = ctrs(:,1);
2450       y = ctrs(:,2);
2451       r1 = sqrt((x+15).^2 + (y+25).^2);
2452       r2 = sqrt((x-85).^2 + (y+65).^2);
2453       imgi.elem_data(r1<25)= 1/2;
2454       imgi.elem_data(r2<30)= 2;
2455       clf; show_fem(imgi,1);
2456       vi = fwd_solve(imgi);
2457    end
2458 
2459 function do_unit_test_diff()
2460    [imdl, vh, imgi, vi] = unit_test_imdl();
2461 
2462    imdl.reconst_type = 'absolute';
2463    imdl.inv_solve_core.line_search_func = @ret1_func;
2464    img_abs = inv_solve(imdl,vi);
2465    imdl.reconst_type = 'difference';
2466    img_itr = inv_solve(imdl,vh,vi);
2467    imdl.inv_solve_core.max_iterations = 1; 
2468    img_it1 = inv_solve(imdl,vh,vi);
2469    imdl.solve = 'inv_solve_diff_GN_one_step';
2470    img_gn1 = inv_solve(imdl,vh,vi);
2471    clf; subplot(223); show_fem(img_it1,1); title('GN 1-iter');
2472         subplot(224); show_fem(img_gn1,1); title('GN 1-step');
2473         subplot(221); h=show_fem(imgi,1);  title('fwd model'); set(h,'EdgeColor','none');
2474         subplot(222); show_fem(img_itr,1); title('GN 10-iter');
2475         subplot(222); show_fem(img_abs,1); title('GN abs 10-iter');
2476    unit_test_cmp('core (1-step) vs. diff_GN_one_step', img_it1.elem_data, img_gn1.elem_data, eps*15);
2477    unit_test_cmp('core (1-step) vs. core (N-step)   ', img_it1.elem_data, img_itr.elem_data, eps*2);
2478    unit_test_cmp('core (N-step) vs. abs  (N-step)   ', img_it1.elem_data, img_abs.elem_data-1, eps);
2479 
2480 
2481 
2482 
2483 function do_unit_test_sub
2484 d = 1;
2485 while d ~= 1 & d ~= 0
2486   d = rand(1);
2487 end
2488 disp('TEST: map_data()');
2489 elem_types = {'conductivity', 'log_conductivity', 'log10_conductivity', ...
2490               'resistivity',  'log_resistivity',  'log10_resistivity'};
2491 expected = [d         log(d)         log10(d)      1./d      log(1./d)      log10(1./d); ...
2492             exp(d)    d              log10(exp(d)) 1./exp(d) log(1./exp(d)) log10(1./exp(d)); ...
2493             10.^d     log(10.^d )    d             1./10.^d  log(1./10.^d ) log10(1./10.^d ); ...
2494             1./d      log(1./d  )    log10(1./d)   d         log(d)         log10(d); ...
2495             1./exp(d) log(1./exp(d)) log10(1./exp(d)) exp(d) d              log10(exp(d)); ...
2496             1./10.^d  log(1./10.^d)  log10(1./10.^d)  10.^d  log(10.^d)     d ];
2497 for i = 1:length(elem_types)
2498   for j = 1:length(elem_types)
2499     test_map_data(d, elem_types{i}, elem_types{j}, expected(i,j));
2500   end
2501 end
2502 
2503 disp('TEST: map_meas()');
2504 N = 1/15;
2505 Ninv = 1/N;
2506 
2507 elem_types = {'voltage', 'log_voltage', 'log10_voltage', ...
2508               'apparent_resistivity',  'log_apparent_resistivity',  'log10_apparent_resistivity'};
2509 expected = [d         log(d)         log10(d)      N*d      log(N*d)      log10(N*d); ...
2510             exp(d)    d              log10(exp(d)) N*exp(d) log(N*exp(d)) log10(N*exp(d)); ...
2511             10.^d     log(10.^d )    d             N*10.^d  log(N*10.^d ) log10(N*10.^d ); ...
2512             Ninv*d      log(Ninv*d  )    log10(Ninv*d)   d         log(d)         log10(d); ...
2513             Ninv*exp(d) log(Ninv*exp(d)) log10(Ninv*exp(d)) exp(d) d              log10(exp(d)); ...
2514             Ninv*10.^d  log(Ninv*10.^d)  log10(Ninv*10.^d)  10.^d  log(10.^d)     d ];
2515 for i = 1:length(elem_types)
2516   for j = 1:length(elem_types)
2517     test_map_meas(d, N, elem_types{i}, elem_types{j}, expected(i,j));
2518   end
2519 end
2520 
2521 disp('TEST: Jacobian scaling');
2522 d = [d d]';
2523 unit_test_cmp( ...
2524    sprintf('Jacobian scaling (%s)', 'conductivity'), ...
2525    ret1_func(d), 1);
2526 
2527 unit_test_cmp( ...
2528    sprintf('Jacobian scaling (%s)', 'log_conductivity'), ...
2529    dx_dlogx(d), diag(d));
2530 
2531 unit_test_cmp( ...
2532    sprintf('Jacobian scaling (%s)', 'log10_conductivity'), ...
2533    dx_dlog10x(d), diag(d)*log(10));
2534 
2535 unit_test_cmp( ...
2536    sprintf('Jacobian scaling (%s)', 'resistivity'), ...
2537    dx_dy(d), diag(-d.^2));
2538 
2539 unit_test_cmp( ...
2540    sprintf('Jacobian scaling (%s)', 'log_resistivity'), ...
2541    dx_dlogy(d), diag(-d));
2542 
2543 unit_test_cmp( ...
2544    sprintf('Jacobian scaling (%s)', 'log10_resistivity'), ...
2545    dx_dlog10y(d), diag(-d)/log(10));
2546 
2547 
2548 function test_map_data(data, in, out, expected)
2549 
2550    calc_val = map_data(data, in, out);
2551    str = sprintf('map_data(%s -> %s)', in, out);
2552    unit_test_cmp(str, calc_val, expected)
2553 
2554 function test_map_meas(data, N, in, out, expected)
2555 
2556    calc_val = map_meas(data, N, in, out);
2557    str = sprintf('map_data(%s -> %s)', in, out);
2558    unit_test_cmp(str, calc_val, expected,100*eps)
2559 
2560 
2561 
2562 
2563 function do_unit_test_rec1(solver)
2564 
2565 
2566 
2567 
2568 
2569 [imdl, vh, imgi, vi] = unit_test_imdl();
2570 imdl.solve = solver;
2571 imdl.reconst_type = 'absolute';
2572 imdl.inv_solve_core.prior_data = 1;
2573 imdl.inv_solve_core.elem_prior = 'conductivity';
2574 imdl.inv_solve_core.elem_working = 'log_conductivity';
2575 imdl.inv_solve_core.meas_working = 'apparent_resistivity';
2576 imdl.inv_solve_core.calc_solution_error = 0;
2577 imdl.inv_solve_core.verbose = 0;
2578 
2579 imgp = map_img(imgi, 'log10_conductivity');
2580 
2581 
2582 
2583 
2584 
2585 img1= inv_solve(imdl, vi);
2586 
2587 disp('TEST: previous solved at default verbosity');
2588 disp('TEST: now solve same at verbosity=0 --> should be silent');
2589 imdl.inv_solve_core.verbose = 0;
2590 
2591 img2= inv_solve(imdl, vi);
2592 
2593 imdl.inv_solve_core.elem_output = 'log10_resistivity'; 
2594 img3= inv_solve(imdl, vi);
2595 
2596 disp('TEST: try coarse2fine mapping with background');
2597 ctrs= interp_mesh(imdl.rec_model);
2598 x= ctrs(:,1); y= ctrs(:,2);
2599 r=sqrt((x+5).^2 + (y+5).^2);
2600 imdl.rec_model.elems(x-y > 300, :) = [];
2601 c2f = mk_approx_c2f(imdl.fwd_model,imdl.rec_model);
2602 imdl.fwd_model.coarse2fine = c2f;
2603 
2604 
2605 imdl.inv_solve_core.elem_output = 'conductivity';
2606 img4= inv_solve(imdl, vi);
2607 
2608 clf; subplot(221); h=show_fem(imgp,1); set(h,'EdgeColor','none'); axis tight; title('synthetic data, logC');
2609    subplot(222); show_fem(img1,1); axis tight; title('#1 verbosity=default');
2610    subplot(223); show_fem(img2,1); axis tight; title('#2 verbosity=0');
2611    subplot(223); show_fem(img3,1); axis tight; title('#3 c2f + log10 resistivity out');
2612    subplot(224); show_fem(img4,1); axis tight; title('#4 c2f cut');
2613    drawnow;
2614 d1 = img1.elem_data; d2 = img2.elem_data; d3 = img3.elem_data; d4 = img4.elem_data;
2615 unit_test_cmp('img1 == img2', d1, d2, eps);
2616 unit_test_cmp('img1 == img3', d1, 1./(10.^d3), eps);
2617 unit_test_cmp('img1 == img4', (d1(x-y <=300)-d4)./d4, 0, 0.05); 
2618 
2619 
2620 
2621 
2622 function do_unit_test_rec_mv(solver)
2623 disp('TEST: conductivity and movement --> baseline conductivity only');
2624 
2625 
2626 
2627 
2628 
2629 imdl= mk_common_model('c2t4',16); 
2630 ne = length(imdl.fwd_model.electrode);
2631 nt = length(imdl.fwd_model.elems);
2632 imdl.solve = solver;
2633 imdl.reconst_type = 'absolute';
2634 
2635 imdl.inv_solve_core.meas_input   = 'voltage';
2636 imdl.inv_solve_core.meas_working = 'apparent_resistivity';
2637 imdl.inv_solve_core.elem_prior   = {   'conductivity'   };
2638 imdl.inv_solve_core.prior_data   = {        1           };
2639 imdl.inv_solve_core.elem_working = {'log_conductivity'};
2640 imdl.inv_solve_core.elem_output  = {'log10_conductivity'};
2641 imdl.inv_solve_core.calc_solution_error = 0;
2642 imdl.inv_solve_core.verbose = 0;
2643 imdl.hyperparameter.value = 0.1;
2644 
2645 
2646 imgsrc= mk_image( imdl.fwd_model, 1);
2647 vh=fwd_solve(imgsrc);
2648 
2649 ctrs= interp_mesh(imdl.fwd_model);
2650 x= ctrs(:,1); y= ctrs(:,2);
2651 r1=sqrt((x+5).^2 + (y+5).^2); r2 = sqrt((x-45).^2 + (y-35).^2);
2652 imgsrc.elem_data(r1<50)= 0.05;
2653 imgsrc.elem_data(r2<30)= 100;
2654 
2655 
2656 vi=fwd_solve( imgsrc );
2657 
2658 
2659 noise_level= std(vi.meas - vh.meas)/10^(30/20);
2660 vi.meas = vi.meas + noise_level*randn(size(vi.meas));
2661 
2662 
2663 hh=clf; subplot(221); imgp = map_img(imgsrc, 'log10_conductivity'); show_fem(imgp,1); axis tight; title('synth baseline, logC');
2664 
2665 
2666 img0= inv_solve(imdl, vi);
2667 figure(hh); subplot(222);
2668  img0 = map_img(img0, 'log10_conductivity');
2669  show_fem(img0, 1); axis tight;
2670 
2671 disp('TEST: conductivity + movement');
2672 imdl.fwd_model = rmfield(imdl.fwd_model, 'jacobian');
2673 
2674 imdl.inv_solve_core.elem_prior   = {   'conductivity'   , 'movement'};
2675 imdl.inv_solve_core.prior_data   = {        1           ,     0     };
2676 imdl.inv_solve_core.RtR_prior    = {     @eidors_default, @prior_movement_only};
2677 imdl.inv_solve_core.elem_len     = {       nt           ,   ne*2    };
2678 imdl.inv_solve_core.elem_working = {  'log_conductivity', 'movement'};
2679 imdl.inv_solve_core.elem_output  = {'log10_conductivity', 'movement'};
2680 imdl.inv_solve_core.jacobian     = { @jacobian_adjoint  , @jacobian_movement_only};
2681 imdl.inv_solve_core.hyperparameter = {   [1 1.1 0.9]    ,  sqrt(2e-3)     }; 
2682 imdl.inv_solve_core.verbose = 0;
2683 
2684 
2685 nn = [imgsrc.fwd_model.electrode(:).nodes];
2686 elec_orig = imgsrc.fwd_model.nodes(nn,:);
2687 
2688 nn = imgsrc.fwd_model.nodes;
2689 imgsrc.fwd_model.nodes = nn * [1-0.02 0; 0 1+0.02]; 
2690 
2691 nn = [imgsrc.fwd_model.electrode(:).nodes];
2692 elec_mv = imgsrc.fwd_model.nodes(nn,:);
2693 
2694 
2695 vi=fwd_solve( imgsrc );
2696 
2697 
2698 noise_level= std(vi.meas - vh.meas)/10^(30/20);
2699 
2700 
2701 
2702 nn = [imgsrc.fwd_model.electrode(1:4).nodes];
2703 figure(hh); subplot(223); imgp = map_img(imgsrc, 'log10_conductivity'); show_fem_move(imgp,elec_mv-elec_orig,10,1); axis tight; title('synth mvmt, logC');
2704 
2705 
2706 img1= inv_solve(imdl, vi);
2707 figure(hh); subplot(224);
2708  imgm = map_img(img1, 'movement');
2709  img1 = map_img(img1, 'log10_conductivity');
2710  show_fem_move(img1,reshape(imgm.elem_data,16,2), 10, 1); axis tight;
2711 
2712 d0 = img0.elem_data; d1 = img1.elem_data;
2713 unit_test_cmp('img0 == img1 + mvmt', d0-d1, 0, 0.40);
2714 
2715 
2716 function Jm = jacobian_movement_only (fwd_model, img);
2717   pp = fwd_model_parameters(img.fwd_model);
2718   szJm = pp.n_elec * pp.n_dims; 
2719   img = map_img(img, 'conductivity'); 
2720   Jcm = jacobian_movement(fwd_model, img);
2721   Jm = Jcm(:,(end-szJm+1):end);
2722 
2723 
2724 
2725 
2726 
2727 
2728 
2729 
2730 function RtR = prior_movement_only(imdl);
2731   imdl.image_prior.parameters(1) = 1; 
2732   pp = fwd_model_parameters(imdl.fwd_model);
2733   szPm = pp.n_elec * pp.n_dims; 
2734   RtR = prior_movement(imdl);
2735   RtR = RtR((end-szPm+1):end,(end-szPm+1):end);
2736 
2737 function do_unit_test_rec2(solver)
2738 disp('TEST: reconstruct a discontinuity');
2739 [imdl, vh] = unit_test_imdl();
2740 imgi = mk_image(imdl, 1);
2741 ctrs = interp_mesh(imdl.fwd_model);
2742 x = ctrs(:,1); y = ctrs(:,2);
2743 imgi.elem_data(x-y>100)= 1/10;
2744 vi = fwd_solve(imgi); vi = vi.meas;
2745 clf; show_fem(imgi,1);
2746 
2747 imdl.solve = solver;
2748 imdl.hyperparameter.value = 1e2; 
2749 
2750 imdl.reconst_type = 'absolute';
2751 imdl.inv_solve_core.elem_working = 'log_conductivity';
2752 imdl.inv_solve_core.elem_output = 'log10_resistivity';
2753 imdl.inv_solve_core.meas_working = 'apparent_resistivity';
2754 imdl.inv_solve_core.dtol_iter = 4; 
2755 imdl.inv_solve_core.max_iterations = 20; 
2756 imdl.inv_solve_core.calc_solution_error = 0;
2757 imdl.inv_solve_core.verbose = 10;
2758 imgr= inv_solve_core(imdl, vi);
2759 
2760 clf; show_fem(imgr,1);
2761 img = mk_image( imdl );
2762 img.elem_data= 1./(10.^imgr.elem_data);
2763 
2764 
2765 
2766 
2767 
2768 
2769 
2770 
2771 
2772 
2773 
2774 function do_unit_test_img0_bg()
2775    stim = mk_stim_patterns(32,1, ...
2776    [0,5],[0,5],{'meas_current'});
2777    extra = {'ball',['solid ball = ' ...
2778    'sphere(0.4,0,0.4; 0.1);']};
2779    fmdl = ng_mk_cyl_models([1,1,0.1],...
2780    [16,0.3,0.7],0.05,extra);
2781    [~,fmdl] = elec_rearrange([16,2],...
2782    'square',fmdl);
2783    fmdl.stimulation = stim;
2784    img= mk_image(fmdl,1);
2785    vh=fwd_solve(img);
2786    img.elem_data(fmdl.mat_idx{2}) = 2;
2787    vi=fwd_solve(img);
2788    cmdl = ng_mk_cyl_models([1,1,0.2],[],[]);
2789    fmdl.coarse2fine = ...
2790    mk_coarse_fine_mapping(fmdl,cmdl);
2791    imdl1= select_imdl(fmdl,{'Basic GN dif'});
2792    imdl1.rec_model = cmdl;
2793    imdl1.hyperparameter.value = .0001;
2794    imdl1.inv_solve_gn.max_iterations = 1;
2795    imdl1.solve = @inv_solve_gn;
2796    
2797    imgr1 = inv_solve(imdl1, vh, vi);
2798    
2799    
2800    
2801    
2802    
2803 
2804 
2805 
2806 function do_unit_test_diffseq(solver)
2807    lvl = eidors_msg('log_level',1);
2808    imdl = mk_common_model('f2C',16);
2809    imdl.solve = solver;
2810    imdl.hyperparameter.value = 1e-2; 
2811 
2812    imgh = mk_image(imdl,1);
2813    xyr = [];
2814    for deg = [0:5:360]
2815       R = [sind(deg) cosd(deg); cosd(deg) -sind(deg)]; 
2816       xyr(:,end+1) = [R*[0.5 0.5]'; 0.2];
2817    end
2818    [vh, vi, ~, imgm] = simulate_movement(imgh, xyr);
2819    disp('*** alert: inverse crime underway ***');
2820    disp(' - no noise has been added to this data');
2821    disp(' - reconstructing to the same FEM discretization as simulated data');
2822    disp('***');
2823    vv.meas = [];
2824    vv.time = nan;
2825    vv.name = 'asfd';
2826    vv.type = 'data';
2827    n_frames = size(vi,2);
2828    for ii = 1:7
2829       switch ii
2830          case 1
2831             fprintf('\n\nvh:numeric, vi:numeric data\n');
2832             vhi = vh;
2833             vii = vi;
2834          case 2
2835             fprintf('\n\nvh:struct, vi:numeric data\n');
2836             vhi = vv; vhi.meas = vh;
2837             vii = vi;
2838          case 3
2839             fprintf('\n\nvh:numeric, vi:struct data\n');
2840             vhi = vh;
2841             clear vii;
2842             for jj=1:n_frames;
2843                vii(jj) = vv; vii(jj).meas = vi(:,jj);
2844             end
2845          case 4
2846             fprintf('\n\nvh:struct, vi:struct data\n');
2847             vhi = vv; vhi.meas = vh;
2848             clear vii;
2849             for jj=1:n_frames;
2850                vii(jj) = vv; vii(jj).meas = vi(:,jj);
2851             end
2852          case 5
2853             fprintf('method = Cholesky\n');
2854             imdl.inv_solve_core.update_method = 'cholesky';
2855          case 6
2856             fprintf('method = PCG\n');
2857             imdl.inv_solve_core.update_method = 'pcg';
2858          otherwise
2859             fprintf('method = default\n');
2860             imdl.inv_solve_core = rmfield(imdl.inv_solve_core,'update_method');
2861       end
2862       img= inv_solve(imdl, vhi, vii);
2863       for i=1:size(imgm,2)
2864          clf;
2865          subplot(211); imgi=imgh; imgi.elem_data = imgm(:,i); show_fem(imgi); title(sprintf('fwd#%d',i));
2866          subplot(212); imgi=imgh; imgi.elem_data = img.elem_data(:,i); show_fem(imgi); title('reconst');
2867          drawnow;
2868          err(i) = norm(imgm(:,i)/max(imgm(:,i)) - img.elem_data(:,i)/max(img.elem_data(:,i)));
2869          fprintf('image#%d: reconstruction error = %g\n',i,err(i));
2870       end
2871       for i=1:size(imgm,2)
2872          unit_test_cmp( sprintf('diff seq image#%d',i), ...
2873             imgm(:,i)/max(imgm(:,i)), ...
2874             img.elem_data(:,i)/max(img.elem_data(:,i)), ...
2875             0.90 );
2876       end
2877    end
2878    eidors_msg('log_level',lvl);
2879 
2880 
2881 function do_unit_test_absseq(solver)
2882    lvl = eidors_msg('log_level',1);
2883    imdl = mk_common_model('f2C',16);
2884    imdl.reconst_type = 'absolute';
2885    imdl.solve = solver;
2886    imdl.hyperparameter.value = 1e-2; 
2887 
2888    imgh = mk_image(imdl,1);
2889    xyr = [];
2890    for deg = [0:5:360]
2891       R = [sind(deg) cosd(deg); cosd(deg) -sind(deg)]; 
2892       xyr(:,end+1) = [R*[0.5 0.5]'; 0.2];
2893    end
2894    [~, vi, ~, imgm] = simulate_movement(imgh, xyr);
2895    imgm = imgm + 1; 
2896    disp('*** alert: inverse crime underway ***');
2897    disp(' - no noise has been added to this data');
2898    disp(' - reconstructing to the same FEM discretization as simulated data');
2899    disp('***');
2900    vv.meas = [];
2901    vv.time = nan;
2902    vv.name = 'asfd';
2903    vv.type = 'data';
2904    n_frames = size(vi,2);
2905    for ii = 1:5
2906       switch ii
2907          case 1
2908             fprintf('\n\nvi:numeric data\n');
2909             vii = vi;
2910          case 2
2911             fprintf('\n\nvi:struct data\n');
2912             clear vii;
2913             for jj=1:n_frames;
2914                vii(jj) = vv; vii(jj).meas = vi(:,jj);
2915             end
2916          case 3
2917             fprintf('method = Cholesky\n');
2918             imdl.inv_solve_core.update_method = 'cholesky';
2919          case 4
2920             fprintf('method = PCG\n');
2921             imdl.inv_solve_core.update_method = 'pcg';
2922          otherwise
2923             fprintf('method = default\n');
2924             imdl.inv_solve_core = rmfield(imdl.inv_solve_core,'update_method');
2925       end
2926       img= inv_solve(imdl, vii);
2927       for i=1:size(imgm,2)
2928          clf;
2929          subplot(211); imgi=imgh; imgi.elem_data = imgm(:,i); show_fem(imgi); title(sprintf('fwd#%d',i));
2930          subplot(212); imgi=imgh; imgi.elem_data = img.elem_data(:,i); show_fem(imgi); title('reconst');
2931          drawnow;
2932          err(i) = norm(imgm(:,i)/max(imgm(:,i)) - img.elem_data(:,i)/max(img.elem_data(:,i)));
2933          fprintf('image#%d: reconstruction error = %g\n',i,err(i));
2934       end
2935       for i=1:size(imgm,2)
2936          unit_test_cmp( sprintf('abs seq image#%d',i), ...
2937             imgm(:,i)/max(imgm(:,i)), ...
2938             img.elem_data(:,i)/max(img.elem_data(:,i)), ...
2939             0.46 );
2940       end
2941    end
2942    eidors_msg('log_level',lvl);