Code covered by the BSD License  

Highlights from
emgr - Empirical Gramian Framework

image thumbnail
from emgr - Empirical Gramian Framework by Christian
A framework for computating empirical gramians. Compatible with Octave and Matlab.

emgr(f,g,q,p,t,w,nf,ut,us,xs,um,xm,yd)
function W = emgr(f,g,q,p,t,w,nf,ut,us,xs,um,xm,yd)
% emgr - Empirical Gramian Framework (Version 1.1)
% by Christian Himpe, 2013 ( http://gramian.de )
% released under BSD 2-Clause License ( http://gramian.de/#license )
%
% SYNOPSIS:
%	W = emgr(f,g,q,p,t,w,[nf],[ut],[us],[xs],[um],[xm],[yd]);
%
% ABOUT:
%	emgr - Empirical Gramian Framemwork, computating empirical gramians
%	for model reduction and system identification.
%	Compatible with Octave and Matlab.
%
% INPUTS:
%		  (func)  f - system function handle, signature: xdot = f(x,u,p)
%		  (func)  g - output function handle, signature: y = g(x,u,p)
%		(vector)  q - system dimensions [inputs,states,outputs]
%		(vector)  p - parameters, 0 if none
%		(vector)  t - time [start,step,stop]
%		  (char)  w - gramian type:
%			* 'C' or 'c' : empirical controllability gramian (WC)
%			* 'O' or 'o' : empirical observability gramian (WO)
%			* 'X' or 'x' : empirical cross gramian (WX / WCO)
%			* 'S' or 's' : empirical sensitivity gramian (WS)
%			* 'I' or 'i' : empirical identifiability gramian (WI)
%			* 'J' or 'j' : empirical joint gramian (WJ)
%		(vector) [nf = 0] - options, 10 components
%			+ residualize against zero(0), average(1), last(2), steady-state(3), pca(4)
%			+ unit-normal(0), pca(1) directions
%			+ linear(0), log(1), rombergseq(2), single(3) input scale spacing
%			+ linear(0), log(1), rombergseq(2), single(3) init-state scale spacing
%			+ unit(0), [factorial(1)], single(3) rotations of input directions
%			+ unit(0), [factorial(1)], single(3) rotations of init-state directions
%			+ single(0), double(0) run
%			+ disable(0), enable(1)
%				* robust parameters (WC, WS only)
%				* data-driven pca (WO, WI only)
%				* less scales (WX, WJ only)
%			+ disable(0), enable(1) data-driven gramians
%			+ euler-method-1(0), adams-bashforth-2(1)
%  (matrix,vector,scalar) [ut = 1] - input; default: delta impulse
%         (vector,scalar) [us = 0] - steady-state input
%         (vector,scalar) [xs = 0] - steady-state
%  (matrix,vector,scalar) [um = 1] - input scales
%  (matrix,vector,scalar) [xm = 1] - init-state scales
%                (matrix) [yd = 0] - observed data
%
% OUTPUT:
%	        (matrix)  W - Gramian matrix (WC, WO, WX only)
%	          (cell)  W - {State,Parameter} Gramian Matrices (WS, WI, WJ only)
%
% TODO:
% 	factorial transformations
%
% For further information see http://gramian.de
%*

global x;		%Make x available to ode1x
global o;		%Make y available to ode1y

J = q(1);		%Number of Inputs
N = q(2);		%Number of States
O = q(3);		%Number of Outputs
p = p(:);		%Ensure parameters are in column vector
P = numel(p);		%Number of parameters
h = t(2);		%Time step
T = (t(3)-t(1))/h;	%Number of time steps
w = lower(w);		%Force lower case gramian type

if (nargin<7) ||(isempty(nf)) nf = 0; end;		%If options empty, set to zero
if (nargin<8) ||(isempty(ut)) ut = 1; end;		%If input empty, set to one
if (nargin<9) ||(isempty(us)) us = 0; end;		%If steady-state input empty, set to zero
if (nargin<10)||(isempty(xs)) xs = 0; end;		%If steady-state empty, set to zero
if (nargin<11)||(isempty(um)) um = 1; end; 	%If input scales empty, set to one
if (nargin<12)||(isempty(xm)) xm = 1; end; 	%If state scales empty, set to one
if (nargin<13)||(isempty(yd)) yd = cell(2,1); end;

if (numel(nf)<10) nf(10) = 0; end;		%If options scalar, extend to vector
if (numel(ut)==1) ut = ones(J,1)*ut; end;		%If input scalar, extend to vector
if (numel(us)==1) us = ones(J,1)*us; end;		%If steady-state input scalar, extend to vector
if (numel(xs)==1) xs = ones(N,1)*xs; end;		%If steady-state scalar, extend to vector
if (numel(um)==1) um = ones(J,1)*um; end;		%If input scales scalar, extend to vector
if (numel(xm)==1) xm = ones(N,1)*xm; end;		%If state scales scalar, extend to vector

if(w=='c' || w=='o' || w=='x')
	if(w=='c'&&nf(8)~=0)
		J = J+P;
		if(size(um,1)==J) um = [um;ones(P,1)]; end;
		F = f; f = @(x,u,p) F(x,u(1:J),u(J+1:J+P));
		G = g; g = @(x,u,p) G(x,u(1:J),u(J+1:J+P));
	end;

	if(size(um,2)==1) um = scales(um,nf(3),nf(5),w=='x'&&nf(8)==0); end;	%Generate Scales
	if(size(xm,2)==1) xm = scales(xm,nf(4),nf(6),w=='x'&&nf(8)==0); end;	%Generate Scales
	C = size(um,2);								%Number input scales
	D = size(xm,2);								%Number state scales

	if(size(us,2)==1) us = us*ones(1,T); end;					%Expand input steady state for each step
	if(size(ut,2)==1) k  = (1/h); ut = [ut,zeros(J,T-1)]; else k = 1; end;	%TODO ut sparse when octave compat

	if(nf(1)==4)&&(w=='o')&&(nf(8)~=0) nf(1) = 5; end;
	if(nf(2)==1)&&(w~='o') dx = pod(ut);                     else dx = 0; end;	%Set input directions
	if(nf(2)==1)&&(w~='c') dy = pod(ode1x(f,N,h,T,xs,us,p)); else dy = 0; end;	%Set state directions

	o = cell(N,1);	%Preallocate array of outputs
	x = zeros(N,T);	%Preallocate array pf states
	W = zeros(N,N);	%Preallocate gramian

	switch(nf(1))
		case 0
			X = f(zeros(N,1),zeros(J,1),p);
			Y = g(zeros(N,1),zeros(J,1),p);
		case 3
			X = f(xs,us(:,1),p);
			Y = g(xs,us(:,1),p);
		case 5
			X = 0;
			Y = pod(yd);
		otherwise
			X = 0;
			Y = 0;
	end;

	if(nf(7)==1)	%Double Run
		nf(7) = 0;
		A = emgr(f,g,q,p,t,w,nf,ut,us,xs,um,xm,yd);
		A = sqrt(diag(A));
		B = diag(1./A);
		A = diag(A);

		F = f;
		G = g;
		f = @(x,u,p) A*F(B*x,u,p);
		g = @(x,u,p)   G(B*x,u,p);
	end;

	sn = size(yd,1)==1;
end;

switch(w)												%Switch by gramian type
	case 'c'												%Type: controllability gramian
		for c=1:C										%For all input scales
			for j=1:J									%For all input components
				uu = us + bsxfun(@times,ut,dirs(j,J,dx)*(um(j,c)*k));			%Set up input
				if(nf(9)==0) odex(f,h,T,xs,uu,p,nf(10)); else x = yd{1,c}; end;		%Simulate (nonlinear) system
				x = bsxfun(@minus,x,steady(nf(1),x,X))*(1.0/um(j,c));			%Subtract scaled steady state
				W = W + x*x';								%Vectorized sum of dyadic products x(:,t)'*x(:,t) for all t
			end;
		end;
		W = W*(h/C);										%Symmetrize and normalize by number of scales
	case 'o'												%Type: observability gramian
		for d=1:D										%For all scales
			for n=1:N									%For all state components
				xx = xs + dirs(n,N,dy)*xm(n,d);						%Set up initial value
				if(nf(9)==0) odey(f,g,h,T,xx,us,p,n,nf(10)); else o{n} = yd{2-sn,d}; end;	%Simulate (nonlinear) system
				o{n} = bsxfun(@minus,o{n},steady(nf(1),o{n},Y))*(1.0/xm(n,d));		%Subtract scaled steady state
			end;
			for n=1:N									%For each row
				for m=1:N								%For each column
					W(n,m) = W(n,m) + o{n}(:)'*o{m}(:);				%Vectorized dot product of o{n}(:,t)*o{m}(:,t)' for all t
				end;
			end;
		end;
		W = W*(h/D);										%Symmetrize and normalize by number of scales
	case 'x'												%Type: cross gramian
		if(J~=O) error('ERROR: non-square system!'); end;						%error if non square
		for d=1:D										%For all state scales
			for n=1:N 									%For all state components
				xx = xs + dirs(n,N,dy)*xm(n,d);						%Set up initial value
				if(nf(9)==0) odey(f,g,h,T,xx,us,p,n,nf(10)); else o{n} = yd{2,d}; end;	%Simulate (nonlinear) system
				o{n} = bsxfun(@minus,o{n},steady(nf(1),o{n},Y))*(1.0/xm(n,d));		%Subtract scaled steady state
			end;
			for c=1:C									%For all input scales
				for j=1:J								%For all input components
					uu = us + bsxfun(@times,ut,dirs(j,J,dx)*(um(j,c)*k));		%Set up input
					if(nf(9)==0) odex(f,h,T,xs,uu,p,nf(10)); else x = yd{1,c}; end;	%Simulate (nonlinear) system
					x = bsxfun(@minus,x,steady(nf(1),x,X))*(1.0/um(j,c));		%Subtract scaled steady state
					for m=1:N							%For each column
						W(:,m) = W(:,m) + x*o{m}(j,:)';				%Sum product of control and observe components
					end;
				end;
			end;
		end;
		W = W*(h/(C*D));								%Symmetrize and normalize by number of scales
	case 's'										%Type: sensitivity gramian
		W = cell(2,1);								%Allocate return type
		W{1} = emgr(f,g,[J N O],zeros(P,1),t,'c',nf,ut,us,xs,um,xm); 		%Compute parameterless controllability gramian
		W{2} = eye(P);								%Allocate return type
		F = @(x,u,p) f(x,zeros(J,1),p*u);						%Augment system function with constant parameter input
		G = @(x,u,p) g(x,zeros(J,1),p*u);						%Adapt Output function
		for q=1:P								%For each parameter
			V = emgr(F,G,[1 N O],(1:P==q)*p(q),t,'c',nf,0,p(q),xs,1,xm);	%Compute parameter controllability Gramian
			W{2}(q,q) = trace(V);						%Trace of current parameter controllability gramian
			W{1} = W{1} + V;							%Accumulate controllability gramian
		end;
	case 'i'										%Type: identifiability gramian
		if(size(xm,1)==N) xm = [xm;ones(P,1)]; end;				%Augment state scales
		W = cell(2,1);								%Allocate return type
		F = @(x,u,p) [f(x(1:N),u,x(N+1:N+P));zeros(P,1)];				%Augment system function with constant parameter states
		G = @(x,u,p)  g(x(1:N),u,x(N+1:N+P));					%Adapt Output function
		V = emgr(F,G,[J N+P O],0,t,'o',nf,ut,us,[xs;p],um,xm); 			%Compute Observability Gramian of augmented system
		W{1} = V(1:N,1:N);							%Extract Observability gramian
		V = chol(V+speye(N+P));							%Use shortcut to compute schur complement of augmented with ensured positive definiteness
		V = V(N+1:N+P,N+1:N+P);							%Extract parameter observability gramian using cholesky factors
		W{2} = V'*V - speye(P);	 						%Compute identifiability gramian
	case 'j'										%Type: joint gramian
		if(size(um,1)==J) um = [um;ones(P,1)]; end;				%Augment input scales
		if(size(xm,1)==N) xm = [xm;ones(P,1)]; end;				%Augment state scales
		up = [p+(p==0),zeros(P,size(ut,2)-1)];
		W = cell(2,1);								%Allocate return type
		F = @(x,u,p) [f(x(1:N),u(1:J),x(N+1:N+P));u(J+1:J+P)];			%Augment system function with constant parameter states
		G = @(x,u,p) [g(x(1:N),u(1:J),x(N+1:N+P));x(J+1:J+P)];			%Adapt Output function
		V = emgr(F,G,[J+P N+P O+P],0,t,'x',nf,[ut;up],[us;zeros(P,1)],[xs;p],um,xm);%Compute Cross Gramian of double augmented system
		S = norm(V,1);								%Bound largest eigenvalue
		W{1} = V(1:N,1:N);							%Extract Cross gramian
		V = chol(V+S*speye(N+P));							%Use shortcut to compute schur complement of augmented wit ensured positive definiteness
		V = V(N+1:N+P,N+1:N+P);							%Extract parameter cross gramian using cholesky factors
		W{2} = V'*V - S*speye(P);							%Compute cross identifiability gramian
	otherwise
		error('ERROR: unknown gramian type!');
end;

if(w=='c' || w=='o' || w=='x') W = 0.5*(W+W'); end; 	%Enforce Symmetry

end	%end emgr

%********

function d = dirs(n,N,e)

	switch(e)
		case 0    %Unit-Normal
			d = (1:N==n)';
		otherwise %PCA
			d = e;
	end;

end	%end dirs

%********

function s = scales(s,d,e,f)

	switch(d)
		case 0 %Linear
			if(f) s = s*[1/2,1];   else s = s*[1/4,1/2,3/4,1]; end;
		case 1 %Logarithmic
			if(f) s = s*[1/100,1]; else s = s*[1/1000,1/100,1/10,1]; end;
		case 2 %Romberg Sequence
			if(f) s = s*[1/4,1];   else s = s*[1/8,1/4,1/2,1]; end;
		case 3 %Single
			%s = s;
	end;

	switch(e)
		case 0 %Unit
			s = [-s,s];
		case 1 %Factorial
			s = s*(2*(dec2bin(0:2^q-1)-'0')'-1)./sqrt(2^q);	%not beautiful but working
		case 2 %Ideas: All, Dyadic

		case 3 %Single
			%s = s;
	end;

end	%end scales

%********

function y = steady(v,d,e)

	switch(v)
		case 0 %Zero
			y = e;
		case 1 %Average
			y = mean(d,2);
		case 2 %Last
			y = d(end,:);
		case 3 %Steady State
			y = e;
		case 4 %PCA
			y = pod(d);
		case 5 %Data PCA
			y = e;
	end;

end	%end steady

%********

function p = pod(y)

	p = svd(cov(bsxfun(@minus,y,mean(y,2))'),'econ');

end	%end pod

%********

function odex(f,h,T,z,u,p,q)

global x;

switch(q)
	case 0 %Eulers Method
		for t=1:T
			z = z + h*f(z,u(:,t),p);
			x(:,t) = z;
		end;
	case 1 %Adams-Bashforth Method
		m = h*f(z + 0.5*h*f(z,u(:,1),p),u(:,1),p);
		z = z + m;
		x(:,1) = z;
		m = 0.5*m;

		for t=2:T
			k = 0.5*h*f(z,u(:,t),p);
			z = z + 3.0*k-m;
			x(:,t) = z;
			m = k;
		end;
	end;

end	%end odex

%********

function odey(f,g,h,T,z,u,p,n,q)

global o;

switch(q)
	case 0 %Eulers Method
		for t=1:T
			z = z + h*f(z,u(:,t),p);
			o{n}(:,t) = g(z,u(:,t),p);
		end;
	case 1 %Adams-Bashforth Method
		m = h*f(z + 0.5*h*f(z,u(:,1),p),u(:,1),p);
		z = z + m;
		o{n}(:,1) = g(z,u(:,1),p);
		m = 0.5*m;

		for t=2:T
			k = 0.5*h*f(z,u(:,t),p);
			z = z + 3.0*k-m;
			o{n}(:,t) = g(z,u(:,t),p);
			m = k;
		end;
	end;

end	%end odey

Contact us