function test_failed = test_uwpfbt(verbose)
%TEST_WFBTPR
%
% Checks perfect reconstruction of the general wavelet transform of different
% filters
%
disp('========= TEST UWPFBT ============');
global LTFAT_TEST_TYPE;
tolerance = 1e-8;
if strcmpi(LTFAT_TEST_TYPE,'single')
   tolerance = 2e-6;
end

test_failed = 0;
if(nargin>0)
   verbose = 1;
else
   verbose = 0;
end

type = {'dec'};
ext = {'per'};

J = 4;
%! Mild tree
wt1 = wfbtinit({'db10',6,'full'});
wt1 = wfbtremove(1,1,wt1,'force');
wt1 = wfbtremove(2,1,wt1,'force');

%! Hardcore tree
wt2 = wfbtinit({'db3',1});
wt2 = wfbtput(1,1,'mband1',wt2);
wt2 = wfbtput(2,2,'mband1',wt2);
wt2 = wfbtput(3,3,'mband1',wt2);
wt2 = wfbtput(3,1,'db10',wt2);
wt2 = wfbtput(4,1,'dgrid2',wt2);
wt2 = wfbtput(5,1,'db2',wt2);

% wt2 = wfbtinit();
% wt2 = wfbtput(0,0,{'db',4},wt2);
% wt2 = wfbtput(1,0,{'algmband',1},wt2);
% wt2 = wfbtput(1,1,{'hden',3},wt2);
% wt2 = wfbtput(2,0,{'dgrid',2},wt2);
% wt2 = wfbtput(2,1,{'dgrid',2},wt2);

test_filters = {

               {'algmband1',J} % 3 filters, uniform, crit. sub.
               {'algmband2',J} % 4 filters, uniform, crit. sub.
               {'db10',J}
               %{{'hden',3},J} % 3 filters, non-uniform, no crit. sub. no correct
               {'dgrid1',J} % 4 filters. sub. fac. 2
               wt1
               wt2
               };

scaling = {'scale','sqrt','noscale'};
scalingInv = scaling(end:-1:1);

interscaling = {'intscale','intsqrt','intnoscale'};
interscalingInv = interscaling(end:-1:1);

for scIdx = 1:numel(scaling)
for iscIdx = 1:numel(interscaling)
%testLen = 4*2^7-1;%(2^J-1);
testLen = 53;
f = tester_rand(testLen,1);

for extIdx=1:length(ext)
   extCur = ext{extIdx};

   for typeIdx=1:length(type)
     for tt=1:length(test_filters)
        actFilt = test_filters{tt};
         if verbose, if(~isstruct(actFilt))fprintf('J=%d, filt=%s, ext=%s, inLen=%d \n',actFilt{2},actFilt{1},extCur,length(f)); else disp('Custom'); end; end;

        [c,info] = uwpfbt(f,actFilt,scaling{scIdx},interscaling{iscIdx});
        fhat = iuwpfbt(c,actFilt,scalingInv{scIdx},interscalingInv{iscIdx});

            err = norm(f-fhat,'fro');
            [test_failed,fail]=ltfatdiditfail(err,test_failed,tolerance);
            if(~verbose)
              if(~isstruct(actFilt))
                  fprintf('J=%d, %5.5s, ext=%4.4s, %s, %s, L=%d, err=%.4e %s \n',actFilt{2},actFilt{1},extCur,scaling{scIdx},interscaling{iscIdx},size(f,1),err,fail);
              else
                  fprintf('Custom, %s, %s, err=%.4e %s\n',scaling{scIdx},interscaling{iscIdx},err,fail);
              end;
            end
            if strcmpi(fail,'FAILED')
               if verbose
                 if(~isstruct(actFilt)) fprintf('err=%d, filt=%s, ext=%s, inLen=%d \n',err,actFilt{1},extCur,testLen);else disp('Fail. Custom'); end;
                 figure(1);clf;stem([f,fhat]);
                 figure(2);clf;stem([f-fhat]);
                 break;
               end
            end

            fhat2 = iuwpfbt(c,info);
            err = norm(f-fhat2,'fro');
            [test_failed,fail]=ltfatdiditfail(err,test_failed,tolerance);

            if(~isstruct(actFilt))
                  fprintf('INFO J=%d, %5.5s, ext=%4.4s, %s, %s, L=%d, err=%.4e %s \n',actFilt{2},actFilt{1},extCur,scaling{scIdx},interscaling{iscIdx},size(f,1),err,fail);
            else
                  fprintf('INFO Custom, %s, %s, err=%.4e %s\n',scaling{scIdx},interscaling{iscIdx},err,fail);
            end;

            if test_failed && verbose, break; end;

     end
     if test_failed && verbose, break; end;
   end
   if test_failed && verbose, break; end;
end
end
end


function plotci2()
    %  plot a circle containing ni events
    %  around each grid point
    % turned into function by Celso G Reyes 2017

    ZG=ZmapGlobal.Data; % used by get_zmap_globals

    report_this_filefun();

    st = 2;
    [X,Y] = meshgrid(gx,gy);
    [m,n]= size(r);
    set(gca,'NextPlot','add')
    x = -pi-0.1:0.1:pi;
    for i = 1:st:m
        for k = 1:st:n
            if r(i,k) <= ZG.tresh_km
                plot(X(i,k)+r(i,k)*sin(x)/(cosd(ya0)*111),Y(i,k)+r(i,k)*cos(x)/(cosd(ya0)*111) ,'k')
                plot(X(i,k),Y(i,k),'+k')
            end
        end
    end

end


function im_scale = prep_im_for_blob_size(im_size, target_size, max_size)

    im_size_min = min(im_size(1:2));
    im_size_max = max(im_size(1:2));
    im_scale = double(target_size) / im_size_min;

    % Prevent the biggest axis from being more than MAX_SIZE
    if round(im_scale * im_size_max) > max_size
        im_scale = double(max_size) / double(im_size_max);
    end
end


% make a x-section plus topography...

report_this_filefun(mfilename('fullpath'));

% make a track
lis1 = linspace(lat1,lat2,1000);
lis2 = linspace(lon1,lon2,1000);

tr = [lis2 ; lis1]; tr = tr';
z = [];
% get the topo at each point

for i = 1:length(tr)
    x = find(abs(vlon - tr(i,1)) == min(abs(vlon - tr(i,1))) );
    y = find(abs(vlat - tr(i,2)) == min(abs(vlat - tr(i,2))) );
    z = [z tmap(y,x)  ];
end

figure
axes('pos',[0.1 0.4 0.75 0.3])
plot(xsecx,-xsecy,'or');
hold on

axes('pos',[0.1 0.7 0.75 0.15])
pl = plot(z,'k'); hold on
l = z >= 0; l2 = find(z >= 0);
pl = plot(l2, z(l),'ks');
set(pl,'Markersize',6,'markerfacecolor','k')
l = z < 0; l2 = find(z < 0);
pl = plot(l2, z(l),'bs');
set(pl,'Markersize',6,'markerfacecolor','b')

set(pl,'Linewidth',2)
set(gca,'XTick',[])
grid

axes('pos',[0.1 0.1 0.75 0.3])
pcolor(gx,gy,re3);


function [Torr] = inH2O2Torr(inH2O)
% Convert pressure from inches of water column at 4 degrees to torr
% Chad Greene 2012
Torr = inH2O*1.86832;


function [ a, b ] = p06_lim ( dim_num )

%*****************************************************************************80
%
%% P06_LIM returns the integration limits for problem 06.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    19 March 2007
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer DIM_NUM, the dimension of the argument.
%
%    Output, real A(DIM_NUM), B(DIM_NUM), the lower and upper
%    limits of integration.
%
  a(1:dim_num) = 0.0;
  b(1:dim_num) = 1.0;

  return
end


clear classes

W = diag([0.1, 1*pi/180].^2);
P0 = diag([0.005, 0.005, 0.001].^2);
V = diag([0.005, 0.5*pi/180].^2);

randinit
map = Map(20, 10);
veh = Vehicle(V);
RandomPath(veh, map.dim);
sensor = RangeBearingSensor(veh, map, W);
sensor.interval = 5;
ekf = EKF(veh, [], P0, sensor, W, []);
ekf.verbose = true;

randinit
ekf.run(1000);

f1
clf
map.visualize()
veh.plot_xy('b');
ekf.plot_map();
grid on
xyzlabel


%   TTeMPS Toolbox.
%   Michael Steinlechner, 2013-2016
%   Questions and contact: michael.steinlechner@epfl.ch
%   BSD 2-clause license, see LICENSE.txt

function [X, residual, cost, times] = amen_fast( L, F, X, opts )

t_start = tic();
% set default opts
if ~exist( 'opts', 'var');        opts = struct();     end
if ~isfield( opts, 'nSweeps');    opts.nSweeps = 4;    end
if ~isfield( opts, 'maxrank');    opts.maxrank = 20;   end
if ~isfield( opts, 'maxrankRes'); opts.maxrankRes = 4; end
if ~isfield( opts, 'tolRes');     opts.tolRes = 1e-13;  end
if ~isfield( opts, 'tol');        opts.tol = 1e-13;     end
if ~isfield( opts, 'solver');     opts.solver = 'direct';   end
if ~isfield( opts, 'prec');       opts.prec = true;   end

d = X.order;
n = X.size;

normF = norm(F);
cost = cost_function( L, X, F );
residual = norm( apply(L, X) - F ) / normF;
times = toc(t_start);

