https://github.com/gramian/emgr
Raw File
Tip revision: 79c4355b7f064130fbfb08db18c178f9e297a5dd authored by Christian Himpe on 30 September 2023, 09:49:15 UTC
Update README.md
Tip revision: 79c4355
est.m
function [r,m] = est(sys,task,config)
%%% project: emgr - EMpirical GRamian Framework ( https://gramian.de )
%%% version: 5.99 (2022-04-13)
%%% authors: Christian Himpe (0000-0003-2194-6754)
%%% license: BSD-2-Clause (opensource.org/licenses/BSD-2-Clause)
%%% summary: est - empirical system theory (emgr frontend)

    %global ODE;    ODE = [];							% Custom integrator handle
    global STAGES; STAGES = 3;							% Default integrator configuration
    global RANK;   RANK = Inf;							% Maximum rank of decompositions

    persistent WC;
    persistent WO;
    persistent WQ;
    persistent WX;
    persistent FC;
    persistent FO;
    persistent FQ;
    persistent FX;
    persistent GC;
    persistent GO;
    persistent GQ;
    persistent GX;

    sysdim = [sys.M, sys.N, sys.Q];						% System dimension
    tdisc = [sys.dt, sys.Tf];							% Time discretizations

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% DEFAULT VALUES

    pr = hasfield(sys,'p',[]);                              			% Parameters
    nf = zeros(1,13);								% Configuration Flags
    ut = [];									% Training Input
    us = hasfield(sys,'us',zeros(sys.M,1));					% Steady-State Input
    xs = hasfield(sys,'xs',zeros(sys.N,1));					% Steady-State
    um = [];									% Input Perturbation Scales
    xm = [];									% Steady-State Perturbation Scales

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% DECODE CONFIGURATION

    % Choose system integrator
    if isfield(config,'solver')

        if isa(config.solver,'function_handle')

            ODE = config.solver;						% Custom function handle
        else

            switch lower(config.solver)

                case 'rk1ex',   STAGES = 1;					% Explicit 1st Order Runge-Kutta Method (Explicit Euler's Method)

                case 'rk2ex',   STAGES = 2;					% Explicit 2nd Order Runge-Kutta Method (Heun's Method) Optimal Strong Stabilty Preserving, Low-Storage

                case 'rk45ex',  ODE = @rk45ex;					% Adaptive Embedded 4th/5th Order Runge-Kutta Method (Dormand-Prince Method)
            end%switch
        end%if
    end%if

    % Choose gramian kernel
    if isfield(config,'kernel') && isa(config.kernel,'function_handle')

        dp = config.kernel;
    end%if

    gtimes = @(m) m * m';

    dp = match(config,'kernel',[],{'lie'            @(x,y) x*y - y'*x';        % Lie Bracket kernel
                                   'hyp'            @(x,y) x*x' - y*y';        % Hyperbolic SVD kernel
                                   'sum',           @kernel_sum; ...		% Sum Peudo Kernel
                                   'trace',         @kernel_trace; ...	% Trace Peudo Kernel
                                   'diagonal',      @kernel_diagonal; ...	% Diagonal Pseudo Kernel
                                   'dmd',           @dmd; ...			% DMD Pseudo Kernel
                                   'position',      @(x,y) x(1:size(x,1)/2,:) * y(:,1:size(y,2)/2); ...		% Position Pseudo Kernel
                                   'velocity',      @(x,y) x(size(x,1)/2+1:end,:) * y(:,size(y,2)/2+1:end); ...	% Velocity Pseudo Kernel
                                   'quadratic',     @(x,y) (x * y).^2 + 1.0; ...		% Quadratic (Polynomial) Kernel
                                   'cubic',         @(x,y) (x * y).^3 + 1.0; ...		% Cubic (Polynomial) Kernel
                                   'sigmoid',       @(x,y) tanh(x * y - 1.0); ...		% Sigmoid Kernel
                                   'mercersigmoid', @(x,y) tanh(x - 1.0) * tanh(y - 1.0); ...	% Sigmoid-Mercer Kernel
                                   'logarithmic',   @(x,y) log(x + 1.0) * log(y + 1.0); ...	% Logarithmic Kernel
                                   'exponential',   @(x,y) exp(x * y); ...			% Exponential Kernel
                                   'gauss',         @(x,y) exp(-0.5 * gtimes(x-y')); ...	% Gauss Kernel
                                   'single',        @(x,y) single(x) * single(y)});		% Single Precision Kernel

    % Choose training input
    ut = match(config,'training','i',{'impulse', 'i'; ...			% Impulse input
                                      'step',    's'; ...			% Step input
                                      'chirp',   'h'; ...			% Chirp input
                                      'sinc',    'a'; ...			% Sinc input
                                      'random',  'r'});			% Random-binary input

    % Choose trajectory weighting
    nf(13) = match(config,'weighting',0,{'none',      0; ...			% No weighting
                                         'linear',    1; ...			% Linear Time-Weighting
                                         'quadratic', 2; ...			% Quadratic Time-Weighting
                                         'state',     3; ...			% State-Based Weighting
                                         'scale',     4; ...			% Range-Based Weighting
                                         'rsqrt',     5});			% Reciprocal Square-Root Time-Weighting

    % Choose trajectory centering
    nf(1) = match(config,'centering',0,{'none',     0; ...			% No Centering
                                        'steady',   1; ...			% Steady State
                                        'final',    2; ...			% Final State
                                        'mean',     3; ...			% Arithmetic Mean
                                        'rms',      4; ...			% Root-Mean-Squared
                                        'midrange', 5; ...			% Mid-Range
                                        'range'     6;});			% Range-based

    % Choose perturbation scales
    nf([2,3]) = match(config,'scales',0,{'single',      0; ...			% No Subdivision: [1.0]
                                         'linear',      1; ...			% Linear Scale Subdivision: [0.25, 0.5, 0.75, 1.0]
                                         'geometric',   2; ...			% Geometric Scale Subdivision: [0.125, 0.25, 0.5, 1.0]
                                         'logarithmic', 3; ...			% Logarithmic Scale Subdivision: [0.001, 0.01, 0.1, 1.0]
                                         'sparse',      4});			% Sparse Scale Subdivision: [0.01, 0.5, 0.99, 1.0]

    % Choose perturbation rotations
    nf([4,5]) = match(config,'rotations',0,{'posneg', 0; ...			% Positive and negative rotations
                                            'single', 1});			% Only Positive Perturbations

    % Choose gramian normalization
    nf(6) = match(config,'normalize',0,{'none',   0; ...			% No normalization
                                        'steady', 1; ...			% Steady-State Normalization
                                        'jacobi', 2});				% Jacobi Normalization

    % State gramian variant
    nf(7) = match(config,'stype',0,{'standard',                0; ...		% Regular state Gramian
                                    'special' ,                1; ...		% Generic Non-Standard
                                    'output_controllability',  1; ...		% Output Controllabilty Gramian
                                    'averaged_observability',  1; ...		% Averaged Observability Gramian
                                    'nonsymmetric_minimality', 1});		% Nonsymmetric Cross Gramian

    % Extra input for observability and sensitivity
    nf(8) = match(config,'extra_input',0,{'none', 0; ...			% No extra input
                                          'yes',  1});				% Use extra input

    % Choose parameter centering
    nf(9) = match(config,'pcentering',0,{'none',        0; ...			% No Scaling
                                         'linear',      1; ...			% Linear Scaling and Parameter Centering
                                         'logarithmic', 2; ...			% Logarithmic Scaling Parameter Centering
                                         'nominal'      3});			% Linear Scaling and Nominal Parameter Centering

    % Parameter gramian variant
    nf(10) = match(config,'ptype',0,{'standard',       0; ...			% Regular parameter Gramian
                                     'special',        1; ...			% Generic non-standard
                                     'io_sensitivity', 1; ...			% input-output-based sensitivity gramian
                                     'coarse_schur',   1;                      % (cross-)identifiability gramian via coarse schur complement
                                     'exact_schur'     2});			% (cross-)identifiability gramian via exact schur complement

    % Set maximum rank for decompositions
    RANK = hasfield(config,'max_order',Inf);

    % rom_training

    islinear = isfield(config,'linearity') && isequal(config.linearity,'linear');

    if islinear

        f = {sys.f, sys.F, sys.f};
        g = {sys.g, 1, sys.F};
        w = {'c', 'c', 'y'};
    else

        f = {sys.f, sys.f, sys.f};
        g = {sys.g, sys.g, sys.g};
        w = {'c', 'o', 'x'};
    end%if

    switch lower(task.type)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% MATRIX EQUATIONS

        case 'matrix_equation' % OK

            v = match(task,'method',[],{'lyapunov',  1; ...
                                        'sylvester', 3});

            assert(not(isempty(v)),'est: Unknown matrix_equation method');

            r = emgr(f{v},g{v},sysdim,tdisc,w{v},pr,nf,ut,us,xs,um,xm,dp);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% SINGULAR VALUES

        case 'singular_values' % OK

            v = match(task,'method',[],{'controllability', 1; ...
                                        'observability',   2; ...
                                        'minimality',      3});

            assert(not(isempty(v)),'est: Unknown singular_value method');

            r = SVD(emgr(f{v},g{v},sysdim,tdisc,w{v},pr,nf,ut,us,xs,um,xm,dp));

            if hasfield(config,'score',false)

                r = morscore(1:numel(r), r ./ max(r));
            elseif nargout == 2

                m = morscore(1:numel(r), r ./ max(r));
            end%if

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% MODEL REDUCTION

        case 'model_reduction' % OK

            if isequal(task.method,'dmd_galerkin')

                dp = @dmd;
            end%if

            if     (isequal(task.method,'dmd_galerkin') || isequal(task.method,'poor_man')) && isequal(task.variant,'observability')

                W = {emgr(f{2},g{2},sysdim,tdisc,w{2},pr,nf,ut,us,xs,um,xm,dp)};

            elseif (isequal(task.method,'dmd_galerkin') || isequal(task.method,'poor_man'))

                W = {emgr(f{1},g{1},sysdim,tdisc,w{1},pr,nf,ut,us,xs,um,xm,dp)};

            elseif isequal(task.variant,'observability')

                W = {emgr(f{1},g{1},sysdim,tdisc,w{1},pr,nf,ut,us,xs,um,xm,dp), ...
                     emgr(f{2},g{2},sysdim,tdisc,w{2},pr,nf,ut,us,xs,um,xm,dp)};

            elseif isequal(task.variant,'minimality')

                W = {emgr(f{3},g{3},sysdim,tdisc,w{3},pr,nf,ut,us,xs,um,xm,dp)};
            else

                error('est: Unknown model_reduction variant');
            end%if

            reductor = match(task,'method',[],{'poor_man',            @poor_man;  ...
                                               'dmd_galerkin',        @poor_man;  ...
                                               'dominant_subspaces',  @dominant_subspaces; ...
                                               'approx_balancing',    @approx_balancing; ...
                                               'balanced_pod',        @balanced_pod; ...
                                               'balanced_truncation', @balanced_truncation});

            assert(not(isempty(reductor)),'est: Unknown model_reduction method');

            [UX,~,VX] = reductor(W);

            if any(strcmp(hasfield(config,'kernel',''),{'position','velocity'}))

                UX = blkdiag(UX,UX);
                VX = blkdiag(VX,VX);
            end%if

            if hasfield(config,'test',false)

                r = assess(sys,config,UX,VX,1,1);

                if hasfield(config,'score',false)

                    r = morscore(r{1},r{4});
                elseif nargout == 2

                    m = morscore(r{1},r{4});
                end%if
            else

                r = {UX,VX};
            end%if

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% PARAMETER REDUCTION

        case 'parameter_reduction' % OK

            w = match(task,'method',[],{'observability', 'i'; ...
                                        'minimality',    'j'});

            assert(not(isempty(w)),'est: Unknown parameter_reduction method');

            W = emgr(sys.f,sys.g,sysdim,tdisc,w,pr,nf,ut,us,xs,um,xm,dp);

            [UP,~,~] = SVD(W{2});

            if hasfield(config,'test',false)

                r = assess(sys,config,1,1,UP,UP);

                if hasfield(config,'score',false)

                    r = morscore(r{2},r{4});
                elseif nargout == 2

                    m = morscore(r{2},r{4});
                end%if
            else

                r = {UP,UP};
            end%if

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% COMBINED REDUCTION

        case 'combined_reduction' % OK

            if isequal(task.method,'observability')

                W = [emgr(sys.f,sys.g,sysdim,tdisc,'c',pr,nf,ut,us,xs,um,xm,dp), ...
                     emgr(sys.f,sys.g,sysdim,tdisc,'i',pr,nf,ut,us,xs,um,xm,dp)];

            elseif isequal(task.method,'minimality')

                W = emgr(sys.f,sys.g,sysdim,tdisc,'j',pr,nf,ut,us,xs,um,xm,dp);
            else

                error('est: Unknown combined_reduction method');
            end%if

            [UP,~,~] = SVD(W{end});

            reductor = match(task,'variant',[],{'poor_man',            @poor_man;  ...
                                                'dominant_subspaces',  @dominant_subspaces; ...
                                                'approx_balancing',    @approx_balancing; ...
                                                'balanced_pod',        @balanced_pod; ...
                                                'balanced_truncation', @balanced_truncation});

            assert(not(isempty(reductor)),'est: Unknown combined_reduction variant');

            [UX,~,VX] = reductor(W(1:end-1));

            if hasfield(config,'test',false)

                r = assess(sys,config,UX,VX,UP,UP);

                if hasfield(config,'score',false)

                    r = morscore({r{1},r{2}},r{4});
                elseif nargout == 2

                    m = morscore({r{1},r{2}},r{4});
                end%if
            else

                r = {UX, VX; ...
                     UP, UP};
            end%if

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% DECENTRALIZED CONTROL

        case 'decentralized_control' % OK

            nf(7) = 1;

            elem = @(v,k) v(k);

            coher = @(m) trace(m)^2 / sum(sum(m .* m'));

            gtrace = @(m) sum(sum(m .* m'));

            ys = sys.g(xs,us,pr,0);

            if islinear

                eg = @(ui,yj,dp) emgr(@(x,u,p,t) sys.f(x,us + sparse(ui,1,u,sys.M,1),p,t), ...
                                      @(x,u,p,t) sys.F(x,ys + sparse(yj,1,u,sys.Q,1),p,t), ...
                                      [1,sys.N,1],tdisc,'y',pr,nf,ut,[],xs,um,xm,dp);
            else

                eg = @(ui,yj,dp) emgr(@(x,u,p,t) sys.f(x,us + sparse(ui,1,u,sys.M,1),p,t), ...
                                      @(x,u,p,t) elem(sys.g(x,u,p,t),yj), ...
                                      [1,sys.N,1],tdisc,'x',pr,nf,ut,[],xs,um,xm,dp);
            end%if

            em = match(task,'method',[],{'relative_gain_array',  @(ui,yj) eg(ui,yj,@kernel_trace); ...
                                         'io_coherence',         @(ui,yj) coher(eg(ui,yj,[])); ...
                                         'io_pairing',           @(ui,yj) abs(det(eg(ui,yj,[]))); ...
                                         'participation_matrix', @(ui,yj) sqrt(gtrace(eg(ui,yj,[]))); ...
                                         'hardy_2',              @(ui,yj) abs(emgr(@(x,u,p,t) sys.f(x,us + sparse(ui,1,u,sys.M,1),p,t), ...
                                                                                   @(x,u,p,t) elem(sys.g(x,u,p,t),yj), ...
                                                                                   [1,sys.N,1],tdisc,'c',pr,nf,ut,[],xs,um,xm)); ...
                                         'hardy_inf',            @(ui,yj) sum(abs(EIG(eg(ui,yj,[])))); ...
                                         'hankel_interaction',   @(ui,yj) abs(eigs(eg(ui,yj,[]),1)); ...
                                         'rms_hsv',              @(ui,yj) sum(SVD(eg(ui,yj,[])).^4)});

            assert(not(isempty(em)),'est: Unknown decentralized_control method');

            r = arrayfun(em,repmat(1:sys.Q,sys.M,1),repmat((1:sys.M)',1,sys.Q));
            if isequal(task.method,'relative_gain_array'), r = r.*pinv(r)'; end%if
            r = r./max(r(:));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% STATE SENSITIVITY

        case 'state_sensitivity' % OK

            v = match(task,'method',[],{'controllability', 1; ...
                                        'observability',   2; ...
                                        'minimality',      3});

            assert(not(isempty(v)),'est: Unknown state_sensitivity method');

            r = sqrt(abs(emgr(f{v},g{v},sysdim,tdisc,w{v},pr,nf,ut,us,xs,um,xm,@kernel_diagonal)));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% PARAMETER SENSITIVITY

        case 'parameter_sensitivity' % OK

            v = match(task,'method',[],{'controllability', 0; ...
                                        'observability',   0; ...
                                        'minimality',      1});

            assert(not(isempty(v)),'est: Unknown parameter_sensitivity method');

            nf(10) = v;

            nf(7) = match(task,'method',0,{'observability', 1});

            ws = emgr(sys.f,sys.g,sysdim,tdisc,'s',pr,nf,ut,us,xs,um,xm,dp);

            r = ws{2};

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% PARAMETER IDENTIFIABILITY

        case 'parameter_identifiability' % OK

            w = match(task,'method',[],{'observability', 'i'; ...
                                        'minimality',    'j'});

            assert(not(isempty(w)),'est: Unknown parameter_identifiability method');

            wi = emgr(sys.f,sys.g,sysdim,tdisc,w,pr,nf,ut,us,xs,um,xm,dp);

            r = SVD(wi{2});

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% UNCERTAINTY QUANTIFICATION

        case 'uncertainty_quantification' % OK

            v = match(task,'method',0,{'controllability', 0; ...
                                       'observability',   1});

            assert(not(isempty(w)),'est: Unknown uncertainty_quantification method');

            nf(7) = v;

            [UC,SC,~] = SVD(emgr(sys.f,sys.g,sysdim,tdisc,'c',pr,nf,ut,us,xs,um,xm,dp));

            r = SC;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% NONLINEARITY QUANTIFICATION

        case 'nonlinearity_quantification' % OK

            w = match(task,'method',[],{'controllability', 'c'; ...
                                        'observability',   'o'; ...
                                        'minimality',      'x'; ...
                                        'correlation',     '!'});

            assert(not(isempty(w)),'est: Unknown nonlinearity_quantification method');

            if isequal(w,'!')

                rc = est(sys,setfield(task,'method','controllability'),config);
                ro = est(sys,setfield(task,'method','observability'),config);
                rx = est(sys,setfield(task,'method','minimality'),config);

                r = (rx .* rx) ./ (rc .* ro);
            else

                r = arrayfun(@(k) emgr(sys.f,sys.g,sysdim,tdisc,w,pr,nf,ut,us,xs,k,k,@kernel_trace),linspace(1.0,10.0,10));
            end%if

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% GRAMIAN INDEX

        case 'gramian_index' % OK

            xlogx = @(x) x .* log(x);

            inw = match(task,'method',[],{'sigma_min',             @(w) min(SVD(w)); ...
...
                                          'harmonic_mean',         @(w) size(w,1)/sum(1./SVD(w))
...
                                          'geometric_mean',        @(w) prod(SVD(w))^(1.0/size(w,1)); ...
...
                                          'energy_fraction',       @(w) sum(SVD(w)); ...
...
                                          'operator_norm',         @(w) norm(w,'fro'); ...
...
                                          'sigma_max',             @(w) svds(w,1); ...
...
                                          'log_det',               @(w) sum(log(SVD(w))); ...
...
                                          'entropy',               @(w) -1.0./size(w,1) * sum(xlogx(SVD(w))); ...
...
                                          'storage_efficiency',    @(w) sqrt(prod(SVD(w))/prod(diag(w))); ...
...
                                          'unobservability_index', @(w) 1.0./sqrt(min(SVD(w))); ...
...
                                          'performance_index',     @(w) trace(w) * prod(SVD(w))^(1.0/size(w,1))});

            assert(not(isempty(inw)),'est: Unknown gramian_index method');

            switch hasfield(task,'variant','')

                case 'controllability'

                    if isempty(WC) || not(isequal(FC,sys.f)) || not(isequal(GC,sys.g))

                        WC = emgr(f{1},g{1},sysdim,tdisc,w{1},pr,nf,ut,us,xs,um,xm,dp);
                        FC = sys.f;
                        GC = sys.g;
                    end%if

                    W = WC;

                case 'observability'

                    if isempty(WO) || not(isequal(FO,sys.f)) || not(isequal(GO,sys.g))

                        WO = emgr(f{2},g{2},sysdim,tdisc,w{2},pr,nf,ut,us,xs,um,xm,dp);
                        FO = sys.f;
                        GO = sys.g;
                    end%if

                    W = WC;

                case 'minimality'

                    if isempty(WX) || not(isequal(FX,sys.f)) || not(isequal(GX,sys.g))

                        WX = emgr(f{3},g{3},sysdim,tdisc,w{3},pr,nf,ut,us,xs,um,xm,dp);
                        FX = sys.f;
                        GX = sys.g;
                    end%if

                    W = WX;
            end%switch

            r = eps + abs(inw(W) - arrayfun(@(k) inw(sys.proj{2}(:,1:k)'*W*sys.proj{1}(:,1:k)),1:min(sys.N,RANK)));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% SYSTEM INDEX

        case 'system_index' % OK

            inwx = match(task,'method',[],{'cauchy_index',    @(wx) sum(sign(real(EIG(wx)))); ...
...
                                           'system_entropy',  @(wx) size(wx,1)/log(2.0*exp(1.0)*pi) + sum(log(abs(abs(EIG(wx))))); ...
...
                                           'system_symmetry', @(wx) sqrt(abs(sum(sum(wx.*wx'))))/norm(wx,'Fro'); ...
...
                                           'io_coherence',    @(wx) abs(sum(sum(wx.*wx')))/trace(wx)^2; ...
...
                                           'system_gain',     @(wx) abs(trace(wx))});

            if not(isempty(inwx))

                if isempty(WX) || not(isequal(FX,sys.f)) || not(isequal(GX,sys.g))

                    WX = emgr(f{3},g{3},sysdim,tdisc,w{3},pr,nf,ut,us,xs,um,xm,dp);
                    FX = sys.f;
                    GX = sys.g;
                end%if

                r = eps + abs(inwx(WX) - arrayfun(@(k) inwx(sys.proj{2}(:,1:k)'*WX*sys.proj{1}(:,1:k)),1:min(sys.N,RANK)));
                return
            end%if

            inco = match(task,'method',[],{'gramian_distance',            @(wc,wo) norm(log(sqrt(EIG(wc*wo))),2); ...
...
                                           'network_sensitivity',         @(wc,wo) trace(wc) + trace(wo); ...
...
                                           'geometric_mean_hsv',          @(wc,wo) prod(sqrt(EIG(wc*wo)))^(1.0/size(wc,1)); ...
...
                                           'rv_coefficient',              @(wc,wo) sum(sum(wc.*wo))/(norm(wc,'Fro')*norm(wo,'Fro'))});

            if not(isempty(inco))

                if isempty(WC) || not(isequal(FC,sys.f)) || not(isequal(GC,sys.g)) || not(isequal(FO,sys.f)) || not(isequal(GO,sys.g))

                    nf(7) = 0;
                    WC = emgr(f{1},g{1},sysdim,tdisc,w{1},pr,nf,ut,us,xs,um,xm,dp);
                    WO = emgr(f{2},g{2},sysdim,tdisc,w{2},pr,nf,ut,us,xs,um,xm,dp);
                    FC = sys.f;
                    GC = sys.g;
                    FO = sys.f;
                    GO = sys.g;
                end%if

                r = eps + abs(inco(WC,WO) - arrayfun(@(k) inco(sys.proj{2}(:,1:k)'*WC*sys.proj{1}(:,1:k),sys.proj{2}(:,1:k)'*WO*sys.proj{1}(:,1:k)),1:min(sys.N,RANK)));
                return
            end%if

            error('est: Unknown system_index method');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% SYSTEM NORM

        case 'system_norm' % OK

            inoc = match(task,'method',[],{'hardy_2_norm', @(w) sqrt(abs(trace(w)))});

            if not(isempty(inoc))

                if isempty(WQ) || not(isequal(FQ,sys.f)) || not(isequal(GQ,sys.g))

                    nf(7) = 1;
                    WQ = emgr(f{1},g{1},sysdim,tdisc,w{1},pr,nf,ut,us,xs,um,xm,dp);
                    FQ = sys.f;
                    GQ = sys.g;
                end%if

                r = eps + abs(inoc(WQ) - arrayfun(@(k) inoc(emgr(f{1},@(x,u,p,t) g{1}(sys.proj{1}(:,1:k)*(sys.proj{2}(:,1:k)'*x),u,p,t),sysdim,tdisc,w{1},pr,nf,ut,us,xs,um,xm,dp)),1:min(sys.N,RANK))); 
                return
            end%if

            inco = match(task,'method',[],{'hardy_inf_norm',              @(wc,wo) sum(sqrt(EIG(wc*wo))); ...
...
                                           'hilbert_schmidt_hankel_norm', @(wc,wo) norm(wc*wo,'Fro'); ...
...
                                           'hankel_norm',                 @(wc,wo) sqrt(min(EIG(wc*wo)))});

            if not(isempty(inco))

                if isempty(WC) || not(isequal(FC,sys.f)) || not(isequal(GC,sys.g)) || not(isequal(FO,sys.f)) || not(isequal(GO,sys.g))

                    nf(7) = 0;
                    WC = emgr(f{1},g{1},sysdim,tdisc,w{1},pr,nf,ut,us,xs,um,xm,dp);
                    WO = emgr(f{2},g{2},sysdim,tdisc,w{2},pr,nf,ut,us,xs,um,xm,dp);
                    FC = sys.f;
                    GC = sys.g;
                    FO = sys.f;
                    GO = sys.g;
                end%if

                r = eps + abs(inco(WC,WO) - arrayfun(@(k) inco(sys.proj{2}(:,1:k)'*WC*sys.proj{1}(:,1:k),sys.proj{2}(:,1:k)'*WO*sys.proj{1}(:,1:k)),1:min(sys.N,RANK)));
                return
            end%if

            error('est: Unknown system_norm method');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% TAU FUNCTION

        case 'tau_function' % OK

            r = arrayfun(@(k) prod(real(EIG(eye(sys.N) + emgr(f{3},g{3},sysdim,tdisc,w{3},pr,nf,ut,us,xs,um,xm,@(x,y) x(:,k:end)*y(k:end,:))))),1:floor(sys.Tf / sys.dt));
    end%switch

    ODE = [];
    STAGES = [];
    RANK = [];
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% DECOMPOSITION WRAPPER

function varargout = SVD(A)
% summary: svd/svds wrapper

    global RANK;

    switch nargout

        case 1

            if isinf(RANK)
                varargout = {svd(A)};
            else
                varargout = {svds(A,RANK)};
            end%if

        case 2

            if isinf(RANK)
                [U,D,~] = svd(A);
            else
                [U,D,~] = svds(A,RANK);
            end%if
            D = diag(D);
            varargout = {U,D};

        case 3

            if isinf(RANK)
                [U,D,V] = svd(A);
            else
                [U,D,V] = svds(A,RANK);
            end%if
            D = diag(D);
            varargout = {U,D,V};

    end%switch
end

function varargout = EIG(A)
% summary: eig/eigs wrapper

    global RANK;

    switch nargout

        case 1

            if isinf(RANK)
                varargout = {eig(A)};
            else
                varargout = {eigs(A,RANK)};
            end%if

        case 2

            if isinf(RANK)
                [U,D,~] = eig(A,'vector');
            else
                [U,D,~] = eigs(A,RANK);
                D = diag(D);
            end%if
            varargout = {U,D};

    end%switch
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% UTILITIES

function r = hasfield(str,key,def)
% summary: get field key from struct str if exists otherwise return def

    if isfield(str,key)

        r = getfield(str,key);
    else

        r = def;
    end%if
end

function r = match(str,key,def,map)
% summary: return map(ped) values for member key of struct str otherwise def

    s = cell2struct(map(:,2),map(:,1),1);

    if not(isfield(str,key)) || not(isfield(s,getfield(str,key)))

        r = def;
    else

        r = getfield(s,getfield(str,key));
    end%if
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% PSEUDO-KERNELS

function r = kernel_sum(x,y)
% summary: Sum Pseudo-Kernel

    r = sum(sum(x*y));
end

function r = kernel_trace(x,y)
% summary: Trace Pseudo-Kernel

    r = sum(sum(x.*y'));
end

function r = kernel_diagonal(x,y)
% summary: Diagonal Pseudo-Kernel

    r = sum(x.*y',2);
end

function r = dmd(x,y)
% summary: Dynamic-Mode-Decomposition-Galerkin Pseudo Kernel

    r = x(:,2:end) * pinv(y(1:end-1,:)',sqrt(eps));
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% ADAPTIVE RUNGE-KUTTA SOLVER

function y = rk45ex(f,g,t,x0,u,p)
% summary: Adaptive 4th/5th Dormand-Prince Runge-Kutta method

    [S,x] = ode45(@(t,x) f(x,u(t),p,t),[0,t(2)],x0,odeset('InitialStep',t(1)));
    z = cell2mat(arrayfun(@(k) g(x(k,:)',u(S(k)),p,S(k)),1:numel(S),'UniformOutput',false));
    y = interp1(S,z',(0:t(1):t(2)))';
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% MORSCORE (MODEL ORDER REDUCTION SCORE)

function s = morscore(orders,errors)

    if iscell(orders) && all(size(errors)>1)

        nx = orders{1} ./ max(orders{1});
        ny = orders{2} ./ max(orders{2});
        nz = log10(errors + eps) ./ floor(log10(eps));

        s = max(0,trapz(ny(:),trapz(nx(:),nz,2)));
    else

        nx = orders ./ max(orders);
        ny = log10(errors + eps) ./ floor(log10(eps));

        s = max(0,trapz(nx(:),ny(:)));
    end%if
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% REDUCTORS

function [U,D,V] = poor_man(W)
% summary: Poor man's method (pod)

    [U,D,~] = SVD(W{1});
    V = U;
end

function [U,D,V] = dominant_subspaces(W)
% summary: Dominant subspaces

    if isequal(numel(W),1)

        [UX,DX,VX] = SVD(W{1});
        [U,D,~] = SVD([UX.*DX',VX.*DX']);
    else

        [UC,DC,~] = SVD(W{1});
        [UO,DO,~] = SVD(W{2});
        [U,D,~] = SVD([UC.*DC',UO.*DO']);
    end%if

    V = U;
end

function [U,D,V] = approx_balancing(W)
% summary: approximate balancing (modified pod)

    if isequal(numel(W),1)

        [U,DX,VX] = SVD(W{1});
        D = diag(DX);
        V = VX*(VX'*U);
    else

        [U,DC,~] = SVD(W{1});
        [VX,DO,~] = SVD(W{2});
        D = diag(DC)./diag(DO);
        V = VX*(VX'*U);
    end%if
end

function [U,D,V] = balanced_pod(W)
% summary: Balanced pod

    if isequal(numel(W),1)

        [LC,EC] = SVD(W{1});
        [LO,EO] = SVD(W{1}');
    else

        [LC,EC] = SVD(W{1});
        [LO,EO] = SVD(W{2});
    end%if

    LC = LC .* sqrt(abs(EC))';
    LO = LO .* sqrt(abs(EO))';

    [UB,HSV,VB] = svd(LC' * LO,'econ');
    D = sqrt(diag(HSV) + 2.0*eps)';
    U = LC * (UB ./ D);
    V = LO * (VB ./ D);
end

function [U,D,V] = balanced_truncation(W)
% summary: Balanced truncation

    if isequal(numel(W),1)

        [LC,EC] = EIG(W{1});
        [LO,EO] = EIG(W{1}');
    else

        [LC,EC] = EIG(W{1}*W{2});
        [LO,EO] = EIG(W{2}*W{1});
    end%if

    LC = LC .* sqrt(abs(EC))';
    LO = LO .* sqrt(abs(EO))';

    [UB,HSV,VB] = svd(LC' * LO,'econ');
    D = sqrt(diag(HSV) + 2.0*eps)';
    U = LC * (UB ./ D);
    V = LO * (VB ./ D);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% REDUCED ORDER MODEL EVALUATION

function r = assess(sys,config,XL,XR,PL,PR)
% summary:

    global ODE;

    pr = hasfield(sys,'p',0);
    us = hasfield(sys,'us',zeros(sys.M,1));
    xs = hasfield(sys,'xs',zeros(sys.N,1));
    x0 = hasfield(sys,'x0',zeros(sys.N,1));

    rand('seed',1009);
    ur = rand(1,floor(sys.Tf / sys.dt) + 1);
    u = @(t) ur(1,floor(t / sys.dt) + 1);

    skip_x = hasfield(config,'skip_x',1);
    skip_p = hasfield(config,'skip_p',1);
    num_test_param = hasfield(config,'num_test_param',1);

    if isequal(num_test_param,1) || isequal(size(pr,2),1)
        param = pr;
    else
        pmin = min(pr,[],2);
        pmax = max(pr,[],2);
        param = pmin + abs(pmax - pmin) .* rand(size(pr,1),num_test_param);
    end%if

    if isequal(numel(XL),1)
        skip_x = 1;
        max_x = 1;
    else
        max_x = min(size(XL,1)-1,size(XL,2));
    end%if

    if isequal(numel(PL),1)
        skip_p = 1;
        max_p = 1;
    else
        max_p = min(size(PL,1)-1,size(PL,2));
    end%if

    test_x = skip_x:skip_x:max_x;
    test_p = skip_p:skip_p:max_p;

    norms = { @(y) sys.dt * norm(y(:),1), ...					% L1 time series norm
              @(y) sqrt(sys.dt) * norm(y(:),2), ...				% L2 time series norm
              @(y) norm(y(:),Inf), ...						% Linf time series norm
              @(y) sum(abs(prod(y,1).^(1/size(y,1)))) };			% L0 time series norm

    ln = cellfun(@(n) zeros(numel(test_x),numel(test_p)),norms,'UniformOutput',false);

    for q = 1:num_test_param

        Y = ODE(sys.f,sys.g,[sys.dt,sys.Tf],x0,@(t) us + u(t),param(:,q));

        for n = test_x

            xl = XL(:,1:n);
            xr = XR(:,1:n)';

            ix = find(test_x==n);

            for p = test_p

                pl = PL(:,1:p);
                pr = PR(:,1:p)';

                ip = find(test_p==p);

                y = ODE(@(x,u,p,t) xr*sys.f(xs + xl*x,u,p,t), ...
                        @(x,u,p,t)    sys.g(xs + xl*x,u,p,t), ...
                        [sys.dt,sys.Tf], xr*x0, @(t) us + u(t), pl*(pr*param(:,q)));

                for m = 1:numel(norms)

                    ln{m}(ix,ip) = ln{m}(ix,ip) + (norms{m}(Y - y)).^2;
                end%for
            end%for
        end%for
    end%for

    ln = cellfun(@(l) sqrt(l)./sqrt(max(l(:))),ln,'UniformOutput',false);

    r = [{test_x, test_p}, ln];
end
back to top