No BSD License  

Highlights from
Automatic Spectral Analysis

from Automatic Spectral Analysis by Stijn de Waele
Automatic spectral analysis for irregular sampling/missing data, analysis of spectral subband.

[ar,ma,ASAsellog,ASAcontrol]=arh2arma(varargin)
function [ar,ma,ASAsellog,ASAcontrol]=arh2arma(varargin)
%ARH2ARMA ARMA model identification
%   [AR,MA,SELLOG] = ARH2ARMA(ARH,N_OBS) estimates autoregressive moving 
%   average models from the high-order AR model ARH and selects a model with 
%   optimal predictive qualities. ARH has been estimated from N_OBS
%   observations. Only ARMA(P,P-1) models are considered with AR order P 
%   being greater than the MA order by one. The selected model is returned 
%   in the parameter vectors AR and MA. The structure SELLOG provides
%   additional information on the selection process.
%   
%   ARH2ARMA(ARH,N_OBS,CAND_AR_ORDER,ARMA_ORDER_DIFF) selects only from 
%   candidate models ARMA(CAND_AR_ORDER,CAND_AR_ORDER - ARMA_ORDER_DIFF). 
%   CAND_AR_ORDER must either be a row of ascending orders, or a single 
%   order (in which case no true order selection is performed). 
%   ARMA_ORDER_DIFF, a scalar, is the difference between AR and MA orders 
%   being constant during selection. In the current version, only a value   
%   of 1 for ARMA_ORDER_DIFF allowed.
%   
%   N_OBS can also be a vector containing the lengths of segments of data.
%
%   CAND_AR_ORDER or ARMA_ORDER_DIFF may be passed as empty arguments. In 
%   case of empty ARMA_ORDER_DIFF, the difference between orders will be 
%   chosen 1. In case of empty CAND_AR_ORDER, an appropriate set of 
%   candidate orders will be chosen depending on the value of 
%   ARMA_ORDER_DIFF and the number of observations.
%   
%   ARH2ARMA is an ARMASA_RS main function.
%   
%   See also: SIG2ARMA, ARH2AR, ARH2MA, ARMASEL_RS, DATA_SEGMENTS.
   
%   Reference:  P. M. T. Broersen and S. de Waele, Selection of Order and
%               Type of Time Series Models Estimated from Reduced Statistics
%               Proceedings of {SYSID} 2000, May 2002.

%Header
%====================================================================

%Declaration of variables
%------------------------

%Declare and asarhn values to local variables
%according to the input argument pattern
[ar_rs,n_obs,cand_ar_order,arma_order_diff,ASAcontrol] = ASAarg(varargin, ...
{'ar_rs'       ;'n_obs'        ;'cand_ar_order';'arma_order_diff';'ASAcontrol'}, ...
{'isnumeric'   ;'isnumeric'    ;'isnumeric'    ;'isnumeric'      ;'isstruct'  }, ...
{'ar_rs'       ;'n_obs'                                                       }, ...
{'ar_rs'       ;'n_obs'        ;'cand_ar_order'                               }, ...
{'ar_rs'       ;'n_obs'        ;'cand_ar_order';'arma_order_diff'             });

if isequal(nargin,1) & ~isempty(ASAcontrol)
      %ASAcontrol is the only input argument
   ASAcontrol.error_chk = 0;
   ASAcontrol.run = 0;
end

%Declare ASAglob variables 
ASAglob = {'ASAglob_subtr_mean';'ASAglob_mean_adj'; ...
      'ASAglob_rc';'ASAglob_ar';'ASAglob_final_f'; ...
      'ASAglob_final_b';'ASAglob_ar_cond'};