for sweep = 1:opts.nSweeps
    X = orthogonalize(X, 1);
    for mu = 1:d-1
        disp( ['Current core: ', num2str(mu)] )

        % STEP 1: Solve mu-th core opimization
        F_mu = contract( X, F, mu );
        sz = [X.rank(mu), X.size(mu), X.rank(mu+1)];

        if strcmpi( opts.solver, 'direct' )
            % if system very small
            L_mu = contract( L, X, mu );
            U_mu = L_mu \ F_mu(:);
            X.U{mu} = reshape( U_mu, sz );
        elseif strcmpi( opts.solver, 'pcg' )
            [left, right] = Afun_prepare( L, X, mu );
            [B2, V, E] =  prepare_precond( L.L0, X, mu );

            U_mu = pcg( @(y) Afun( L, y, mu, sz, left, right), ...
                     F_mu(:), ...
                     1e-10, 1000, ...
                     @(y) apply_precond( B2, V, E, y, sz ), [],...
                     X.U{mu}(:) );
            X.U{mu} = reshape( U_mu, sz );
        else
            error( 'Unknown opts.solver type. Use either ''direct'' (default) or ''pcg''.' )
        end

        % STEP 2: Calculate current residual and cost function
        res =  F - apply(L, X);
        residual = [residual; norm( res ) / normF];
        cost = [cost; cost_function( L, X, F )];
        disp(['Rel. residual: ' num2str(residual(end)) ', Current rank: ' num2str(X.rank) ]);

        % STEP 3: Augment mu-th and (mu+1)-th core with (truncated) residual
        R = contract( X, res, [mu, mu+1] );
        R_combined = unfold(R{1},'left') * unfold(R{2},'right');
        if opts.prec
            R_combined = precond_residual( L.L0, X, R_combined, mu );
        end
        [uu,ss,~] = svd( R_combined, 'econ');
        s = find( diag(ss) > opts.tolRes*norm(diag(ss)), 1, 'last' );
		if opts.maxrankRes ~= 0
			s = min( s, opts.maxrankRes );
		end
        R{1} = reshape( uu(:,1:s)*ss(1:s,1:s), [X.rank(mu), n(mu), s]);
        %R{2} = reshape( vv(:,1:s)', [s, n(mu+1), X.rank(mu+2)]);

        left = cat(3, X.U{mu}, R{1});
        %right = cat(1, X.U{mu+1}, R{2});

        % STEP 4: Move orthogonality to (mu+1)-th core while performing rank truncation
        [U,S,~] = svd( unfold(left,'left'), 'econ' );
        t = find( diag(S) > opts.tol*norm(diag(S)), 1, 'last' );
        t = min( t, opts.maxrank );
        X.U{mu} = reshape( U(:,1:t), [X.rank(mu), n(mu), t] );
        X.U{mu+1} = rand( t, n(mu+1), X.rank(mu+2));

        times = [times; toc(t_start)];
    end
    for mu = d:-1:2
        disp( ['Current core: ', num2str(mu)] )

        % STEP 1: Solve mu-th core opimization
        F_mu = contract( X, F, mu );
        sz = [X.rank(mu), X.size(mu), X.rank(mu+1)];

        if strcmpi( opts.solver, 'direct' )
            L_mu = contract( L, X, mu );
            U_mu = L_mu \ F_mu(:);
            X.U{mu} = reshape( U_mu, size(X.U{mu}) );
        elseif strcmpi( opts.solver, 'pcg' )
            [left, right] = Afun_prepare( L, X, mu );
            [B2, V, E] =  prepare_precond( L.L0, X, mu );

            U_mu = pcg( @(y) Afun( L, y, mu, sz, left, right), ...
                     F_mu(:), ...
                     1e-10, 1000, ...
                     @(y) apply_precond( B2, V, E, y, sz ), [],...
                     X.U{mu}(:) );
            X.U{mu} = reshape( U_mu, sz );
        else
            error( 'Unknown opts.solver type. Use either ''direct'' (default) or ''diag''.' )
        end

        % STEP 2: Calculate current residual and cost function
        res =  F - apply(L, X);
        residual = [residual; norm( res ) / normF];
        disp(['Rel. residual: ' num2str(residual(end)) ', Current rank: ' num2str(X.rank) ]);
        cost = [cost; cost_function( L, X, F )];

        % STEP 3: Augment mu-th and (mu+1)-th core with (truncated) residual
        R = contract( X, res, [mu-1, mu] );
        R_combined = unfold(R{1},'left') * unfold(R{2},'right');
        if opts.prec
            R_combined = precond_residual( L.L0, X, R_combined, mu-1 );
        end
        [~,ss,vv] = svd( R_combined, 'econ');
        s = find( diag(ss) > opts.tolRes*norm(diag(ss)), 1, 'last' );
		if opts.maxrankRes ~= 0
			s = min( s, opts.maxrankRes );
		end
        R{2} = reshape( ss(1:s,1:s)*vv(:,1:s)', [s, n(mu), X.rank(mu+1)]);

        right = cat(1, X.U{mu}, R{2});

        % STEP 4: Move orthogonality to (mu+1)-th core while performing rank truncation
        [~,S,V] = svd( unfold(right,'right'), 'econ' );
        t = find( diag(S) > opts.tol*norm(diag(S)), 1, 'last' );
        t = min( t, opts.maxrank );
        X.U{mu} = reshape( V(:,1:t)', [t, n(mu), X.rank(mu+1)] );
        X.U{mu-1} = rand( X.rank(mu-1), n(mu-1), t);

        times = [times; toc(t_start)];
    end

end

end

function res = cost_function( L, X, F )
res = 0.5*innerprod( X, apply(L, X) ) - innerprod( X, F );
end

function res = euclid_grad( L, X, F )
res = apply(L, X) - F;
end

function res = precond_residual( L0, X, R_combined, idx )
    n = size(L0, 1);
    rl = X.rank(idx);
    rr = X.rank(idx+2);

    B1 = zeros( rl );
    % calculate B1 part:
    for i = 1:idx-1
        % apply L to the i'th core
        tmp = X;
        tmp.U{i} = tensorprod_ttemps( tmp.U{i}, L0, 2 );
        B1 = B1 + innerprod( X, tmp, 'LR', idx-1);
    end

    % calculate B2 part:
    B2 = kron( L0, speye(n) ) + kron( speye(n), L0 );

    B3 = zeros( rr );
    % calculate B3 part:
    for i = idx+2:X.order
        tmp = X;
        tmp.U{i} = tensorprod_ttemps( tmp.U{i}, L0, 2 );
        B3 = B3 + innerprod( X, tmp, 'RL', idx+2);
    end

    [V,E] = eig( kron( eye(rr), B1 ) + kron( B3, eye(rl) ) );
    E = diag(E);

    R_combined = reshape( R_combined, [rl, n*n, rr] );
    rhs = matricize( R_combined, 2 ) * V;
    Y = zeros(size(rhs));
    for i=1:length(E)
        Y(:,i) = (B2 + E(i)*speye(n*n)) \ rhs(:,i);
    end
    res = tensorize( Y*V', 2, [rl, n*n, rr] );
    res = reshape( res, [rl*n, n*rr] );
end

function [left, right] = Afun_prepare( A, x, idx )
    y = A.apply(x);
    if idx == 1
        right = innerprod( x, y, 'RL', idx+1 );
        left = [];
    elseif idx == x.order
        left = innerprod( x, y, 'LR', idx-1 );
        right = [];
    else
        left = innerprod( x, y, 'LR', idx-1 );
        right = innerprod( x, y, 'RL', idx+1 );
    end
end

function res = Afun( A, U, idx, sz, left, right )

    V = reshape( U, sz );
    V = A.apply( V, idx );

    if idx == 1
        tmp = tensorprod_ttemps( V, right, 3 );
    elseif idx == A.order
        tmp = tensorprod_ttemps( V, left, 1 );
    else
        tmp = tensorprod_ttemps( V, right, 3);
        tmp = tensorprod_ttemps( tmp, left, 1);
    end

    res = tmp(:);
end
function [B2, V, E] = prepare_precond( L0, X, idx )
    n = size(L0, 1);
    rl = X.rank(idx);
    rr = X.rank(idx+1);

    B1 = zeros( rl );
    % calculate B1 part:
    for i = 1:idx-1
        % apply L to the i'th core
        tmp = X;
        tmp.U{i} = tensorprod_ttemps( tmp.U{i}, L0, 2 );
        B1 = B1 + innerprod( X, tmp, 'LR', idx-1);
    end

    % calculate B2 part:
    B2 = L0;

    B3 = zeros( rr );
    % calculate B3 part:
    for i = idx+1:X.order
        tmp = X;
        tmp.U{i} = tensorprod_ttemps( tmp.U{i}, L0, 2 );
        B3 = B3 + innerprod( X, tmp, 'RL', idx+1);
    end

    [V,E] = eig( kron( eye(rr), B1 ) + kron( B3, eye(rl) ) );
    E = diag(E);
end
function res = apply_precond( B2, V, E, rhs, sz )
    n = size(B2, 1);
    rhs = reshape( rhs, sz );
    rhs = matricize( rhs, 2 ) * V;
    Y = zeros(size(rhs));
    for i=1:length(E)
        Y(:,i) = (B2 + E(i)*speye(n)) \ rhs(:,i);
    end
    res = tensorize( Y*V', 2, sz );
    res = res(:);
end


function pass = test_determineDiscretization(pref)
% Test DETERMINEDISCRETIZATION method:

if ( nargin == 0 )
    pref = cheboppref();
end

%% Test the VALUES/COEFFS syntax:

% Test when values is passed with periodic boundary conditions:
dom = [0 2*pi];
N = chebop(@(x,u) diff(u,2) + cos(u), dom);
u0 = chebfun('0',dom);
x = chebfun('x',dom);
L = linearize(N, u0, x);
N.bc = 'periodic';
options = cheboppref();
options.discretization = 'values';
out = determineDiscretization(N, length(L.domain), options);
pass(1) = isequal(out.discretization, @trigcolloc);

% Test when coeffs is passed with periodic boundary conditions:
options.discretization = 'coeffs';
out = determineDiscretization(N, length(L.domain), options);
pass(2) = isequal(out.discretization, @trigspec);

% Test when values is passed with periodic boundary conditions and breakpoints:
dom = [0 pi 2*pi];
N = chebop(@(x,u) diff(u,2) + cos(u), dom);
u0 = chebfun('0',dom);
x = chebfun('x',dom);
L = linearize(N, u0, x);
options.discretization = 'values';
out = determineDiscretization(N, length(L.domain), options);
pass(3) = isequal(out.discretization, @chebcolloc2);

% Test when coeffs is passed with periodic boundary conditions and breakpoints:
options.discretization = 'coeffs';
out = determineDiscretization(N, length(L.domain), options);
pass(4) = isequal(out.discretization, @ultraS);

% Test when values is passed with dirichlet boundary conditions:
dom = [-1 1];
N = chebop(@(x,u) diff(u) + exp(u), dom);
u0 = chebfun('0',dom);
x = chebfun('x',dom);
L = linearize(N, u0, x);
N.bc = 'dirichlet';
options.discretization = 'values';
out = determineDiscretization(N, length(L.domain), options);
pass(5) = isequal(out.discretization, @chebcolloc2);

% Test when coeffs is passed with dirichlet boundary conditions:
options.discretization = 'coeffs';
out = determineDiscretization(N, length(L.domain), options);
pass(6) = isequal(out.discretization, @ultraS);

%% Test default:

% Default with dirichlet:
dom = [-1 1];
N = chebop(@(x,u) diff(u) + sin(u), dom);
u0 = chebfun('0',dom);
x = chebfun('x',dom);
L = linearize(N, u0, x);
N.bc = 'dirichlet';
out = determineDiscretization(N, length(L.domain), pref); % use pref
pass(7) = isequal(out.discretization, @chebcolloc2);

% Default with periodic:
N.bc = 'periodic';
out = determineDiscretization(N, length(L.domain), pref); % use pref
pass(8) = isequal(out.discretization, @trigcolloc);

%% Test CHEBCOLLOC1/ULTRAS:

options.discretization = @chebcolloc1;
out = determineDiscretization(N, length(L.domain), options);
pass(9) = isequal(out.discretization, @chebcolloc1);

options.discretization = @ultraS;
out = determineDiscretization(N, length(L.domain), options);
pass(10) = isequal(out.discretization, @ultraS);

%% Test a discontinuous RHS:

% Zero boundary condition at the left:
dom = [-1 1];
L = chebop(dom);
L.op = @(x,u) diff(u) + 2*u;
L.lbc = @(u) u - 0;
rhs = chebfun(@(x) abs(x), 'splitting', 'on');
lengthDom = max(length(L.domain),length(rhs.domain));
out = determineDiscretization(L, lengthDom, pref);
pass(11) = isequal(out.discretization, @chebcolloc2);

% Periodic boundary condition. The rhs is discontinuous so it should use
% CHEBCOLLOC2:
L.bc = 'periodic';
out = determineDiscretization(L, lengthDom, pref);
pass(12) = isequal(out.discretization, @chebcolloc2);

end


function [DI]=TriScatteredInterp_ND(DT,D,XI,InterpMethod)

% function [DI]=TriScatteredInterp_ND(DT,D,XI,InterpMethod)
% ------------------------------------------------------------------------
%
%
%
% ------------------------------------------------------------------------

%%

if ~isa(DT,'DelaunayTri') %if DT is not a delaunay tesselation
    DT=delaunayTriangulation(DT); %assuming DT are coordinates, replace by Delaunay tesselation
end

DI=nan(size(XI,1),size(D,2)); %Allocate DI
for q=1:size(D,2)% loop over dimensions
    switch InterpMethod
        case 'nat_near' %natural in chull, neirest outside chull
            [DI(:,q),~]=TriScatteredInterp_nat_near(DT,D(:,q),XI);
        otherwise %TriScatterInterp can handle other methods
            F = scatteredInterpolant(DT,D(:,q),InterpMethod); %Construct interpolator
            DI(:,q)=F(XI); %Interpolate
    end
end

%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function [MPa] = psi2MPa(psi)
% Convert units of pressure from pounds per square inch to megapascals.
% Chad Greene 2012
MPa = psi*0.00689476;


function im = visualize3Dpara(alpha, sincosTheta, tran, f, baseShape, edgeAdj, varargin)

% function im = visualize3Dpara(alpha, sincosTheta, tran, f, baseShape, varargin)
%
% Parameters:
%       alpha: nbasis x 1
%       sincosTheta: 6 x 1
%       tran: 2x1
%       f: 1
%       baseShape: 3 x np x nbasis
%       edgeAdj: nedge x 2
%
% Addition parameters:
%    h, w, lineWidth, addNode, circSize
%
% Example:
%   stickStruct = getStickFigure('class', 'chair');
%   im = visualize3Dpara(alpha, sincosTheta, tran, f, stickStruct.baseShape{1}, stickStruct.edgeAdj{1});

para.h = 320;
para.w = 240;
para.lineWidth = 6;
para.addNode = true;
para.circSize = 8;
para = propval(varargin, para);

theta = sctheta2theta(sincosTheta);
x = alpha2x_proj(tran,alpha,theta,f,baseShape, 'w', para.w, 'h',para.h);
im = renderImage(x,edgeAdj,'addNoise',false,'noiseLevel',0,'h',para.h,'w',para.w,'lineWidth',6,'parallel',false,...
    'addNode',para.addNode, 'circSize', para.circSize);

end


function oHDEVArray = hDevCalculation(fracFreq, sPeriod, tau)
%function for doing ovelapping Hadamard calculations for an array tau
%values. input arguments include array of fractional frequency readings,
%sPeriod of readings for overlap factor, and array of tau values

 phaseError = calculatePhaseError((1/sPeriod),fracFreq); %turn freq readings to frac freq values

 tCount = numel(tau); %get the size of the tau array
 oHDEVArray = zeros(1,tCount); %allocate array for Allan Dev values

 %add a wait bar so user knows status
j = 1/tCount;
h = waitbar(0,'Performing HDEV calculations...','CreateCancelBtn','setappdata(gcbf,''canceling'',1)');
 setappdata(h,'canceling',0)
 %loop through tau values and calculate HDEV at each value
 for i = 1:tCount
     oHDEVArray(i) = calculateHDEV(tau(i),sPeriod,phaseError);
     if getappdata(h,'canceling')
        oHDEVArray = [];
        oHDEVArray = -42881;
        delete(h);
        return;
    end
     waitbar((i*j),h,'Performing HDEV calculations...');
 end

 waitbar(1.0,h,'HDEV calculations done');
 delete(h);


%STATSDTC Stats Decision tree Classifier (Matlab Stats Toolbox)
%
%   W = STATSDTC(A,'PARAM1',val1,'PARAM2',val2,...)
%   W = A*STATSDTC([],'PARAM1',val1,'PARAM2',val2,...)
%   D = B*W
%
% INPUT
%   A          Dataset used for training
%   PARAM1     Optional parameter, see CLASSIFICATIONTREE.FIT
%   B          Dataset used for evaluation
%
% OUTPUT
%   W          Decision tree classifier
%   D          Classification matrix, dataset with posteriors
%
% DESCRIPTION
% This is the PRTools interface to the CLASSIFICATIONTREE of the Matlab
% Stats toolbox. See there for more information. It is assumed that objects
% labels, feature labels and class priors are included in the dataset A.
%
% The decision tree is stored in W and can be retrieved by T = +W or by
% T = getdata(W). The Stats toolbox command VIEW can be used to visualize
% it, either in the command window (default) or graphically setting the
% 'mode' options to 'graph'.
%
% SEE ALSO (PRTools Guide)
% DATASETS, MAPPINGS, DTC, TREEC, CLASSIFICATIONTREE, VIEW

% Copyright: R.P.W. Duin, r.p.w.duin@37steps.com

function W = statsdtc(varargin)

  name = 'Stats DecTree';

  if mapping_task(varargin,'definition')
    W = define_mapping(varargin,[],name);
  elseif mapping_task(varargin,'training')
    A       = varargin{1};
    data    = +A;
    labels  = getlabels(A);
    prior   = getprior(A);
    featlab = getfeatlab(A);
    if ischar(featlab)
      featlab = cellstr(featlab);
    end
    tree    = ClassificationTree.fit(data,labels,'prior',prior, ...
    'PredictorNames',featlab,varargin{2:end});
    W = trained_mapping(A,tree);
  else % evaluation
    [A,W]    = deal(varargin{:});
    tree     = getdata(W);
    [dummy,post] = predict(tree,+A);
    W        = setdat(A,post,W);
  end

return


% Outputs:  (1) r: steady state interest rate
%           (2) w: steady state wage
%           (3) K: capital stock
%           (4) A: matrix for computing value function
%           (5) u: utility over grid
%           (5) c: consumption over grid
%           (6) V: value function over grid
%			(7) g: distribution
%           (8) dV_Upwind: derivative of value function by upwind scheme
%           (9) dVf: derivative of value function by forward difference
%           (10) dVb: derivative of value function by backward difference
%           (11) If: indicator for forward drift in savings
%           (12) Ib: indicator for backward drift in savings
%           (13) I0: indicator for no drift in savings

function [r,w,K,A,u,c,V,g,dV_Upwind,dVf,dVb,If,Ib,I0] = compute_steady_state()

%----------------------------------------------------------------
% Housekeeping
%----------------------------------------------------------------

% Declare global variables
global ggamma rrho ddelta aalpha ssigmaTFP rrhoTFP z lla mmu ttau I amin amax a da aa ...
	zz Aswitch rmin rmax r0 maxit crit Delta Ir crit_S zAvg A

% Initialze variables for iteration
dVf = zeros(I,2);
dvB = zeros(I,2);
c = zeros(I,2);
KS = zeros(Ir,1);
r = r0;
KD = (((aalpha) / (r + ddelta)) ^ (1 / (1 - aalpha))) * zAvg;
w = (1 - aalpha) * (KD ^ aalpha) * ((zAvg) ^ (-aalpha));
v0(:,1) = (w*mmu*(1-z(1)) + r.*a).^(1-ggamma)/(1-ggamma)/rrho;
v0(:,2) = (w*(1-ttau)*z(2) + r.*a).^(1-ggamma)/(1-ggamma)/rrho;

%----------------------------------------------------------------
% Iterate to find steady state interest rate
%----------------------------------------------------------------

for ir=1:Ir

    r_r(ir)=r;
    rmin_r(ir)=rmin;
    rmax_r(ir)=rmax;

    KD(ir,1) = (((aalpha) / (r + ddelta)) ^ (1 / (1 - aalpha))) * zAvg;
	w = (1 - aalpha) * (KD(ir) ^ aalpha) * ((zAvg) ^ (-aalpha));

    if ir>1

        v0 = V_r(:,:,ir-1);

    end

    v = v0;

    %%%%
    % Solve for value function given r
    %%%%

    for n=1:maxit

        V = v;
        V_n(:,:,n)=V;

        % Compute forward difference
        dVf(1:I-1,:) = (V(2:I,:)-V(1:I-1,:))/da;
        dVf(I,:) = (w*((1 - ttau) * z + mmu * (1 - z)) + r.*amax).^(-ggamma); %will never be used, but impose state constraint a<=amax just in case

        % Compute backward difference
        dVb(2:I,:) = (V(2:I,:)-V(1:I-1,:))/da;
        dVb(1,:) = (w*((1 - ttau) * z + mmu * (1 - z)) + r.*amin).^(-ggamma); %state constraint boundary condition

        % Compute consumption and savings with forward difference
        cf = dVf.^(-1/ggamma);
        ssf = w*((1 - ttau) * zz + mmu * (1 - zz)) + r.*aa - cf;

        % Compute consumption and savings with backward difference
        cb = dVb.^(-1/ggamma);
        ssb = w*((1 - ttau) * zz + mmu * (1 - zz)) + r.*aa - cb;

        % Compute consumption and derivative of value function for no drift
        c0 = w*((1 - ttau) * zz + mmu * (1 - zz)) + r.*aa;
        dV0 = c0.^(-ggamma);

        % Compute upwind differences
        If = ssf > 0;       %positive drift --> forward difference
        Ib = ssb < 0;       %negative drift --> backward difference
        I0 = (1-If-Ib);     %no drift
        dV_Upwind = dVf.*If + dVb.*Ib + dV0.*I0;
        c = dV_Upwind.^(-1/ggamma);
        u = c.^(1-ggamma)/(1-ggamma);
        savingsSS = w*((1 - ttau) * zz + mmu * (1 - zz)) + r.*aa - c;

        % Construct matrix for updating implicit scheme
        X = -min(ssb,0)/da;
        Y = -max(ssf,0)/da + min(ssb,0)/da;
        Z = max(ssf,0)/da;

        A1=spdiags(Y(:,1),0,I,I)+spdiags(X(2:I,1),-1,I,I)+spdiags([0;Z(1:I-1,1)],1,I,I);
        A2=spdiags(Y(:,2),0,I,I)+spdiags(X(2:I,2),-1,I,I)+spdiags([0;Z(1:I-1,2)],1,I,I);
        A = [A1,sparse(I,I);sparse(I,I),A2] + Aswitch;

        B = (1/Delta + rrho)*speye(2*I) - A;

        u_stacked = [u(:,1);u(:,2)];
        V_stacked = [V(:,1);V(:,2)];
        b = u_stacked + V_stacked/Delta;

        % Solve system of equations for updating implicit scheme
        V_stacked = B\b;

        V = [V_stacked(1:I),V_stacked(I+1:2*I)];

        % Update value function and check convergence
        Vchange = V - v;
        v = V;

        dist(n) = max(max(abs(Vchange)));
        if dist(n)<crit

            %disp('Value Function Converged, Iteration = ')
            %disp(n)
            break

        end

    end

    %%%%
    % Solve for stationary distribution
    %%%%

    % Preallocate matrices for solving linear system
    AT = A';
    b = zeros(2*I,1);

    % Normalization so pdf integrates to 1
    i_fix = 1;
    b(i_fix)=.1;
    row = [zeros(1,i_fix-1),1,zeros(1,2*I-i_fix)];
    AT(i_fix,:) = row;

    %Solve linear system for distribution
    gg = AT\b;
    g_sum = gg'*ones(2*I,1)*da;
    gg = gg./g_sum;
    g = [gg(1:I),gg(I+1:2*I)];

    % Compute objects from this iteration
    g_r(:,:,ir) = g;
    adot(:,:,ir) = w * ((1 - ttau) * zz + mmu * (1 - zz))+ r.*aa - c;
    V_r(:,:,ir) = V;

    KS(ir,1) = g(:,1)'*a*da + g(:,2)'*a*da;
    S(ir,1) = KS(ir,1) - KD(ir,1);

    % Update interest rate
    if S(ir)>crit_S

        %disp('Excess Supply')
        rmax = r;
        r = 0.5*(r+rmin);

    elseif S(ir)<-crit_S;

        %disp('Excess Demand')
        rmin = r;
        r = 0.5*(r+rmax);

    elseif abs(S(ir))<crit_S;

        display('Steady State Found, Interest rate =')
        disp(r)
        break

    end

end

% Save steady state aggregate capital stock
K = KS(ir,1);
% investment
savings = w*((1 - ttau) * zz + mmu * (1 - zz)) + r.*aa - c;
invest = sum((reshape(savings,I*2,1) .* gg)'*da);


function [tx, categoryFeatures] = getBoundaryClassifierFeatures(bndinfo, X, ind)
% X is the raw data
% ind is the set of indices for which the features should be computed
%
% tx:
%   Edge features (1-6)
%        1:  Pb
%        2:  Length / Perimeter
%        3:  Smoothness
%        4:  Angle
%      5-6:  Continuity
%      7-8:  Convexity (area and ratio) - not used
%        9:  Chain length
%   Region features (7-17)+3
%    10-11:  Area
%       12:  Color Mean Difference
%       13:  Color Entropy Difference
%       14:  Gradient Entropy Difference - not used
%    15-16:  Position (x,y)
%    17-18:  Extent Overlap (x, y)
%    an additional 10 features of position/overlap
%   Geometry features (16-39)+3
%    19-28:  Geometric Context Mean
%    29-33:  Geometric Context Difference
%    34   :  Geometric Context Sum Abs Difference
%    35-36:  Geometric Context Most Likely Label (G V or S)
%    37-40:  Depth under- and over-estimates for each side
%    31-43:  Depth, min1-min2, max1-max2, min(max12) - max(min12)
%    44-47:  Depthcol, each sp, diff, abs diff

ng = 5; % five geometric classes

ndata = numel(ind);

[imh, imw] = size(bndinfo.wseg);

tx = zeros([ndata 60], 'single');

if isempty(ind)
    return;
end

spLR = bndinfo.edges.spLR;
s1 = spLR(ind, 1);
s2 = spLR(ind, 2);
wseg = bndinfo.wseg;

categoryFeatures = [];
f = 0;

%% Edge features
tx(:, f+1) = X.edge.pb(ind);

perim = zeros(bndinfo.nseg, 1);
for k = 1:numel(X.edge.length)
    perim(spLR(k, 1)) = perim(spLR(k, 1)) + X.edge.length(k);
    perim(spLR(k, 2)) = perim(spLR(k, 2)) + X.edge.length(k);
end
minperim = min([perim(s1) perim(s2)], [], 2);
tx(:, f+2) = X.edge.length(ind) ./ minperim; % edge length / perim of smaller region

% juncts = bndinfo.edges.junctions(ind, :);
% jpos1 = bndinfo.junctions.position(juncts(:, 1), :);
% jpos2 = bndinfo.junctions.position(juncts(:, 2), :);
% directLength = abs(jpos2(:, 1)-jpos1(:,1)) + abs(jpos2(:, 2)-jpos1(:,2));
tx(:, f+3) = X.edge.smoothness(ind); % measure of smoothess

theta = X.edge.theta;
% discrete angle
tx(:, f+4) = max(ceil((mod(theta(ind),2*pi) / (pi*2) * 16 - 1E-10)),1);
categoryFeatures(end+1) = f+4;

% relative angle (continuity)
%theta = mod([theta ; theta+pi]/pi*180, 360);
theta1 = mod(X.edge.thetaStart*180/pi, 360);
theta2 = mod(X.edge.thetaEnd*180/pi, 360);
maxc = zeros(ndata, 2);
eadj = bndinfo.edges.adjacency;
ne = bndinfo.ne;
for k = 1:ndata
    ki = ind(k);
    ra = abs(theta2(ki)-theta1(eadj{ki}));
    ra = ra - 180*(ra>180);
    if isempty(ra), maxc(k,1) = 0;
    else maxc(k,1) = min(ra);
    end
    ra = mod(abs(theta2(ne+ki)-theta1(eadj{ne+ki})), 180+1E-5);
    if isempty(ra), maxc(k,2) = 0;
    else maxc(k,2) = min(ra);
    end
end
tx(:, f+(5:6)) = [min(maxc, [], 2) max(maxc, [], 2)];

area1 = X.region.area(s1);
area2 = X.region.area(s2);
%tx(:, f+7) = X.edge.convArea(ind) ./ min([area1 area2], [], 2);
%tx(:, f+8) = 0; %X.edge.convRatio;

%ind2 = (X.edge.edge2chain(ind)>0);
%tx(ind2, f+9) = X.edge.chainsize(X.edge.edge2chain(ind(ind2)));
tx(:, f+9) = X.edge.edge2chain(ind);

f = f + 9;

%% Region features

% area

tx(:, f+(1:2)) = [min([area1 area2], [], 2) max([area1 area2], [], 2)];

% color
tx(:, f+3) = sqrt(sum((X.region.colorMean(s1, :)-X.region.colorMean(s2, :)).^2, 2));

ch = X.region.colorHist+1E-10;
for k = 1:ndata
    h1 = ch(s1(k), :);  e1 = sum(-log(h1).*h1);
    h2 = ch(s2(k), :);  e2 = sum(-log(h2).*h2);
    e12 = (area1(k)*e1 + area2(k)*e2)/(area1(k)+area2(k));
    h3 = (area1(k)*h1 + area2(k)*h2)/(area1(k)+area2(k));
    e3 = sum(-log(h3).*h3);
    tx(k, f+4) = e3-e12;
end

% gradient
% ch = X.region.gradHist+1E-10;
% for k = 1:ndata
%     h1 = ch(s1(k), :);  e1 = sum(-log(h1).*h1);
%     h2 = ch(s2(k), :);  e2 = sum(-log(h2).*h2);
%     e12 = (area1(k)*e1 + area2(k)*e2)/(area1(k)+area2(k));
%     h3 = (area1(k)*h1 + area2(k)*h2)/(area1(k)+area2(k));
%     e3 = sum(-log(h3).*h3);
%     tx(k, f+5) = e3-e12;
% end

% position
tx(:, f+6) = (X.region.y(s1, 3))-(X.region.y(s2, 3)); % difference of tops
tx(:, f+7) = (X.region.y(s1, 1))-(X.region.y(s2, 1)); % difference of bottoms
tx(:, f+8) = (X.region.y(s1, 3))-(X.region.y(s2, 1)); % top1 - bottom2
tx(:, f+9) = (X.region.y(s1, 1))-(X.region.y(s2, 3)); % bottom1 - top2
tx(:, f+10) = X.region.y(s1, 3) - X.region.y(s1, 1); % top1 - bottom1
tx(:, f+11) = X.region.y(s2, 3) - X.region.y(s2, 1); % top2 - bottom2
tx(:, f+12) = (X.region.x(s1, 1))-(X.region.x(s2, 1)); % left1 - left2
tx(:, f+13) = (X.region.x(s1, 3))-(X.region.x(s2, 3)); % right1 - right2
tx(:, f+14) = X.region.x(s1, 3) - X.region.x(s1, 1); % right1 - left1
tx(:, f+15) = X.region.x(s2, 3) - X.region.x(s2, 1); % right2 - left2

%tx(:, f+6) = (X.region.x(s1, 2)-X.region.x(s2, 2)) / imw;
%tx(:, f+7) = (X.region.y(s1, 2)-X.region.y(s2, 2)) / imh;

% x alignment
x1 = X.region.x(s1, [1 3]);
x2 = X.region.x(s2, [1 3]);
tx(:, f+16) = (min([x1(:, 2) x2(:, 2)], [], 2)-max([x1(:, 1) x2(:, 1)], [], 2)) ./ ...
    (max([x1(:, 2) x2(:, 2)], [], 2)-min([x1(:, 1) x2(:, 1)], [], 2));

% determine whether regions are x-aligned at boundary
jpos1 = ceil(bndinfo.junctions.position(bndinfo.edges.junctions(ind, 1), :));
jpos2 = ceil(bndinfo.junctions.position(bndinfo.edges.junctions(ind, 2), :));
if jpos1(:, 1)>jpos2(:, 1) % make jpos1 the left-most junction
    tmp = jpos2;
    jpos2 = jpos1;
    jpos1 = tmp;
end
tx(:, f+17) = (jpos2(:, 1)-jpos1(:, 1))/imw; % boundary width
jpos1(:, 1) = max(jpos1(:, 1)-3, 1);  % go a little to left of left junction
jpos2(:, 1) = min(jpos2(:, 1)+3, imw); % go a little to right or right junction
jpos1(:, 2) = min(jpos1(:, 2), imh);
jpos2(:, 2) = min(jpos2(:, 2), imh);
js1 = wseg((jpos1(:, 1)-1)*imh + jpos1(:, 2)); % region slightly to left
js2 = wseg((jpos2(:, 1)-1)*imh + jpos2(:, 2)); % region slightly to right
tx(:, f+18) = (js1~=s1) & (js1~=s2) & (js2~=s1) & (js2~=s2); % whether x-aligned

% y overlap
y1 = X.region.y(s1, [1 3]);
y2 = X.region.y(s2, [1 3]);
tx(:, f+19) = (min([y1(:, 2) y2(:, 2)], [], 2)-max([y1(:, 1) y2(:, 1)], [], 2)) ./ ...
    (max([y1(:, 2) y2(:, 2)], [], 2)-min([y1(:, 1) y2(:, 1)], [], 2));

f = f + 19;

%% 3D Geometry features

% geometric context features
gc = X.region.geomContext;

tx(:, f+(1:ng)) = gc(s1, :);
tx(:, f+ng+(1:ng)) = gc(s2, :);
tx(:, f+2*ng+(1:ng)) = tx(:, f+(1:ng))-tx(:, f+ng+(1:ng));
tx(:, f+3*ng+1) = sum(abs(tx(:, f+2*ng+(1:ng))), 2)/2;

[maxval, maxlab] = max([gc(:, 1) sum(gc(:, 2:4), 2) gc(:, 5)], [], 2);
tx(:, f+3*ng+2) = (maxlab(s1)-1)*3+ maxlab(s2);
categoryFeatures(end+1) = f+3*ng+2;

f = f + 17;

% relative depth
tx(:, f+(1:4)) = [X.edge.depthmin(ind, 1:2)  X.edge.depthmax(ind, 1:2)];
tx(:, f+5) = X.edge.depthmin(ind, 1)-X.edge.depthmin(ind,2);
tx(:, f+6) = X.edge.depthmax(ind, 1)-X.edge.depthmax(ind,2);
tx(:, f+7) = min(X.edge.depthmax(ind, :), [], 2)- max(X.edge.depthmin(ind, :), [], 2);

tx(:, f+(8:9)) = X.region.depthcol([s1 s2]);
tx(:, f+10) = tx(:, f+8)-tx(:, f+9);
tx(:, f+11) = abs(tx(:, f+10));

%% Geometric T-junctions
% Ground-Vertical-Ground junctions
% In future make this faster by checking chains to see when g/v transitions
% to v/v
gvs1 = zeros(bndinfo.ne, 1);  gvs2 = zeros(bndinfo.ne, 1);
gvs1(ind) = maxlab(s1);
gvs2(ind) = maxlab(s2);
is_gv = (gvs1==1 & gvs2==2) | (gvs1==2 & gvs2==1);
is_vv = (gvs1==2 & gvs2==2);
ejuncts = bndinfo.edges.junctions;
juncts = cell(bndinfo.nj, 1);
jsize = zeros(size(juncts));
for k = 1:size(ejuncts, 1)
    j1 = ejuncts(k, 1);  j2 = ejuncts(k, 2);
    juncts{j1}(end+1) = k;
    juncts{j2}(end+1) = k;
    jsize(j1) = jsize(j1) + 1;
    jsize(j2) = jsize(j2) + 1;
end
juncts = cell2mat(juncts(jsize==3));
isGeomJunction = sum(is_gv(juncts), 2)==2 & sum(is_vv(juncts), 2)==1;
juncts = juncts(isGeomJunction, :);
nGeomJuncts = zeros(bndinfo.nseg, 1);
e2chain = X.edge.edge2chain;
spLR = [bndinfo.edges.spLR ; bndinfo.edges.spLR(:, [2 1])];
for k = 1:size(juncts, 1) % check that g/v --> v/v angle is less than g/v -->
    gve = juncts(k, is_gv(juncts(k, :)));
    % if ground on left, then reverse
    vve = juncts(k, is_vv(juncts(k, :)));
    if maxlab(spLR(gve(1), 1))==1, gve(1) = gve(1)+ne;  end
    if maxlab(spLR(gve(2), 1))==1, gve(2) = gve(2)+ne;  end
    if any(e2chain(vve)==e2chain(gve)) && (e2chain(vve)>0) % foreground on left
        chainind = sort(X.edge.chains{e2chain(vve)});
        chainind1 = chainind(chainind<=ne);
        chainind2 = chainind(chainind>ne)-ne;
        tmpind = ismember_sorted(ind, chainind1);
        tx(tmpind, f+12) = 1;
        tmpind = ismember_sorted(ind, chainind2);
        tx(tmpind, f+12) = 2;
        nGeomJuncts(spLR(vve, 1)) = nGeomJuncts(spLR(vve, 1))+1;
    elseif any(e2chain(vve+ne)==e2chain(gve)) && (e2chain(vve+ne)>0) % foreground on right
        chainind = sort(X.edge.chains{e2chain(vve+ne)});
        chainind1 = chainind(chainind<=ne);
        chainind2 = chainind(chainind>ne)-ne;
        tmpind = ismember_sorted(ind, chainind1);
        tx(tmpind, f+12) = 1;
        tmpind = ismember_sorted(ind, chainind2);
        tx(tmpind, f+12) = 2;
        nGeomJuncts(spLR(vve, 2)) = nGeomJuncts(spLR(vve, 2))+1;
    end
end
categoryFeatures(end+1) = f+12;

% add feature if segment has two or more geometric T-junctions
tmpind = find(tx(:, f+12)>0);
for k = tmpind'
    if tx(k, f+12)==1 && nGeomJuncts(spLR(ind(k), 1))>1
        tx(k, f+13) = 1;
    elseif tx(k, f+12)==2 && nGeomJuncts(spLR(ind(k), 2))>1
        tx(k, f+13) = 2;
    end
end
categoryFeatures(end+1) = f+13;

% Sky-Vertical-Vertical junctions (same as above but substitute sky for
% ground)
% In future make this faster by checking chains to see when s/v transitions
% to v/v
is_gv = (gvs1==3 & gvs2==2) | (gvs1==2 & gvs2==3);
is_vv = (gvs1==2 & gvs2==2);
ejuncts = bndinfo.edges.junctions;
juncts = cell(bndinfo.nj, 1);
jsize = zeros(size(juncts));
for k = 1:size(ejuncts, 1)
    j1 = ejuncts(k, 1);  j2 = ejuncts(k, 2);
    juncts{j1}(end+1) = k;
    juncts{j2}(end+1) = k;
    jsize(j1) = jsize(j1) + 1;
    jsize(j2) = jsize(j2) + 1;
end
juncts = cell2mat(juncts(jsize==3));
isGeomJunction = sum(is_gv(juncts), 2)==2 & sum(is_vv(juncts), 2)==1;
juncts = juncts(isGeomJunction, :);
nGeomJuncts = zeros(bndinfo.nseg, 1);
e2chain = X.edge.edge2chain;
for k = 1:size(juncts, 1) % check that g/v --> v/v angle is less than g/v -->
    gve = juncts(k, is_gv(juncts(k, :)));
    % if ground on left, then reverse
    vve = juncts(k, is_vv(juncts(k, :)));
    if maxlab(spLR(gve(1), 1))==3, gve(1) = gve(1)+ne;  end
    if maxlab(spLR(gve(2), 1))==3, gve(2) = gve(2)+ne;  end
    if any(e2chain(vve)==e2chain(gve)) && (e2chain(vve)>0) % foreground on left
        chainind = sort(X.edge.chains{e2chain(vve)});
        chainind1 = chainind(chainind<=ne);
        chainind2 = chainind(chainind>ne)-ne;
        tmpind = ismember_sorted(ind, chainind1);
        tx(tmpind, f+14) = 1;
        tmpind = ismember_sorted(ind, chainind2);
        tx(tmpind, f+14) = 2;
        nGeomJuncts(spLR(vve, 1)) = nGeomJuncts(spLR(vve, 1))+1;
    elseif any(e2chain(vve+ne)==e2chain(gve)) && (e2chain(vve+ne)>0) % foreground on right
        chainind = sort(X.edge.chains{e2chain(vve+ne)});
        chainind1 = chainind(chainind<=ne);
        chainind2 = chainind(chainind>ne)-ne;
        tmpind = ismember_sorted(ind, chainind1);
        tx(tmpind, f+14) = 1;
        tmpind = ismember_sorted(ind, chainind2);
        tx(tmpind, f+14) = 2;
        nGeomJuncts(spLR(vve, 2)) = nGeomJuncts(spLR(vve, 2))+1;
    end
end
categoryFeatures(end+1) = f+14;

% add feature if segment has two or more geometric T-junctions
tmpind = find(tx(:, f+14)>0);
for k = tmpind'
    if tx(k, f+14)==1 && nGeomJuncts(spLR(ind(k), 1))>1
        tx(k, f+15) = 1;
    elseif tx(k, f+14)==2 && nGeomJuncts(spLR(ind(k), 2))>1
        tx(k, f+15) = 2;
    end
end
categoryFeatures(end+1) = f+15;

%% Object features
if isfield(X, 'objectFeatures')
    tx = [tx X.objectFeatures(ind, :)];
end


function [params] = pt_calc(params)
    % Calculation of the probabilistic forecast test.
    %
    % [params] = pt_calc(params)
    %
    % Input parameters:
    %   params.mCatalog           Earthquake catalog
    %   params.mPolygon           Polygon (defined by ex_selectgrid)
    %   params.vX                 X-vector (defined by ex_selectgrid)
    %   params.vY                 Y-vector (defined by ex_selectgrid)
    %   params.vUsedNodes         Used nodes vX * vY defining the mPolygon (defined by ex_selectgrid)
    %   params.bRandom            Perform random simulation (true) or real calculation (false)
    %   params.nCalculation       Number of random simulations
    %   params.bMap               Calculate a map (true) or a cross-section (false)
    %   params.bNumber            Use constant number (true) or constant radius (false)
    %   params.nNumberEvents      Number of earthquakes if bNumber is true
    %   params.fRadius            Radius of gridnode if bNumber is false
    %   params.nMinimumNumber     Minimum number of earthquakes per node for determining a b-value
    %   params.fForecastPeriod    Forecasting period in years
    %   params.bLearningPeriod    Use different learning period than the rest of the catalog
    %   params.fLearningPeriod    Learning period in years
    %   params.bSignificance      Calculate significance during random simulation
    %                             using params.fRealProbability
    %   params.fRealProbability   Probability of calculation with real data
    %   params.nCalculateMC       Method to calculate magnitude of completeness (see also: help calc_Mc)
    %   params.nTestMethod        Method to calculate the Kagan & Jackson test (see also: help kj_poissonian)
    %   params.bMinMagMc          Use magnitude of completeness as lower limit of magnitude range for testing (true)
    %                             Use params.fMinMag as lower limit (false)
    %   params.fMinMag            Lower limit of magnitude range for testing
    %   params.fMaxMag            Upper limit of magnitude range for testing
    %
    % Output parameters:
    %   Same as input parameters including
    %   params.mValueGrid         Matrix of calculated Kagan & Jackson test values
    %   params.vRandomMeans       Vector of means of probability differences per simulation run
    %   params.vSignificanceLevel Vector of significance levels per simulation run
    %   params.fBValueOverall     Calculated overall b-value
    %   params.fStdDevOverall     Calculated standard deviation
    %   params.fMcOverall         Calculated magnitude of completeness
    %
    % Danijel Schorlemmer
    % July 10, 2002

    report_this_filefun();

    % Perform the calculation
    % -----------------------
    % Compute the b-values for both models
    [mValueGridH, vcsGridNamesH] = pt_calcinput(params.mLearningCatalog, params.mObservedCatalog, params.mPolygon, params.rOptions(1));
    [mValueGridN, vcsGridNamesN] = pt_calcinput(params.mLearningCatalog, params.mObservedCatalog, params.mPolygon, params.rOptions(2));
    % Apply test moethod settings
    if params.nTestMethod == 2    % Use overall b-value for test hypothesis if it's impossible to compute a b-value
        vSel = isnan(mValueGridH(:,1));
        mValueGridH(vSel,:) = mValueGridN(vSel,:);
    end
    % Init result matrix
    mValueGrid_ = [];
    % Loop over all grid nodes
    for nNode_ = 1:length(params.mPolygon(:,1))
        % Create node catalogs
        mLearningNodeCatalog_ = params.mLearningCatalog(params.rOptions(3).caLearningNodeIndices{nNode_}, :);
        mObservedNodeCatalog_ = params.mObservedCatalog(params.rOptions(3).caObservedNodeIndices{nNode_}, :);
        % Define magnitude range for testing
        fMinMag_ = max([mValueGridH(nNode_,3) mValueGridN(nNode_,3)]);
        if ~(params.bMinMagMc)
            fMinMag_ = max(params.fMinMag, fMinMag_);
        end
        % Calculate the probability-ratio
        [fDeltaProbability, fProbabilityN, fProbabilityH, vPredictionFMD, vObservedFMD, vMagnitudeBins] = pt_poissonian(mLearningNodeCatalog_, params.fLearningPeriodUsed, ...
            mObservedNodeCatalog_, params.fObservedPeriodUsed, params.rOptions(3).nMinimumNumber, mValueGridH(nNode_,1), mValueGridN(nNode_,1), ...
            mValueGridH(nNode_,3), mValueGridN(nNode_,3), fMinMag_, params.fMaxMag);
        if (((params.bRandomNode) | (params.bSaveRates)) & (~isnan(fProbabilityH)))
            nLen_ = length(vObservedFMD(:,1));
            vXMin_ = ones(nLen_, 1) * params.mPolygon(nNode_,1) - (params.rOptions(3).fSizeRectX/2);
            vXMax_ = ones(nLen_, 1) * params.mPolygon(nNode_,1) + (params.rOptions(3).fSizeRectX/2);
            vYMin_ = ones(nLen_, 1) * params.mPolygon(nNode_,2) - (params.rOptions(3).fSizeRectY/2);
            vYMax_ = ones(nLen_, 1) * params.mPolygon(nNode_,2) + (params.rOptions(3).fSizeRectY/2);
            vZMin_ = zeros(nLen_, 1);
            vZMax_ = zeros(nLen_, 1);
            vWeight_ = ones(nLen_, 1);
            vMagMin_ = vMagnitudeBins - 0.05;
            vMagMax_ = vMagnitudeBins + 0.05;
            vRatesH = [vXMin_ vXMax_ vYMin_ vYMax_ vZMin_ vZMax_ vMagMin_ vMagMax_ vPredictionFMD(:,1) vWeight_ vObservedFMD];
            vRatesN = [vXMin_ vXMax_ vYMin_ vYMax_ vZMin_ vZMax_ vMagMin_ vMagMax_ vPredictionFMD(:,2) vWeight_ vObservedFMD];
            if params.bSaveRates
                params.vRatesH = [params.vRatesH; vRatesH];
                params.vRatesN = [params.vRatesN; vRatesN];
            end
            if params.bRandomNode
                [rRelmTest] = relm_RTest4(vRatesH, vRatesN, params.nNumberCalculationNode, vMagnitudeBins(1), 1, 1, 0);
                fAlpha = rRelmTest.fAlpha;
                fBeta = rRelmTest.fBeta;
            else
                fAlpha = nan;
                fBeta = nan;
            end
        else
            fAlpha = nan;
            fBeta = nan;
        end
        mValueGrid_= [mValueGrid_; fDeltaProbability fProbabilityN fProbabilityH ...
            fAlpha fBeta];
    end % for nNode
    params.vcsGridNames = cellstr(char('Probability difference', ...
        'Null hypothesis', 'Test hypothesis', 'Alpha', 'Beta'));
    % Add the b-values of the hypothesis to the overall value grid
    params.mValueGrid = [mValueGrid_ mValueGridH mValueGridN];
    params.vcsGridNames = [params.vcsGridNames; vcsGridNamesH; vcsGridNamesN];
end


function PopDec=rGA(fhandle,Problem)
% real-parameter genetic algorithm to find minimal objective value

%--------------------------------------------------------------------------
% This function is written by Youwei He (email: 1554748356@qq.com)

% the number of design variables
D = Problem.D;
% population size of GA
GAPopulationSize = 10*D;
% Generation of GA
GAGeneration = 100;
obj_max=Inf;
% the first GA generation, randomly generated
Offspring = repmat(Problem.upper-Problem.lower,GAPopulationSize,1).*...
            UniformPoint(GAPopulationSize,D,'Latin')+repmat(Problem.lower,GAPopulationSize,1);
% the GA process for optimizing the objective function
for gen = 1 :  GAGeneration
    obj_Offspring = feval(fhandle, Offspring);
    [~,index] = sort(obj_Offspring,'ascend');
    if obj_Offspring(index(1)) < obj_max
        Best = Offspring(index(1),:);
        obj_max   = obj_Offspring(index(1));
    end
    Parent    = Offspring(index(1:ceil(GAPopulationSize/2)),:);
    Offspring = [OperatorGA(Problem,Parent(TournamentSelection(2,size(Parent,1), ...
        obj_Offspring(index(1:ceil(GAPopulationSize/2)))),:));OperatorGA(Problem,Parent,{0.9,2,1/D,20})];
end
PopDec = Best;
end


function [ Y_hat, W_hat] = MCWNNM_ADMM2_Estimation( NL_mat, Sigma_arr, CurPat, Par )

Y_hat = zeros(size(CurPat));
W_hat    = zeros(size(CurPat));
for  i      =  1 : length(Par.SelfIndex) % For each keypatch group
    Y    =   CurPat(:, NL_mat(1:Par.nlsp,i)); % Non-local similar patches to the keypatch
    mY  =   repmat(mean( Y, 2 ),1,Par.nlsp);
    Y    =   Y-mY;
    X 	=   MCWNNM_ADMM2( Y, Sigma_arr(:, Par.SelfIndex(i)), Par); % WNNM Estimation
    Y_hat(:,NL_mat(1:Par.nlsp,i))  = Y_hat(:,NL_mat(1:Par.nlsp,i))+X+mY;
    W_hat(:,NL_mat(1:Par.nlsp,i))     = W_hat(:,NL_mat(1:Par.nlsp,i))+ones(Par.ps2ch, Par.nlsp);
end
end


function [Res] = rma2(P, Vec, Mat, Pth)

% rma2 replaces all descriptors from Mat except the ones in the given descriptor vector in the given position Pth and returns
%      and returns the vector with lower S_err, using multiple linear regression analysis.
%      (The initial descriptor is changed even if it has the lower S_err).
%
%	   Input:
%             P             Property vector
%             Vec          Descriptor vector
%             Mat           Descriptor matrix
%             Pth           Path to follow
%
%     Returns:
%
%             Res           Vector containing in the first place the
%                           S_err, and afterwards the corresponding descriptor vector
%
%
%
% Andrew G. Mercader
% INIFTA, La Plata, Argentina
% Created: 30 Jan 2007

if (nargin < 4)
   error('the function requires at least 4 input variables. Type ''help rma2''.');
end

[k, n_m] = size(Mat);

Num=[1:n_m];
Num(Vec)=[];

[k,n_n]=size(Num);

Smin=10000;   % A very big number compared to a normal S necessary just to start the program
for j=1:n_n;
Vec(Pth)=Num(j);
Ser=rms(P,Vec,Mat);
    if (Ser<Smin)
         Smin=Ser;
        desc=Num(j);
    end
end
Vec(Pth)=desc;
Res=[Smin, Vec];
%End of rma2


%function [W]=myCTwriteVMC(inputCT,Xnew, Ynew, Znew);
function [W]=myCTwriteVMC(inputCT,scaleX, scaleY, scaleZ, str);

% Create CT input file *.ct for VMC++ engine from CERR MatLab CT matrix
% scaleX - X voxel size in cm
% scaleY - Y voxel size in cm
% scaleZ - Z voxel size in cm
% numSlice - number of slices in CT matrix
% In this code CERR CT data converted to density from HF (water has 1024).
% CT coordinate system is following that real (Xmin,Ymin,Zmin) is (0,0,0) in all cases here
% Output - *.ct file and dimension
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

CT=inputCT;
W=size(CT);

%[newfile,newpath] = uiputfile('*.ct','Enter CT file name for VMC++ input ');
%str=strcat(newpath,newfile);

fid=fopen(str,'w');

count=fwrite(fid,W(1),'int32');
count=fwrite(fid,W(2),'int32');
count=fwrite(fid,W(3),'int32');

%X=[0 Xnew];
%Y=[0 Ynew];
%Z=[0 Znew];

X(1)=0;
for i=1:W(1),
    X(i+1)=scaleX*i;
end
Y(1)=0;
for i=1:W(2),
    Y(i+1)=scaleY*i;
end
Z(1)=0;
for i=1:W(3),
    Z(i+1)=scaleZ*i;
end

count=fwrite(fid,X,'float32');
count=fwrite(fid,Y,'float32');
count=fwrite(fid,Z,'float32');
scale=1e3;
%h = waitbar(0,'Please wait for CT file writing');
density_max=4;
for k=1:W(3),
    temp=double(CT(:,:,k))/1024; % round up to 2 decimal places
    temp(temp>density_max)=density_max-1e-6; % cut values greater than 3.
    count=fwrite(fid,temp,'float32');
%    waitbar(k/W(3));
end

%close(h);
fclose(fid);

numSlice=W(3);


function [mMcBdepth] = calc_McBwdepth(mCatalog, fBinning, nEvents, nOverlap, nBootSample,nMinNumberevents )
% function [mMcBdepth] = calc_McBwdepth(mCatalog, fBinning, nEvents, nOverlap, nBootSample,nMinNumberevents)
% ------------------------------------------------------------------------
% Calculate Mc and b_value with depth; Mc is EMR-medianMc determined by bootstrapping
%
% Incoming variables:
% mCatalog : Earthquake catalog
% fBinnig  : Binning interval
% nEvents  : Number of events per window
% nOverlap : Number of events that overlap in windows
% nBootSample : Number of bootstrap samples
% nMinNumberevents : Minimum number of events
%
% Outgoing variables:
% mMcBdepth(:,1) : Depth
% mMcBdepth(:,2) : EMR Median Mc
% mMcBdepth(:,3) : 16-percentile
% mMcBdepth(:,4) : 84-percentile
% mMcBdepth(:,5) : b-value
% mMcBdepth(:,6) : Standard deviation of b (Shi & Bolt, 1982)
% mMcBdepth(:,7) :a-value
%
% Author: J. Woessner
% last update: 04.04.03

% Initialze
mMcBdepth = [];

% Set fix values
fMinMag = min(mCatalog(:,6));
fMaxMag = max(mCatalog(:,6));
fMinDepth = min(mCatalog(:,7));
fMaxDepth = max(mCatalog(:,7));

% Sorting by depth
[vSortDepth,vIndiceSort] = sort(mCatalog(:,7));
mCatDep = mCatalog(vIndiceSort(:,1),:);
for nStep = 1:nEvents/nOverlap:length(mCatDep(:,6))-nEvents
    mCat = mCatDep(nStep:nStep+nEvents,:);
    % Determine Mc by bootstrapping
    [vMc, vMls, fMc_org, fStdMc_org, fSkew, vPerc, fMedianMc, fMeanMc, fStdMc, v1Sigma] = calc_BstMc(mCat,fBinning,nBootSample);
    % Mean depth
    fMeanDepth = (mCatDep(nStep,7)+mCatDep(round(nStep+nEvents),7))/2;
    % B-value determination
    % Select magnitude range to calculate b-value for EMR median Mc
    vSel = mCatDep(:,6) >= fMedianMc-0.05;
    mCat2 = mCatDep(vSel,:);
    % Check for minimum number of events
    if length(mCat(:,1)) > nMinNumberevents
        try
            [fMeanMag, fBValue, fStdDev, fAValue] =  calc_bmemag(mCat2, fBinning);
            vBvalue = [fBValue fStdDev fAValue];
        catch
            vBvalue = [NaN NaN NaN];
        end
    else
        vBvalue = [NaN NaN NaN];
    end; % END of IF
    % Result matrix
    mMcBdepth = [mMcBdepth; fMeanDepth fMedianMc v1Sigma vBvalue];
end; % End of FOR nStep


%PREX_DATAFILE  PRTools example of the datafile usage

delfigs
echo on

prdatafiles
            % makes sure that datafiles are available
						% will download them from the PRTools website when needed

a = highway
            % load datafile
            % 100 observations of 5 images and a label image
						% R,G,B
						% and two pixel features (comparisons with previous frames)

b = gendat(a,0.06)
            % random selection of 6 observations

figure; show(b); drawnow

c = selectim(b,[1 2 3])
            % select RGB

figure; show(c); drawnow
showfigs

x = b(2,:)
           % select one observation
figure; show(x,3); drawnow
showfigs

y = data2im(x);
           % select features by retrieving images

data = squeeze(y(:,:,1:5)); % data
lab  = squeeze(y(:,:,6));   % labels
datasize = size(data);

z = im2feat(data);  % store images as feature, pixels become objects
z = setlabels(z,lab(:));
z = setobjsize(z,datasize(1:2));
            % stores pixels as objects with 5 features

trainset = gendat(z,[1000,1000]);
w = qdc(trainset) % train classifier on trainset only
d = z*w*classc;   % classify entire image
figure; show(z*w*classc);
figure; imagesc(d*classim);
figure; imagesc(lab);
showfigs

echo off


function y=rfft(x,n,d)
%RFFT     Calculate the DFT of real data Y=(X,N,D)
% Data is truncated/padded to length N if specified.
%   N even:	(N+2)/2 points are returned with
% 			the first and last being real
%   N odd:	(N+1)/2 points are returned with the
% 			first being real
% In all cases fix(1+N/2) points are returned
% D is the dimension along which to do the DFT

s=size(x);
if prod(s)==1
    y=x
else
    if nargin <3 || isempty(d)
        d=find(s>1);
        d=d(1);
        if nargin<2
            n=s(d);
        end
    end
    if isempty(n)
        n=s(d);
    end
    y=fft(x,n,d);
    y=reshape(y,prod(s(1:d-1)),n,prod(s(d+1:end)));
    s(d)=1+fix(n/2);
    y(:,s(d)+1:end,:)=[];
    y=reshape(y,s);
end


function P = cpf_p(parameterization, step, z, V, lam, Vprv, lamprv, pv, pq)
%CPF_P Computes the value of the CPF parameterization function.
%   P = CPF_P(PARAMETERIZATION, STEP, Z, V, LAM, VPRV, LAMPRV, PV, PQ)
%
%   Computes the value of the parameterization function at the current
%   solution point.
%
%   Inputs:
%       PARAMETERIZATION : Value of cpf.parameterization option
%       STEP : continuation step size
%       Z : normalized tangent prediction vector from previous step
%       V : complex bus voltage vector at current solution
%       LAM : scalar lambda value at current solution
%       VPRV : complex bus voltage vector at previous solution
%       LAMPRV : scalar lambda value at previous solution
%       PV : vector of indices of PV buses
%       PQ : vector of indices of PQ buses
%
%   Outputs:
%       P : value of the parameterization function at the current point
%
%   See also CPF_PREDICTOR, CPF_CORRECTOR.

%   MATPOWER
%   Copyright (c) 1996-2016, Power Systems Engineering Research Center (PSERC)
%   by Shrirang Abhyankar, Argonne National Laboratory
%   and Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

%% evaluate P(x0, lambda0)
if parameterization == 1        %% natural
    if lam >= lamprv
        P = lam - lamprv - step;
    else
        P = lamprv - lam - step;
    end
elseif parameterization == 2    %% arc length
    Va = angle(V);
    Vm = abs(V);
    Vaprv = angle(Vprv);
    Vmprv = abs(Vprv);
    P = sum(([Va([pv; pq]); Vm(pq); lam] - [Vaprv([pv; pq]); Vmprv(pq); lamprv]).^2) - step^2;
elseif parameterization == 3    %% pseudo arc length
    nb = length(V);
    Va = angle(V);
    Vm = abs(V);
    Vaprv = angle(Vprv);
    Vmprv = abs(Vprv);
    P = z([pv; pq; nb+pq; 2*nb+1])' * ...
        ( [Va([pv; pq]); Vm(pq); lam] - [Vaprv([pv; pq]); Vmprv(pq); lamprv] )...
        - step;
end


%%*************************************************************************
%% symqmr: symmetric QMR with left (symmetric) preconditioner.
%%         The preconditioner used is based on the analytical
%%         expression of inv(A).
%%
%% [x,resnrm,solve_ok] = symqmr(A,b,L,tol,maxit)
%%
%% child function: linsysolvefun.m
%%
%% A = [mat11 mat12; mat12' mat22].
%% b = rhs vector.
%% if matfct_options = 'chol' or 'spchol'
%%    L = Cholesky factorization of (1,1) block.
%%    M = Cholesky factorization of
%%        Schur complement of A ( = mat12'*inv(mat11)*mat12-mat22).
%% else
%%    L = triangular factors of A.
%%    M = not relevant.
%% end
%% resnrm = norm of qmr-generated residual vector b-Ax.
%%
%% SDPT3: version 3.1
%% Copyright (c) 1997 by
%% K.C. Toh, M.J. Todd, R.H. Tutuncu
%% Last Modified: 16 Sep 2004
%%*************************************************************************

   function  [xx,resnrm,solve_ok] = symqmr(A,b,L,tol,maxit,printlevel)

   N = length(b);
   if (nargin < 6); printlevel = 1; end
   if (nargin < 5) | isempty(maxit); maxit = max(30,length(A.mat22)); end;
   if (nargin < 4) | isempty(tol); tol = 1e-10; end;
   tolb = min(1e-4,tol*norm(b));

   solve_ok = 1;
   x = zeros(N,1);
   if (norm(x))
      if isstruct(A); Aq = matvec(A,x); else; Aq=A*x; end;
      r = b-Aq;
   else
      r = b;
   end
   err = norm(r); resnrm(1) = err; minres = err; xx = x;
   if (err < 1e-3*tolb); return; end

   q = precond(A,L,r);
   tau_old   = norm(q);
   rho_old   = r'*q;
   theta_old = 0;
   d = zeros(N,1);
   res = r; Ad = zeros(N,1);
%%
%% main loop
%%
   tiny = 1e-30;
   for iter = 1:maxit

       if isstruct(A); Aq = matvec(A,q); else; Aq=A*q; end;
       sigma = q'*Aq;
       if (abs(sigma) < tiny)
          solve_ok = 2;
          if (printlevel); fprintf('*'); end;
          break;
       else
          alpha = rho_old/sigma;
          r = r - alpha*Aq;
       end
       u = precond(A,L,r);

       theta = norm(u)/tau_old; c = 1/sqrt(1+theta^2);
       tau = tau_old*theta*c;
       gam = (c^2*theta_old^2); eta = (c^2*alpha);
       d = gam*d + eta*q;
       x = x + d;
%%
       Ad = gam*Ad + eta*Aq;
       res = res - Ad;
       err = norm(res); resnrm(iter+1) = err;
       if (err < minres); xx = x; minres = err; end
       if (err < tolb); break; end
       if (iter > 10)
          if (err > 0.98*mean(resnrm(iter-10:iter)))
             solve_ok = 0.5; break;
          end
       end
%%
       if (abs(rho_old) < tiny)
          solve_ok = 2;
          if (printlevel); fprintf('*'); end;
          break;
       else
          rho  = r'*u;
          beta = rho/rho_old;
          q = u + beta*q;
       end
       rho_old = rho;
       tau_old = tau;
       theta_old = theta;
   end
   if (iter == maxit); solve_ok = 0.3; end;
%%
%%*************************************************************************
%% precond:
%%*************************************************************************

   function Mx = precond(A,L,x)

   m = length(L.perm); m2 = length(x)-m;
   Mx = zeros(length(x),1);

   for iter = 1:1
      if norm(Mx); r = full(x - matvec(A,Mx)); else; r = full(x); end
      r1 = r(1:m);
      if (m2 > 0)
         r2 = r(m+[1:m2]);
         w = linsysolvefun(L,r1);
         z = mexMatvec(A.mat12,w,1) - r2;
         z = L.Mu \ (L.Ml \ (L.Mp*z));
         r1 = r1 - mexMatvec(A.mat12,z);
      end
      d = linsysolvefun(L,r1);
      if (m2 > 0)
         d = [d; z];
      end
      Mx = Mx + d;
   end
%%*************************************************************************
%% matvec: matrix-vector multiply.
%% matrix = [A.mat11 A.mat12; A.mat12' A.mat22]
%%*************************************************************************

   function Ax = matvec(A,x);

   m = length(A.mat11); m2 = length(x)-m;

   if issparse(x); x = full(x); end
   if (m2 > 0)
      x1 = x(1:m);
   else
      x1 = x;
   end
   Ax = mexMatvec(A.mat11,x1);
   if (m2 > 0)
      x2 = x(m+[1:m2]);
      Ax = Ax + mexMatvec(A.mat12,x2);
      Ax2 = mexMatvec(A.mat12,x1,1) + mexMatvec(A.mat22,x2);
      Ax = [full(Ax); full(Ax2)];
   end
%%*************************************************************************


classdef mlcnn
% Convolutional Multilayer Neural Network
%------------------------------------------------------------------------------
% Initialize, train, and test a multilayer convolutional neural network.
%
% Supports multiple activation functions including linear, sigmoid, tanh, and
% soft rectification (softplus).
%
% Also supports mean squared error (mse), binary (xent), and multi-class
% (mcxent) cross-entropy cost functions.
%
% Supports early stopping regularization.
%------------------------------------------------------------------------------
% DES
% stan_s_bury@berkeley.edu

properties
	class = 'mlcnn';
	nLayers;				% # OF UNIT LAYERS
	layers ;				% LAYER STRUCTS

	netOut = [];			% CURRENT OUTPUT OF THE NETWORK
	netError;				% CURRENT NETWORK OUTPUT ERROR

	costFun = 'mse';		% COST FUNCTION
	J = [];					% CURRENT COST FUNCTION VALUE

	nEpoch = 10;			% TOTAL NUMBER OF TRAINING EPOCHS
	epoch = 1;				% CURRENT EPOCH

	batchSize = 20;			% SIZE OF MINIBATCHES
	trainBatches = [];		% BATCH INDICES
	xValBatches = [];		% XVAL. DATA INDICES

	trainCost = [];			% TRAINING ERROR HISTORY (ALL DATA)
	xValCost = [];			% XVALIDATION EROR HISTORY
	bestxValCost = Inf		% TRACK BEST NETWORK ERROR

	nXVal = 0;				% PORTION OF DATA HELD OUT FOR XVALIDATION
	stopEarly = 5;			% # OF EARLY STOP EPOCHS (XVAL ERROR INCREASES)
	stopEarlyCnt = 0;		% EARLY STOPPING CRITERION COUNTER
	bestNet = [];			% STORAGE FOR BEST NETWORK

	denoise = 0;			% PROPORTION OF VISIBLE UNIT DROPOUT (SIGMOID ONLY)
	dropout = 'not used';	% (POTENTIAL) PROP. OF HID. UNIT DROPOUT (SIGMOID ONLY)

	wPenalty = 0;			% WEIGHT DECAY TERM
	beginWeightDecay=1;
	momentum = 'not used';	% (POTENTIAL) MOMENTUM

	saveEvery = realmax;	% SAVE PROGRESS EVERY # OF EPOCHS
	saveDir;
	visFun;					% VISUALIZATION FUNCTION HANDLE
	trainTime = Inf;		% TRAINING DURATION
	verbose = 500;			% DISPLAY THIS # OF WEIGHT UPDATES
	auxVars = [];			% AUXILIARY VARIABLES (VISUALIZATION, ETC)
	useGPU = 0;				% USE THE GPU, IF AVAILABLE
	gpuDevice = [];			% STORAGE FOR GPU DEVICE
	checkGradients = 0;		% NUMERICAL GRADIENT CHECKING
end

methods
	function self = mlcnn(arch)
	% net = mlnn(arch)
	%--------------------------------------------------------------------------
	%mlnn constructor method. Initilizes a mlnn object, <net> given a user-
	%provided architecture, <arch>.
	%--------------------------------------------------------------------------
		self = self.init(arch);
	end

	function print(self)
		properties(self)
		methods(self)
	end

	function self = train(self, data, targets)
	%net = train(data, targets)
	%--------------------------------------------------------------------------
	% Train a convolutional neural net using stochastic gradient descent.
	% INPUT:
	%     <data>:  - [#PixelsY x #PixelsX x #Channels x #Obs];
	%
	%  <targets>:  - [#Output x #Obs]
	%--------------------------------------------------------------------------

		% DISTRIBUTE VALUES TO THE GPU
		if self.useGPU
			self = gpuDistribute(self);
		end

		self = self.makeBatches(data);
		self.trainCost = zeros(self.nEpoch,1);
		self.xValCost = zeros(self.nEpoch,1);
		nBatches = numel(self.trainBatches);
		tic; cnt = 1;

		if self.checkGradients,	checkNNGradients(self,data,targets); end

		% MAIN
	    while 1
			if self.verbose, self.printProgress('epoch'); end
			batchCost = zeros(nBatches,1);
			wPenalty = self.wPenalty;

			if self.epoch >= self.beginWeightDecay
				self.wPenalty = wPenalty;
			else
				self.wPenalty = 0;
			end

			for iB = 1:nBatches
				% GET BATCH DATA
				batchIdx = self.trainBatches{iB};
				netInput = data(:,:,:,batchIdx);
				netTargets = (targets(:,batchIdx));

				% ADD NOISE TO INPUT (DENOISING CAE)
				if self.denoise > 0
				    netInput = netInput.*(rand(size(netInput))>self.denoise);
				end

				% BACKPROP MAIN
				self = self.fProp(netInput, netTargets);
				self = self.bProp;
				self = self.updateParams;

				% ASSESS BATCH COST
				batchCost(iB) = self.J;
				cnt = cnt + 1;
			end

	        % AVERAGE COST OVER ALL TRAINING POINTS
			self.trainCost(self.epoch) = mean(batchCost);

	        % CROSS-VALIDATION
			if ~isempty(self.xValBatches)
				self = self.crossValidate(data,targets);
				self = self.assessNet;
				if self.verbose, self.printProgress('xValCost');end
			end

			% EARLY STOPPING
			if (self.epoch >= self.nEpoch) || ...
				(self.stopEarlyCnt >= self.stopEarly),
				self.trainCost = self.trainCost(1:self.epoch);
				self.xValCost = self.xValCost(1:self.epoch);
				break;
			end

			% SAVE BEST NETWORK
			if ~mod(self.epoch,self.saveEvery) & ~isempty(self.saveDir)
				self.save;
			end

			% DISPLAY
			if self.verbose
				self.printProgress('trainCost');
				if ~mod(cnt,self.verbose);
					self.visLearning;
				end
			end
			self.epoch = self.epoch + 1;
	    end
		self.trainTime = toc;

		% RETURN VALUES FROM THE GPU
		if self.useGPU
			self = gpuGather(self);
			reset(self.gpuDevice);
			self.gpuDevice = [];
		end
	end

	function self = fProp(self,netInput, targets)
	%[net,out] = fProp(netInput, targets)
	%--------------------------------------------------------------------------
	%Forward propagation of input signals, <netInput>. Also updates state of
	%network cost, if provided with <targets>. Also returns network output
	%<out>, if requested.
	%--------------------------------------------------------------------------
		if notDefined('targets'), targets = []; end
		nObs = size(netInput, 1);
		self.layers{1}.fm = netInput;

		for lL = 2:self.nLayers
			switch self.layers{lL}.type
			case 'conv'
			% LOOP OVER LAYER FEATURE MAPS
			for jM = 1:self.layers{lL}.nFM

				[nInY,nInX,nInFM,nObs] = size(self.layers{lL-1}.fm);

				% INITIALIZE LAYER MAP -- [nY,nX,nM,nObs]
				featMap = zeros([self.layers{lL}.fmSize,1,nObs]);

				% POOL OVER INPUT FEATURE MAPS,
				% CALC LAYER PRE-ACTIVATION
				for iM = 1:self.layers{lL-1}.nFM
					featMap = featMap + convn(self.layers{lL-1}.fm(:,:,iM,:), ...
					self.layers{lL}.filter(:,:,iM,jM),'valid');
				end

				if any(isnan(self.layers{lL}.b(jM))), keyboard, end

				% ADD LAYER BIAS
				featMap = featMap + self.layers{lL}.b(jM);

				% COMPLETE FEATURE MAP
				self.layers{lL}.fm(:,:,jM,:) = self.calcAct(featMap,self.layers{lL}.actFun);

			end

			case 'subsample'
				stride = self.layers{lL}.stride;
				% DOWNSAMPLE THE FEATURE MAPS FROM LAYER (l-1)
				for jM = 1:self.layers{lL-1}.nFM
					layerIn = self.layers{lL-1}.fm(:,:,jM,:);
					self.layers{lL}.fm(:,:,jM,:) = self.DOWN(layerIn,stride);
				end

			case 'output'
				% UNPACK OUTPUT FEATURES & CALCULATE NETWORK OUTPUT
				self = self.calcOutput;
			case 'rect'
			case 'lcn'
			case 'pool'
			end
		end
		% COST FUNCTION & OUTPUT ERROR SIGNAL
		if ~isempty(targets)
			[self.J, self.netError] = self.cost(targets,self.costFun);
		end
		if nargout > 1
			out = self.layers{end}.act;
		end
	end

	function self = calcOutput(self);
	% net = calcOutput();
	%--------------------------------------------------------------------------
	% Calculate the network ouput given the current parameters
	%--------------------------------------------------------------------------

		[nY,nX,nM,nObs]= size(self.layers{end-1}.fm);

		% # OF ENTRIES IN EACH FEATURE MAP
		nMap = prod([nY,nX]);

		% INITIALIZE OUTPUT FEATURES
		self.layers{end}.features = zeros(nMap*nM,nObs);

		% UNPACK MAPS INTO A MATRIX FOR CALCULATING OUTPUT
		for jM = 1:self.layers{end-1}.nFM
			map = self.layers{end-1}.fm(:,:,jM,:);
			self.layers{end}.features((jM-1)*nMap+1:jM*nMap,:) = reshape(map,nMap,nObs);
		end

		% CALC NET OUTPUTS
		preAct = bsxfun(@plus,self.layers{end}.W* ...
						self.layers{end}.features, ...
		                self.layers{end}.b);

		self.netOut = self.calcAct(preAct,self.layers{end}.actFun);

		if any(isnan(self.netOut)), keyboard; end
	end

	function [J, dJ] = cost(self,targets,costFun)
	% [J, dJ] = cost(self,targets,costFun)
	%--------------------------------------------------------------------------
	% Calculate the cost function <J> and derivative thereof for a set of targets
	% and the current state of the network.
	%--------------------------------------------------------------------------

		netOut = self.netOut;

		[nTargets,nObs] = size(netOut);
		switch costFun
		case 'mse' % REGRESSION
			delta = targets - netOut;
			J = 0.5*sum(sum(delta.^2))/nObs;
			dJ = -delta;

		case 'xent' % BINARY CLASSIFICATION
			J = -sum(sum(targets.*log(netOut) + (1-targets).*log(1-netOut)))/nObs;
			dJ = (netOut - targets)./(netOut.*(1-netOut));

		case 'mcxent' % MULTI-CLASS CLASSIFICATION (UNDER DEVO)
			class = softMax(netOut);
			J = -sum(sum(targets.*log(class)))/nObs;
			dJ = sum(labels - targets);

		case {'class','classerr'}  % CLASSIFICATION ERROR (WINNER TAKE ALL)

			[~, class] = max(netOut,[],1);
			[~, t] = max(targets,[],1);
			J = sum((class ~= t))/nObs;
			dJ = 'no gradient';
		case {'correlation','cc'}
			J = corr2(netOut,targets);
			dJ = 'no gradient';
		end
		if any(isnan(self.J)), keyboard, end
	end

	function self = bProp(self)
	%net = bProp()
	%--------------------------------------------------------------------------
	%Perform gradient descent no the loss w.r.t. each of the model parameters
	%using the backpropagation algorithm. Returns updated network object, <net>
	%--------------------------------------------------------------------------

		% DERIVATIVE OF OUTPUT ACTIVATION FUNCTION
		dAct = self.calcActDeriv(self.netOut,self.layers{end}.actFun);

		% OUTPUT ERROR SIGNAL -- [#Out x #Obs]
		outES = self.netError.*dAct;

		% ERROR SIGNAL  -- [#Features x #Obs]
		es = self.layers{end}.W'*outES;

		% IN CASE LAST FEATURE LAYER IS CONV.
		if strcmp(self.layers{end-1}.type,'conv')
			dAct = self.calcActDeriv(self.layers{end}.features,self.layers{end-1}.actFun);
			es = es.*dAct;
		end

		% REPACK ERROR SIGNAL INTO 2-D FEATURE MAP REPRESENTATION
		[nY,nX,nM,nObs] = size(self.layers{end-1}.fm);
		self.layers{end-1}.es = zeros([nY,nX,nM,nObs]);

		nMap = prod([nY*nX]); % NUMBER OF ENTRIES PER 2D MAP

		for jM = 1:self.layers{end-1}.nFM
			self.layers{end-1}.es(:,:,jM,:) = ...
			reshape(es((jM-1)*nMap+1:jM*nMap,:),[nY,nX,1,nObs]);
		end

		% BACKPROPATE ERROR SIGNAL
		for lL = self.nLayers-2:-1:2
			switch self.layers{lL}.type
			case 'conv'
				stride = self.layers{lL + 1}.stride;
				mapSz = size(self.layers{lL}.fm);
				self.layers{lL}.es = zeros(mapSz);

				for jM = 1:self.layers{lL}.nFM
					switch self.layers{lL+1}.type
					case 'subsample'
						% UPSAMPLE ES FROM ABOVE SUBSAMPLE LAYER
						propES = self.UP(self.layers{lL+1}.es(:,:,jM,:), ...
						                 [stride(1), stride(2),1,1])/prod(stride);
					case 'rect'
					case 'lcn'
					end

                   % DERIVATIVE OF ACTIVATION FUNCTION
                   dAct = self.calcActDeriv(self.layers{lL}.fm(:,:,jM,:), ...
											self.layers{lL}.actFun);

                   % CALCULATE LAYER ERROR SIGNAL
					self.layers{lL}.es(:,:,jM,:) = propES.*dAct;
				end
			case 'rect'
			case 'lcn'
			case 'pool'

			case 'subsample'
				[nY,nX,nM,nObs] = size(self.layers{lL}.fm);
				self.layers{lL}.es = zeros([nY,nX,nM,nObs]);
				for jM = 1:self.layers{lL}.nFM
					% FORM FEATURE MAP ERROR SIGNAL
					propES = zeros(nY,nX,1,nObs);
					for kM = 1:self.layers{lL+1}.nFM
						rotFilt = self.ROT(self.layers{lL+1}.filter(:,:,jM,kM));
						es = self.layers{lL+1}.es(:,:,kM,:);
						propES = propES + convn(es,rotFilt,'full');
					end
					self.layers{lL}.es(:,:,jM,:) = propES;
					if any(isnan(self.layers{lL}.es(:))), keyboard; end
					if any(isinf(self.layers{lL}.es(:))), keyboard; end
				end
			end
		end

		% CALCULATE THE GRADIENTS
		for lL = 2:self.nLayers-1
			[nX,nY,nM,nObs] = size(self.layers{lL}.fm);
			switch self.layers{lL}.type
			case 'conv'
				for jM = 1:self.layers{lL}.nFM
					es = self.layers{lL}.es(:,:,jM,:);
					for iM = 1:self.layers{lL-1}.nFM
						input = self.FLIPDIMS(self.layers{lL-1}.fm(:,:,iM,:));
						dEdFilter = convn(input,es,'valid')/nObs;
						self.layers{lL}.dFilter(:,:,iM,jM) = dEdFilter;
						if isnan(any(dEdFilter)), keyboard; end
					end
					self.layers{lL}.db(jM) = sum(es(:))/nObs;
				end
			case 'rect'
			case 'lcn'
			case 'pool'
			end
		end

		% GRADIENTS FOR OUTPUT LAYER WEIGHTS AND BIASES
		self.layers{end}.dW = outES*self.layers{end}.features'/nObs;
		self.layers{end}.db = mean(outES,2);

	end

	function self = updateParams(self)
	%net = updateParams()
	%--------------------------------------------------------------------------
	%Update network parameters based on states of netowrk gradient, perform
	%regularization such as weight decay and weight rescaling
	%--------------------------------------------------------------------------
		wPenalty = 0;
		for lL = 2:self.nLayers-1
			switch self.layers{lL}.type
			% CURRENTLY, ONLY UPDATE FILTERS AND FM BIASES
			% PERHAPS, IN THE FUTURE, WE'LL BE FANCY, AND DO FANCY UPDATES
			case {'conv','output'}
				lRate = self.layers{lL}.lRate;
				for jM = 1:self.layers{lL}.nFM
					% UPDATE FEATURE BIASES
					self.layers{lL}.b(jM) = self.layers{lL}.b(jM) - ...
					                    lRate*self.layers{lL}.db(jM);

					% UPDATE FILTERS
					for iM = 1:self.layers{lL-1}.nFM
						if self.wPenalty > 0 % L2 REGULARIZATION
							wPenalty = self.layers{lL}.filter(:,:,iM,jM)*self.wPenalty;
						elseif self.wPenalty < 0 % L1-REGULARIZATION (SUB GRADIENTS)
							wPenalty = sign(self.layers{lL}.filter(:,:,iM,jM))*abs(self.wPenalty);
						end
						self.layers{lL}.filter(:,:,iM,jM) = ...
						self.layers{lL}.filter(:,:,iM,jM) - ...
						lRate*(self.layers{lL}.dFilter(:,:,iM,jM)+wPenalty);
					end
				end
			end
	    end
	end

	function out = calcAct(self,in,actFun)
	%out = calcAct(in,actFun)
	%--------------------------------------------------------------------------
	%Calculate the output activation <out> from an input <in> for activation
	%function <actFun>. Available activation functions include 'linear','exp',
	%'sigmoid', 'softmax', 'tanh', and 'softrect'.
	%--------------------------------------------------------------------------

		switch actFun
			case 'linear'
				out = self.stabilizeInput(in,1);

			case 'exp'
				in = self.stabilizeInput(in,1);
				out = exp(in);

			case 'sigmoid'
				in = self.stabilizeInput(in,1);
				out = 1./(1 + exp(-in));

			case 'softmax'
				in = self.stabilizeInput(in,1);
				maxIn = max(in, [], 2);
				tmp = exp(bsxfun(@minus,in,maxIn));
				out = bsxfun(@rdivide,tmp,sum(tmp,2));

			case 'tanh'
				in = self.stabilizeInput(in,1);
				out = tanh(in);

			case 'softrect'
				k = 8;
				in = self.stabilizeInput(in,k);
				out = 1/k.*log(1 + exp(k*in));
		end
	end

	function dAct = calcActDeriv(self,in,actFun)
	%dAct = calcActDeriv(in,actFun)
	%--------------------------------------------------------------------------
	%Calculate the output activation derivatives <dAct> from an input <in> for
	%activation function <actFun>. Available activation functions derivatives
	% include 'linear','exp', sigmoid','tanh', and 'softrect'.
	%--------------------------------------------------------------------------

		switch actFun
			case 'linear'
				dAct = ones(size(in));

			case 'exp';
				in = self.stabilizeInput(in,1);
				dAct = in;

			case 'sigmoid'
				in = self.stabilizeInput(in,1);
				dAct = in.*(1-in);

			case 'tanh'
				in = self.stabilizeInput(in,1);
				dAct = 1 - in.^2;

			case 'softrect'
				k = 8;
				in = self.stabilizeInput(in,k);
				dAct = 1./(1 + exp(-k*in));
		end
	end

	function out = DOWN(self,data,stride)
	%out = DOWN(data,stride)
	%--------------------------------------------------------------------------
	% Downsample 1st 2 dimensions of a tensor
	%--------------------------------------------------------------------------
		tmp = ones(stride(1),stride(2));
		tmp = tmp/prod(stride(:));
		out = convn(data,tmp,'valid');
		out = out(1:stride(1):end,1:stride(2):end,:,:,:);
	end

	function out = UP(self,data,scale);
	%out = UP(data,stride)
	%--------------------------------------------------------------------------
	% Upsample 1st 2 dimensions of a tensor
	%--------------------------------------------------------------------------
	% UPSAMPLE DIMESIONS OF A TENSOR
		dataSz = size(data);
		idx = cell(numel(dataSz),1);
		for iD = 1:numel(dataSz)
			tmp = zeros(dataSz(iD)*scale(iD),1);
			tmp(1:scale(iD):dataSz(iD)*scale(iD)) = 1;
			idx{iD} = cumsum(tmp);
		end
		out = data(idx{:});
	end

	function out = ROT(self,out)
	%out = ROT(self,in)
	%--------------------------------------------------------------------------
	% Rotate the 1st two dimensions of a tensor by one-half rotation
	%--------------------------------------------------------------------------
		out = out(end:-1:1,end:-1:1,:,:);
	end

	function out = FLIPDIMS(self,out)
	%out = FLIPDIMS(in)
	%--------------------------------------------------------------------------
	% Flip all dimensions of a tensor <in>
	%--------------------------------------------------------------------------
		for iD = 1:numel(size(out))
			out = flipdim(out,iD);
		end
	end

	function self = makeBatches(self,data);
	% net = makeBatches(data);
	%--------------------------------------------------------------------------
	% Create batches based on data. Observations are along the rows of data.
	% (ASSUME THAT data IS RANDOMIZED ACROSS SAMPLES (ROWS))
	%--------------------------------------------------------------------------

		nObs = size(data,4);

		if self.nXVal < 1
			nVal = round(self.nXVal*nObs);
		else
			nVal = self.nXVal;
		end

		xValIdx = nObs-nVal+1:nObs;
		nObs = nObs - nVal;
		nBatches = ceil(nObs/self.batchSize);
		idx = round(linspace(1,nObs+1,nBatches+1));

		for iB = 1:nBatches
			if iB == nBatches
				batchIdx{iB} = idx(iB):nObs;
			else
				batchIdx{iB} = idx(iB):idx(iB+1)-1;
			end
		end
		self.trainBatches = batchIdx;

		nxBatches = ceil(nVal/self.batchSize);
		if ~isempty(xValIdx)
			xValIdx = round(linspace(xValIdx(1),xValIdx(end)+1,nxBatches));

			for iB = 1:nxBatches-1
				if iB == nxBatches
					xBatchIdx{iB} = xValIdx(iB):nVal;
				else
					xBatchIdx{iB} = xValIdx(iB):xValIdx(iB+1)-1;
				end
			end
			self.xValBatches = xBatchIdx;
		end
	end

	function self = crossValidate(self,data,targets)
	%net = crossValidate(data,targets)
	%--------------------------------------------------------------------------
	%Run cross-validation on current model parameters.
	%--------------------------------------------------------------------------

		xValCost = 0;
		for iB = 1:numel(self.xValBatches)
			idx = self.xValBatches{iB};
			tmpCost = self.test(data(:,:,:,idx),targets(:,idx),self.costFun);
			xValCost = xValCost + mean(tmpCost);
		end
		% AVERAGE data-VALIDATION ERROR
		self.xValCost(self.epoch) = xValCost/iB;
	end

	% ASSES PREDICTIONS/ERRORS ON TEST DATA
	function [cost,pred] = test(self,data,targets,costFun)
		if notDefined('costFun') costFun=self.costFun; end

		for lL = 1:self.nLayers
			try
				self.layers{lL}.fm = [];
			catch
			end
		end
		self = self.fProp(data,targets);
		pred = self.netOut;
		cost = self.cost(targets,costFun);
	end

	function self = assessNet(self)
	%assessNet()
	%--------------------------------------------------------------------------
	%Utility function to assess the quality of current netork parameters and
	%store net, if necessary.
	%--------------------------------------------------------------------------

		if self.epoch > 1
			if self.xValCost(self.epoch) < self.bestxValCost
				self.bestNet = self.layers;
				self.bestxValCost = self.xValCost(self.epoch);
				self.stopEarlyCnt = 0;
			else
				self.stopEarlyCnt = self.stopEarlyCnt + 1;
			end
		else
			self.bestNet = self.layers; % STORE FIRST NET BY DEFAULT
		end
	end

	function printProgress(self,type)
	%printProgress(type)
	%--------------------------------------------------------------------------
	%Verbose utility function. <type> is the type of message to print.
	%--------------------------------------------------------------------------
		switch type
		case 'epoch'
			fprintf('Epoch: %i/%i',self.epoch,self.nEpoch);
		case 'trainCost'
			fprintf('\t%s: %2.3f\n',self.costFun,self.trainCost(self.epoch));
		case 'time'
			fprintf('\tTime: %g\n', toc);
		case 'xValCost'
			if ~self.stopEarlyCnt
				fprintf('\tCrossValidation Error:  %g (best net) \n',self.xValCost(self.epoch));
			else
				fprintf('\tCrossValidation Error:  %g\n',self.xValCost(self.epoch));
			end
		case 'gradCheck'
			netGrad = self.auxVars.netGrad;
			numGrad = self.auxVars.numGrad;
			gradFailed = self.auxVars.gradFailed;
			switch gradFailed
				case 1, gradStr = '(Failed)';
				otherwise, gradStr = '(Passed)';
			end
			fprintf('\tNetwork = %2.6f  \t Numerical = %2.6f  %s\n' ,netGrad,numGrad,gradStr);
		case 'save'
			fprintf('\nSaving...\n\n');
		end
	end

	function self = init(self,arch)
	%net = init(arch)
	%--------------------------------------------------------------------------
	%Utility function to used intitialize a neural network given an architecture
	%<arch> is a cell array of structs, one for each layer in the network. The
	%fields of each structure will depend on the type of layer. Supported lay-
	%ers include 'input','conv','subsample','output'.
	%
	%Note, the first and last layers should be of 'type' 'input' and 'output'
	%respectively.
	%
	% Returns a mlcnn object, <net>.
	%--------------------------------------------------------------------------
	% INTITIALIZE A NEURAL NETWORK GIVEN AN ARCHITECTURE
	% <arch> IS A CELLARRAY OF STRUCTS WITH LAYER-SPECIFIC PARAMETERS

		arch = self.ensureArchitecture(arch);
		self.nLayers = numel(arch);

		% INITIALIZE LAYERS FROM ARCHITECTURE
	    for lL = 1:numel(arch)
		    self.layers{lL}.type = arch{lL}.type;
		    switch arch{lL}.type
		    case 'input'
			    self.layers{lL}.dataSize = arch{lL}.dataSize;
			    self.layers{lL}.fmSize = arch{lL}.dataSize(1:2);
			    self.layers{lL}.nFM = arch{lL}.dataSize(3);

			case 'conv'
				if strcmp(arch{lL-1}.type,'input')
					nInY = arch{lL-1}.dataSize(1);
					nInX = arch{lL-1}.dataSize(2);
					nInFM = arch{lL-1}.dataSize(3);
				else
					nInX = self.layers{lL-1}.fmSize(2);
					nInY = self.layers{lL-1}.fmSize(1);
					nInFM = self.layers{lL-1}.nFM;

				end
				% INTERMEDIATE VARIABLES
				nFM = arch{lL}.nFM;
				filtSize = arch{lL}.filterSize;
				fmSize = [nInY,nInX] - filtSize + 1;
				fanIn = nInFM*prod(filtSize);
				fanOut = nFM*prod(filtSize);
				range = 2*sqrt(6/((fanIn + fanOut)));

				% INITIALIZE LAYER PARAMETERS
				self.layers{lL}.actFun = arch{lL}.actFun;
				self.layers{lL}.lRate = arch{lL}.lRate;
				self.layers{lL}.filterSize = filtSize;
				self.layers{lL}.fmSize = fmSize;
				self.layers{lL}.nFM = nFM;
				self.layers{lL}.fm = [];
				self.layers{lL}.filter = range*(rand([filtSize,nInFM,nFM])-.5);
				self.layers{lL}.dFilter = zeros(size(self.layers{lL}.filter));
				self.layers{lL}.b = zeros(nFM,1);
				self.layers{lL}.db = zeros(nFM,1);

			case 'subsample'
				% INTERMEDIATE VARIABLES
				stride = arch{lL}.stride;
				fmSize = floor(self.layers{lL-1}.fmSize./stride);
				nFM = self.layers{lL-1}.nFM;

				% INITIALIZE LAYER PARAMETERS
				self.layers{lL}.stride = stride;
				self.layers{lL}.fmSize = fmSize;
				self.layers{lL}.nFM = nFM;
				self.layers{lL}.fm = [];
				self.layers{lL}.b = zeros(nFM,1);
				self.layers{lL}.db = zeros(nFM,1);

			case 'output'

				nOut = arch{lL}.nOut;
				nFMIn = self.layers{lL-1}.nFM;
				nOutFeats = prod([self.layers{lL-1}.fmSize,nFMIn]);
				range = 2*sqrt(6/(nOutFeats + nOut));

				% ADJUST NETWORK OUTPUT LAYER PARAMETERS
				self.layers{lL}.nOut = nOut;
				self.layers{lL}.actFun = arch{lL}.actFun;
				self.layers{lL}.lRate = arch{lL}.lRate;
				self.layers{lL}.W = range*(rand(nOut,nOutFeats)-.5);
				self.layers{lL}.dW = zeros(size(self.layers{lL}.W));
				self.layers{lL}.b = zeros(nOut,1);
				self.layers{lL}.db = zeros(nOut,1);
		    end
		end
	end

	function arch = ensureArchitecture(self,arch)
	%arch = ensureArchitecture(arch)
	%--------------------------------------------------------------------------
	%Utility function to reprocess a supplied architecture, <arch>
	%--------------------------------------------------------------------------

		if ~iscell(arch), error('<arch> needs to be a cell array of layer params');end
		if ~strcmp(arch{1}.type,'input'), error('define an input layer'); end
		if ~strcmp(arch{end}.type,'output'), error('define an output layer'); end

		% ENSURE LAYER-SPECIFIC PARAMS
		for lL = 1:numel(arch)
			lParams = fields(arch{lL});
			switch arch{lL}.type
			case 'input'
				if ~any(strcmp(lParams,'dataSize')), error('must provide data size');end

			case 'conv'
				if ~any(strcmp(lParams,'filterSize')) || isempty(arch{lL}.filterSize)
					arch{lL}.filterSize = [5 5];
				elseif numel(arch{lL}.filterSize) == 1;
					arch{lL}.filterSize = repmat(arch{lL}.filterSize,[1,2]);
				end
				if ~any(strcmp(lParams,'nFM'));
					arch{lL}.nFM = 10;
				end
				if ~any(strcmp(lParams,'lRate'));
					arch{lL}.lRate = .5;
				end
				if ~any(strcmp(lParams,'actFun'));
					arch{lL}.actFun = 'sigmoid';
				end

			case 'subsample'
				if ~any(strcmp(lParams,'stride')) || isempty(arch{lL}.stride);
					arch{lL}.stride = [2 2];
				elseif numel(arch{lL}.stride) == 1;
					arch{lL}.stride = repmat(arch{lL}.stride,[1,2]);
				end
			case 'output'
				if ~any(strcmp(lParams,'nOut'))
					error('must provide number of outputs');
				end
				if ~any(strcmp(lParams,'actFun'));
					arch{lL}.actFun = 'sigmoid';
				end
				if ~any(strcmp(lParams,'lRate'));
					arch{lL}.lRate = .5;
				end
			case 'rect'
			case 'lcn'
			case 'pool'
			end
		end
	end

	function in = stabilizeInput(self,in,k);
	%in = stabilizeInput(in,k);
	%--------------------------------------------------------------------------
	%Utility function to ensure numerical stability. Clips values of <in>
	%such that exp(k*in) is within single numerical precision.
	%--------------------------------------------------------------------------
		cutoff = log(realmin('single'));
		in(in*k>-cutoff) = -cutoff/k;
		in(in*k<cutoff) = cutoff/k;
	end

	function visLearning(self)
	%visLearning()
	%--------------------------------------------------------------------------
	%Utility function to perform learning isualizations.
	%--------------------------------------------------------------------------
		try
			self.visFun(self);
		catch
			if ~isfield(self.auxVars,'printVisWarning')
				fprintf('\nWARNING: visualization failed.')
				self.auxVars.printVisWarning = true;
			end
		end
	end

	function save(self);
	%save();
	%--------------------------------------------------------------------------
	%Utility function to save current network.
	%--------------------------------------------------------------------------
		if self.verbose, self.printProgress('save'); end
		if ~isdir(self.saveDir)
			mkdir(self.saveDir);
		end
		fileName = fullfile(self.saveDir,sprintf('mlcnn.mat'));
		net = self.bestNet;
		save(fileName,'net');
	end
end % END METHODS
end % END CLASSDEF


function RMP = learnRMP(Problem,SubPopulation)

%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

   Population = [SubPopulation{:}];
    for i = 1 : length(Problem.SubD)
        subpops(i).data = [];
        vars(i)         = Problem.SubD(i);
    end
    for i = 1 : length(Population)
        subpops(Population(i).dec(end)).data = [subpops(Population(i).dec(end)).data;Population(i).dec(1:Problem.SubD(1))];
    end
    RMP = learnRMP_sub(subpops,vars);
end

function rmpMatrix = learnRMP_sub(subpops,vars)
% There are two inputs. subpops(i).data corresponds to the population
% corresponding to the ith task; vars(i) is the number of design variables
% of the ith task.

    numtasks  = length(subpops);
    maxdims   = max(vars);
    rmpMatrix = eye(numtasks);
    % Add noise and Build probabilistic models
    for i = 1 : numtasks
        probmodel(i).nsamples = size(subpops(i).data,1);
        nrandsamples          = floor(0.1*probmodel(i).nsamples);
        randMat               = rand(nrandsamples,maxdims);
        probmodel(i).mean     = mean([subpops(i).data;randMat]);	% Univariate distribution mean
        probmodel(i).stdev    = std([subpops(i).data;randMat]);     % Univariate distribution standard deviation
    end
    for i = 1 : numtasks
        for j = i+1 : numtasks
            popdata(1).probmatrix = ones(probmodel(i).nsamples,2);
            popdata(2).probmatrix = ones(probmodel(j).nsamples,2);
            dims = min([vars(i),vars(j)]);
            for k = 1 : probmodel(i).nsamples
                for l = 1 : dims
                    popdata(1).probmatrix(k,1) = popdata(1).probmatrix(k,1)*pdf('Normal',subpops(i).data(k,l),probmodel(i).mean(l),probmodel(i).stdev(l));
                    popdata(1).probmatrix(k,2) = popdata(1).probmatrix(k,2)*pdf('Normal',subpops(i).data(k,l),probmodel(j).mean(l),probmodel(j).stdev(l));
                end
            end
            for k = 1 : probmodel(j).nsamples
                for l = 1 : dims
                    popdata(2).probmatrix(k,1) = popdata(2).probmatrix(k,1)*pdf('Normal',subpops(j).data(k,l),probmodel(i).mean(l),probmodel(i).stdev(l));
                    popdata(2).probmatrix(k,2) = popdata(2).probmatrix(k,2)*pdf('Normal',subpops(j).data(k,l),probmodel(j).mean(l),probmodel(j).stdev(l));
                end
            end
            rmpMatrix(i,j) = max([0,fminbnd(@(x)loglik(x,popdata,numtasks),0,1)+normrnd(0,0.01)]);  %fminbnd(@(x)loglik(x,popdata,numtasks),0,1)
            rmpMatrix(i,j) = min(rmpMatrix(i,j),1);
            rmpMatrix(j,i) = rmpMatrix(i,j);
        end
    end
end

function f = loglik(rmp,popdata,ntasks)
    f = 0;
    for i = 1 : 2
        for j = 1 : 2
            if i == j
                popdata(i).probmatrix(:,j) = popdata(i).probmatrix(:,j)*(1-(0.5*(ntasks-1)*rmp/ntasks));
            else
                popdata(i).probmatrix(:,j) = popdata(i).probmatrix(:,j)*0.5*(ntasks-1)*rmp/ntasks;
            end
        end
        f = f + sum(-log(sum(popdata(i).probmatrix,2)));
    end
end


%Figure 9.23       Feedback Control of Dynamic Systems, 5e
%                      Franklin, Powell, Emami
%
clf;
ka=10;
sim('fig9_22')
figure(1)
plot(uantib(:,1),uantib(:,2))
axis([0 10 -1.2 1.2])
hold on
figure(2)
plot(yantib(:,1),yantib(:,2))
hold on
ka=0;
sim('fig9_22')
figure(1)
plot(uantib(:,1),uantib(:,2))
title('Fig. 9.23(b) Control with and without antiwindup')
xlabel('Time (sec)');
ylabel('Control');
nicegrid;
gtext('With anti-windup')
gtext('Without anti-winduup')
figure(2)
plot(yantib(:,1),yantib(:,2))
xlabel('Time (sec)');
ylabel('Output');
title('Fig. 9.23(a) Output with and without antiwindup')
gtext('With anti-windup')
gtext('Without anti-winduup')
nicegrid;
hold off