%Assign values to ASAglob variables by screening the
%caller workspace
for ASAcounter = 1:length(ASAglob)
   ASAvar = ASAglob{ASAcounter};
   eval(['global ' ASAvar]);
   if evalin('caller',['exist(''' ASAvar ''',''var'')'])
      eval([ASAvar '=evalin(''caller'',ASAvar);']);
   else
      eval([ASAvar '=[];']);
   end
end

%ARMASA-function version information
%-----------------------------------

%This ARMASA-function is characterized by
%its current version,
ASAcontrol.is_version = [2000 12 30 20 0 0];
%and its compatability with versions down to,
ASAcontrol.comp_version = [2000 12 30 20 0 0];

%This function calls other functions of the ARMASA
%toolbox. The versions of these other functions must
%be greater than or equal to:
ASAcontrol.req_version.burg = [2000 12 30 20 0 0];
ASAcontrol.req_version.cic_s = [2000 12 30 20 0 0];
ASAcontrol.req_version.rc2arset = [2000 12 30 20 0 0];
ASAcontrol.req_version.ar2arset = [2000 12 30 20 0 0];
ASAcontrol.req_version.cov2arset = [2000 12 30 20 0 0];
ASAcontrol.req_version.armafilter = [2000 12 12 14 0 0];
ASAcontrol.req_version.convol = [2000 12 6 12 17 20];
ASAcontrol.req_version.convolrev = [2000 12 6 12 17 20];
ASAcontrol.req_version.deconvol = [2000 12 12 12 0 0];

%Checks
%------

if ~isfield(ASAcontrol,'error_chk') | ASAcontrol.error_chk
   %Perform standard error checks
   %Input argument format checks
   ASAcontrol.error_chk = 1;
   if ~isnum(ar_rs)
      error(ASAerr(11,'ar_rs'))
   end
   if ~isvector(ar_rs)
      error([ASAerr(14) ASAerr(15,'ar_rs')])
   end
   if ~isempty(cand_ar_order)
      if ~isnum(cand_ar_order) | ~isintvector(cand_ar_order) |...
            cand_ar_order(1)<0 | ~isascending(cand_ar_order)
         error(ASAerr(12,{'candidate';'cand_ar_order'}))
      elseif size(cand_ar_order,1)>1
         cand_ar_order = cand_ar_order';
         warning(ASAwarn(25,{'column';'cand_ar_order';'row'},ASAcontrol))
      end
   end
   if ~isempty(arma_order_diff) & ...
         (~isnum(arma_order_diff) | ...
         ~isintscalar(arma_order_diff) |...
         arma_order_diff<0)
      error(ASAerr(17,'arma_order_diff'))
   end
   
   %Input argument value checks
   if ~isreal(ar_rs)
      error(ASAerr(13))
   end
   if ~isempty(cand_ar_order) & ...
         ~isempty(arma_order_diff)
      if cand_ar_order(1)~=0 & ...
            (arma_order_diff < 1 | ...
            arma_order_diff > cand_ar_order(1))
         error(ASAerr(18,{'arma_order_diff';'1';...
               num2str(cand_ar_order(1))}))
      elseif length(cand_ar_order)>1 & ...
            (arma_order_diff < 1 | ...
            arma_order_diff > cand_ar_order(2))
         error(ASAerr(18,{'arma_order_diff';'1';...
               num2str(cand_ar_order(2))}))
      end
   end
end

if ~isfield(ASAcontrol,'version_chk') | ASAcontrol.version_chk
      %Perform version check
   ASAcontrol.version_chk = 1;
      
   %Make sure the requested version of this function
   %complies with its actual version
   ASAversionchk(ASAcontrol);
   
   %Make sure the requested versions of the called
   %functions comply with their actual versions
   cic_s(ASAcontrol);
   rc2arset(ASAcontrol);
   ar2arset(ASAcontrol);
   cov2arset(ASAcontrol);
   armafilter(ASAcontrol);
   convol(ASAcontrol);
   convolrev(ASAcontrol);
   deconvol(ASAcontrol);
end

if ~isfield(ASAcontrol,'run') | ASAcontrol.run
   ASAcontrol.run = 1;
   ASAcontrol.error_chk = 0;
   ASAtime = clock;
   ASAdate = now;
end

if ASAcontrol.run %Run the computational kernel   
   ASAcontrol.version_chk = 0;
   ASAcontrol.error_chk = 0;
   
%Main   
%=====================================================
  
%Initialization of variables
%---------------------------

%Determine the size of the reduced statistic
n_red_stat = length(ar_rs)-1;
var = 1;   %Normalized variance

n_obs_tot = sum(n_obs);

%Assign zero-order ARMA parameters
ma_stack{1} = 1;
ar_stack{1} =1;
stack_entry = 1;

%Initialize AR parameter vectors
rc = [];
ar = [];

all_ill_cond = []; %Order where all methods are ill-conditioned

warn_state = warning;

%Combined determination of maximum candidate AR
%and MA orders
%----------------------------------------------
def_max_ar_order = length(ar_rs)-1;

%Asess the default value of the maximum candidate AR
%order
if isempty(arma_order_diff) %The default value of
    %Assign a default value 1 to the difference between
    %AR and MA orders
    arma_order_diff = 1;
elseif arma_order_diff < 1 | ...
        arma_order_diff > def_max_ar_order
    error(ASAerr(18,{'arma_order_diff';'1';...
            [num2str(def_max_ar_order) ...
                ' (== max. candidate AR order, selected by default)']}))
end

def_max_ar_order = ...
    min(fix([n_obs_tot/10 40*log10(n_obs_tot) (arma_order_diff+n_red_stat)/2]));
if def_max_ar_order > 200; 
    %Limit the default to order 200
    def_max_ar_order = 200;
end

%Determine maximum candidate ARMA orders
if isempty(cand_ar_order) %The default max. candidate
    %AR order is applicable
    cand_ar_order = ...
        [0 arma_order_diff:def_max_ar_order];
end
max_ar_order = cand_ar_order(end);
max_ma_order = max_ar_order-arma_order_diff;

%Determine the maximum candidate sliding AR order
if max_ar_order <= def_max_ar_order %The specified
    %max. AR order is less than the default value
    %Condition the max. sliding AR order to the AR
    %default
    max_slid_ar_order = min(fix([5*def_max_ar_order n_red_stat]));
else %The specified AR order is greater than the
    %default value (which means relatively large)
    %Except for the imposed maximum of 200, apply the
    %same rule to obtain the max. sliding AR order
    max_slid_ar_order = min(fix([5*max_ar_order n_red_stat]));
    %Check whether the size of the reduced statistic is sufficient
    %for the requested maximum model order
    if max_slid_ar_order-max_ar_order < max_ma_order
        %Lack of degrees of freedom in estimation
        max_ar_order = ...
            fix((arma_order_diff+max_slid_ar_order)/2);
        max_ma_order = ...
            max_ar_order - arma_order_diff;
        warning(ASAwarn(16,{num2str(max_ar_order);...
                num2str(max_ma_order)},ASAcontrol));
        if ~any(max_ar_order==cand_ar_order)
            error(ASAerr(38))
        end
    end
end

min_ar_order = arma_order_diff;
min_ma_order = 0;

%Preparations for the estimation procedure
%-----------------------------------------

if max_ar_order > 0 %An estimation is required
    [ar_orig, rc] = ar2arset(ar_rs,[1:max_slid_ar_order],ASAcontrol);
    rc = [1 rc];
    
    %AR model order selection
    if isequal(ASAglob_ar_cond,1) & ...
            ~isempty(ASAglob_ar) %The selected AR order
        %that will be used, is conditioned to an
        %earlier performed AR selection procedure
        %Assess the order of a previously selected AR
        %model
        ar = ASAglob_ar;
        sel_ar_order = length(ar)-1;
    else 
        %Select the optimal AR order for prediction
        res = var*[1 cumprod(1-rc(2:max_slid_ar_order+1).^2)];
        [min_value,sel_location] = ...
            min(cic_s(res,n_obs,ASAcontrol));
        sel_ar_order = sel_location-1;
        if sel_ar_order
            ar = ar_orig{sel_ar_order};
        else
            ar = 1;
        end
    end
    clear res
    
    %Determine the zero-order estimate
    counter = 1;
    res(counter) = moderr_e(1,1,ar_rs,1,1,ASAcontrol)+1;
    ar_stack{counter} = 1;
    ma_stack{counter} = 1;
    meth_s_stack(1) = 1;
    
    %Compute the first ARMA model, which equals an
    %AR model of the minimum candidate AR order
    counter = counter+1;
    ar_order = min_ar_order;
    ma_order = min_ma_order;
    ar_stack{counter} = ar_orig{ar_order};
    ma_stack{counter} = 1;
    
    %Evaluate the variables involved in order selection
    %for the model computed above
    res(counter) = moderr_e(ar_stack{counter},1,ar_rs,1,1,ASAcontrol)+1;
    
    ar_order = ar_order+1;
    ma_order = ma_order+1;
    
    %Initialize the sliding AR order
    slid_ar_order = 3*sel_ar_order+ar_order+ma_order;
    if slid_ar_order > max_slid_ar_order
        slid_ar_order = max_slid_ar_order;
    end
    
    %Initialize the estimation loop
    ar_order_start = ar_order;
    reset_type = 1;
    
    if arma_order_diff ~= 1,
        arma_order_diff
        error('This version of Reduced Statistics ARMA estimation only works for arma_order_diff = 1.')
    end
    
    
    %Estimation procedure
    %--------------------
    %Selection of estimators that are calculated (1 = calculate / 0 = not calculate).
    arma_ini_est = [1 1 1 1];
    all_ill_cond = [];

    for ar_order = ar_order_start:max_ar_order
        ar_slid_win = ar_orig{slid_ar_order};
        res_s = Inf;   %Reset residual of selected estimator
        %_s indicates: selected out of different estimators for the current order
        if arma_ini_est(1),
            %Method 1: long AR
            lastwarn(''); warning on
            R = toeplitz(ar_slid_win(ar_order+1:end-1)',ar_slid_win(ar_order+1:-1:3));
            f = ar_slid_win(ar_order+2:end)';
            ma_ini = [1 -(R\f)'];
            ar_ini = convol(ar_slid_win,ma_ini);
            if ar_ini(1)~=1, 
                disp(['ARMA-Order = ' int2str(ar_order) ': Durbin 1 is ill-conditioned for long AR'])
                arma_ini_est(1) = 0;
            else
                ar_ini = ar2arset(ar_ini,ar_order,ASAcontrol);
                ar_ini = ar_ini{1};
                if ~isempty(lastwarn),
                    disp(['ARMA-Order = ' int2str(ar_order) ': Durbin 1 is ill-conditioned for long AR'])
                    arma_ini_est(1) = 0;
                else
                    [ar_i,ma_i] = durbin2(ar_slid_win,ar_ini,ma_order,ASAcontrol);
                    res_i = moderr_e(ar_i,ma_i,ar_rs,1,1,ASAcontrol)+1;
                    if res_i<0,
                        disp(['ARMA-Order = ' int2str(ar_order) ': Durbin 1 is ill-conditioned for long AR'])
                        arma_ini_est(1) = 0;
                    elseif res_i < res_s,
                        ar_s = ar_i; ma_s = ma_i; res_s = res_i;
                        meth_s = 1;
                    end
                end
            end
        end
        
        if arma_ini_est(2),
            %Method 2: long MA
            lastwarn(''); warning on
            ghat=armafilter([1; zeros(2*n_red_stat,1)],ar_slid_win,1)'; % impulse response
            R = toeplitz(ghat(ar_order:end-1)',ghat(ar_order:-1:1));
            f = ghat(ar_order+1:end)';
            ar_ini = [1 -(R\f)'];
            if ~isempty(lastwarn),
                disp(['ARMA-Order = ' int2str(ar_order) ': Durbin 1 is ill-conditioned for long MA'])
                arma_ini_est(2) = 0;
            else 
                [ar_i,ma_i] = durbin2(ar_slid_win,ar_ini,ma_order,ASAcontrol);
                res_i = moderr_e(ar_i,ma_i,ar_rs,1,1,ASAcontrol)+1;
                if res_i<0,
                    disp(['ARMA-Order = ' int2str(ar_order) ': Durbin 1 is ill-conditioned for long MA'])
                    arma_ini_est(2) = 0;
                elseif res_i < res_s,
                    ar_s = ar_i; ma_s = ma_i; res_s = res_i;
                    meth_s = 2;
                end
            end
        end
        
        if arma_ini_est(3),
            %Method 3: long COV
            lastwarn(''); warning on
            l = ar_order-1;
            coryw = arma2cor(ar_slid_win,1);
            ncor = length(coryw)-1;
            coryw = [coryw((ar_order-1:-1:1)+1) coryw]; %cor(t) = cor(t+ar_order)
            myw = toeplitz(coryw((l:ncor-1) +ar_order),coryw((l:-1:l-ar_order+1)  +ar_order));
            vyw = -coryw((l+1:ncor) +ar_order)';
            ar_ini = myw\vyw;
            ar_ini = [1 ar_ini'];   
            warning on
            if ~isempty(lastwarn),
                disp(['ARMA-Order = ' int2str(ar_order) ': Durbin 1 is ill-conditioned for long COV'])
                arma_ini_est(3) = 0;
            else
                [ar_i,ma_i] = durbin2(ar_slid_win,ar_ini,ma_order,ASAcontrol);
                res_i = moderr_e(ar_i,ma_i,ar_rs,1,1,ASAcontrol)+1;
                if res_i<0,
                    disp(['ARMA-Order = ' int2str(ar_order) ': Durbin 1 is ill-conditioned for long COV'])
                    arma_ini_est(3) = 0;
                elseif res_i < res_s,
                    ar_s = ar_i; ma_s = ma_i; res_s = res_i;
                    meth_s = 3;
                end
            end
        end
        
        if arma_ini_est(4),
            %Method 4: long Rinv; Inverse correlation method  
            lastwarn(''); warning on
            Rinv=arma2cor(1,ar_slid_win,slid_ar_order+1); %inverse correlation for ARMA method 4
            R = toeplitz(Rinv(ar_order+1:end-1)',Rinv(ar_order+1:-1:3));
            f = Rinv(ar_order+2:end)';
            ma_ini = [1 -(R\f)'];
            ar_ini = convol(ar_slid_win,ma_ini);
            if ar_ini(1)~=1, 
                disp(['ARMA-Order = ' int2str(ar_order) ': Durbin 1 is ill-conditioned for long Rinv'])
                arma_ini_est(4) = 0;
            else
                ar_ini = ar2arset(ar_ini,ar_order,ASAcontrol);
                ar_ini = ar_ini{1};
                if ~isempty(lastwarn)
                    disp(['ARMA-Order = ' int2str(ar_order) ': Durbin 1 is ill-conditioned for long Rinv'])
                    arma_ini_est(4) = 0;
                else 
                    [ar_i,ma_i] = durbin2(ar_slid_win,ar_ini,ma_order,ASAcontrol);
                    res_i = moderr_e(ar_i,ma_i,ar_rs,1,1,ASAcontrol)+1;
                    if res_i<0,
                        disp(['ARMA-Order = ' int2str(ar_order) ': Durbin 1 is ill-conditioned for long Rinv'])
                        arma_ini_est(4) = 0;
                    elseif res_i < res_s,
                        ar_s = ar_i; ma_s = ma_i; res_s = res_i;
                        meth_s = 4;
                    end
                end
            end
        end
        if ~any(arma_ini_est),
            %If non of the estimators yield a result, leave loop
            all_ill_cond = ar_order; %Order where all methods are ill-conditioned
            warning(['No ARMA estimates for order => ' int2str(ar_order)])
            break
        end
        %Add the computed parameter vectors to their stacks
        counter = counter+1;
        ar_stack{counter} = ar_s;
        ma_stack{counter} = ma_s;
        res(counter) = res_s;
        meth_s_stack(counter) = meth_s;
        ma_order = ma_order+1;
        
        if slid_ar_order < max_slid_ar_order-1
            %Slide the AR order 2 steps forward
            slid_ar_order = slid_ar_order+2;
        elseif slid_ar_order == max_slid_ar_order-1
            %Equalize the sliding AR order to its maximum  
            slid_ar_order = max_slid_ar_order;
        end
    end  
    if ~isempty(all_ill_cond)
        %If none of the estimators was succesfull:
        %Parameter estimates for higher order models
        %equal to last estimated ARMA model.
        ar_last = ar_stack{counter};
        ma_last = ma_stack{counter};
        res_last= res(counter);
        meth_s_last = meth_s_stack(counter);
        for ar_order = all_ill_cond:max_ar_order 
            counter = counter+1;
            ar_last = [ar_last 0];
            ma_last = [ma_last 0];
            %Possible: Durbin2 iterations
            ar_stack{counter} = ar_last;
            ma_stack{counter} = ma_last;
            res(counter) = res_last;
            meth_s_stack(counter) = meth_s_last;
        end
    end

   %ARMA model order selection
   %--------------------------
   [dummy gain] = arma2cor(ar_rs,1,0);
   res = res/gain;
   %Evaluate variables involved in order selection
   ar_orders = [0 arma_order_diff:max_ar_order];
   ma_orders = max(0,ar_orders-arma_order_diff);
   n_par = ar_orders+ma_orders;
   gic3 = log(res)+3*n_par/n_obs_tot;
    
   %Estimate the prediction error
   if ASAglob_mean_adj
       pe_est = res.*...
           (n_obs_tot+n_par+1)./...
           (n_obs_tot-n_par-1);
   else
       pe_est = res.*...
           (n_obs_tot+n_par)./...
           (n_obs_tot-n_par);
   end
   
   %Reduce the arrays computed above, keeping only the
   %elements associated with requested candidate
   %orders
   counter = 1;
   req_counter = 1;
   det_order = ...
      [0 min_ar_order ar_order_start:max_ar_order];
   for order = 0:max_ar_order
      if order == det_order(counter)
         if order == cand_ar_order(req_counter)
            res(req_counter) = res(counter);
            gic3(req_counter) = gic3(counter);
            pe_est(req_counter) = pe_est(counter);
            req_counter = req_counter+1;            
         end
         counter = counter+1;
      end
   end
   res = res(1:req_counter-1);
   gic3 = gic3(1:req_counter-1);
   pe_est = pe_est(1:req_counter-1);

   %Assess the order:
   %The order to be selected corresponds to the
   %location where GIC(3) has its minimum value
   [min_value,sel_location] = min(gic3);
   sel_cand_ar_order = cand_ar_order(sel_location);
   stack_entry = ...
      max(1,sel_cand_ar_order-min_ar_order+2);
else
    %Determine only the zero-order estimate
    counter = 1;
    res(counter) = moderr_e(1,1,ar_rs,1,1,ASAcontrol)+1;
    ar_stack{counter} = 1;
    ma_stack{counter} = 1;
    meth_s_stack(1) = 1;
    
    %Evaluate the variables involved in order selection
    %for the model computed above
    res(counter) = moderr_e(ar_stack{counter},1,ar_rs,1,1,ASAcontrol)+1;

   %Estimate the prediction error
   if ASAglob_mean_adj
       pe_est = res.*(n_obs_tot+1)/(n_obs_tot-1);
   else
       pe_est = res;
   end
   gic3 = log(res);
   all_ill_cond = [];
end

%Arranging output arguments
%--------------------------

ar_sel = ar;

%Retrieve the parameters of the proper model
ar = ar_stack{stack_entry};
ma = ma_stack{stack_entry};

%Assign reflectioncoefficients to ASAglob_rc, in order
%to make them available for other ARMASA functions
if ~isempty(rc)
   ASAglob_rc = rc;
end

%Generate a structure variable ASAsellog to report
%the selection process
ASAsellog.funct_name = mfilename;
ASAsellog.funct_version = ASAcontrol.is_version;
ASAsellog.date_time = ...
   [datestr(ASAdate,8) 32 datestr(ASAdate,0)];
ASAsellog.comp_time = etime(clock,ASAtime);
ASAsellog.ar = ar;
ASAsellog.ma = ma;
ASAsellog.ar_sel = ar_sel;
ASAsellog.mean_adj = ASAglob_mean_adj;
ASAsellog.cand_ar_order = cand_ar_order;
ASAsellog.arma_order_diff = arma_order_diff;
ASAsellog.gic3 = gic3;
ASAsellog.pe_est = pe_est;
ASAsellog.all_ill_cond = all_ill_cond;
% if cand_ar_order(1)~=0,
%     %Remove white noise model
%     ar_stack = ar_stack(2:end);
%     ma_stack = ma_stack(2:end);
%     meth_s_stack = meth_s_stack(2:end);
% end
ASAsellog.ar_stack = ar_stack(cand_ar_order+1);
ASAsellog.ma_stack = ma_stack(cand_ar_order+1);
ASAsellog.meth_s_stack = meth_s_stack(cand_ar_order+1);

%Footer
%=====================================================

else %Skip the computational kernel
   %Return ASAcontrol as the first output argument
   if nargout>1
      warning(ASAwarn(9,mfilename,ASAcontrol))
   end
   ar = ASAcontrol;
   ASAcontrol = [];
end

%Program history
%======================================================================
%
% Version                Programmer(s)          E-mail address
% -------                -------------          --------------
% former versions        P.M.T. Broersen        broersen@tn.tudelft.nl
% [2000 12 30 20 0 0]         ,,                          ,,

Contact us at files@mathworks.com