Skip to content

Commit

Permalink
Merge pull request #11 from ando-lab/EFA_nifty_addition
Browse files Browse the repository at this point in the history
EFA nifty addition
  • Loading branch information
dxu16 authored Sep 6, 2022
2 parents 138ca04 + 5e2e855 commit 68a842f
Show file tree
Hide file tree
Showing 7 changed files with 1,067 additions and 0 deletions.
506 changes: 506 additions & 0 deletions demo/PheH_0phe_SEC.ipynb

Large diffs are not rendered by default.

Binary file added demo/PheH_0phe_SEC.mlx
Binary file not shown.
Binary file added demo/data/PheH_0phe_SEC.mat
Binary file not shown.
202 changes: 202 additions & 0 deletions matlab/EFA.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
classdef EFA
%EFA - Evolving Factor Analysis

properties
I
sigma = []
svd_mode(1,:) char {mustBeMember(svd_mode,{'exact','fast'})} = 'exact'
end
properties(Dependent = true)
Nr
Nc
end

methods
function obj = EFA(I,sigma,varargin)
% EFA

% assign input arguments
obj.I = I;
obj.sigma = sigma;

% assign name, value pairs
for j=1:2:(numel(varargin)-1)
obj.(varargin{j}) = varargin{j+1};
end
end

function val = get.Nr(obj)
val = size(obj.I,1);
end

function val = get.Nc(obj)
val = size(obj.I,2);
end

function sv = evolvingFactors(obj,k,direction,skip)
%evolving_factors

Ncols = obj.Nc;

if nargin < 3 || isempty(direction)
direction = 'forward';
end

if nargin < 4 || isempty(skip)
skip = 1;
end

sv = NaN*ones(k,Ncols);

for j=1:skip:Ncols
switch lower(direction)
case 'forward'
cols = 1:j;
case {'reverse'}
cols = j:Ncols;
end
sv(:,j) = obj.svd(k,cols);
end

end

function sv = svd(obj,k,cols,normalize,subtract)

if nargin < 2 || isempty(k)
k = obj.Nc;
end
if nargin < 3 || isempty(cols)
cols = 1:obj.Nc;
end
if nargin < 4 || isempty(normalize)
normalize = true;
end
if nargin < 5 || isempty(subtract)
subtract = true;
end

sv = NaN*ones(k,1);

% prepare matrix
A = obj.I(:,cols);

% apply sigma weight
if ~isempty(obj.sigma) && size(obj.sigma,1) == obj.Nr
if size(obj.sigma,2) == 1
A = A./obj.sigma;
else
A = A./mean(obj.sigma(:,cols),2);
end
end

if normalize
A = A/sqrt(obj.Nr);
end

% calculate svd
switch obj.svd_mode
case 'exact'
[~,s,~] = svd(A,'econ');
s = diag(s);
s = s(1:min(k,numel(s)));
case 'fast'
s = svds(A,k,'largest','Tolerance',1);
end

% subtract maximum s.v. of Gaussian random matrix
if subtract && normalize
s = s - sqrt(numel(cols)/obj.Nr);
elseif subtract % && ~normalize
s = s - sqrt(numel(cols));
end

sv(1:numel(s)) = s;

end

function [y,c,R] = quickRotate(obj,xstart,xend)
ncomp = numel(xstart);

% do SVD
w = 1./mean(obj.sigma,2);
[u,s,v] = svd(obj.I.*w,'econ');

% get the important components
u = u(:,1:ncomp);
s = s(1:ncomp,1:ncomp);
v = v(:,1:ncomp);

% calculate optimal rotation
R = zeros(ncomp,ncomp);

for n=1:ncomp
m = false(size(v,1),1);
m(ceil(xstart(n)):floor(xend(n))) = true;

vIn = v(m,:); % concentration basis inside peak
vOut = v(not(m),:); % concentration basis outside peak

% set up least squares problem
A = vOut;
B = mean(vIn,1);
AA = A'*A;
BB = B'*B;

% large Lagrange multiplier enforces peak normalization
lambda = 1E6*trace(AA)/trace(BB);

% solve the normal equations
R(:,n) = (AA + lambda*(BB))\(lambda*B'*1);
end

% calculate I = y*c
y = (u*s*pinv(R'))./w;
c = R'*v';
end
end

methods(Static = true)

function [xinfl,xc,slope] = fitInflection(s,direction,threshold,window)
N = size(s,1);
xinfl = NaN*ones(N,1);
xc = NaN*ones(N,1);
slope = NaN*ones(N,1);
x = 1:size(s,2);
for j=1:N
v = s(j,:);
isIncl = ~isnan(v);
xj = x(isIncl);
yj = v(isIncl);
switch lower(direction)
case 'forward'
x0 = xj(find(yj>threshold,1,'first'));
case 'reverse'
x0 = xj(find(yj>threshold,1,'last'));
otherwise
error('unexpected direction');
end
if isempty(x0)
continue;
end
xc(j) = x0;
isWindow = (x0 - window/2) <= xj & (x0 + window/2) >= xj;
npts = nnz(isWindow);
if npts < 2
continue;
end
xw = xj(isWindow);
yw = yj(isWindow);
A = ones(npts,2);
A(:,1) = xw(:) - x0;
c = A\yw(:);
xinfl(j) = x0 + (1-c(2))/c(1);
slope(j) = c(1);
end
xinfl(xinfl<x(1)) = x(1);
xinfl(xinfl>x(end)) = x(end);

end
end
end

123 changes: 123 additions & 0 deletions matlab/Nifty.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
classdef Nifty
properties
q
int
err
dmax
Nr = 50
isZeroAtDmax = true
isZeroAtZero = true
end
properties(Dependent = true)
F
r
dr
k
L
A
Nq
b
end
methods
function obj = Nifty()
end
function val = get.F(obj)
val = 4*pi*obj.dr*sinc(obj.q(:)*obj.r'/pi);
val(:,[1,end]) = 0.5*val(:,[1,end]); % trapezoid rule
end
function val = get.k(obj)
val = obj.Nr - obj.isZeroAtDmax - obj.isZeroAtZero;
end
function val = get.dr(obj)
val = obj.dmax/(obj.Nr-1);
end
function val = get.r(obj)
val = obj.dmax*linspace(0,1,obj.Nr)';
end
function val = get.Nq(obj)
val = length(obj.q);
end
function val = get.L(obj)
val = sparse(1:(obj.Nr-2),1:(obj.Nr-2),-.5,obj.Nr-2,obj.Nr) + ...
sparse(1:(obj.Nr-2),2:(obj.Nr-1),1,obj.Nr-2,obj.Nr) + ...
sparse(1:(obj.Nr-2),3:(obj.Nr),-.5,obj.Nr-2,obj.Nr);
if obj.isZeroAtDmax
val = val(:,1:(end-1));
end
if obj.isZeroAtZero
val = val(:,2:end);
end
end
function val = get.A(obj)
val = obj.F;
if length(obj.err)==1
val = val/obj.err;
elseif isempty(obj.err)
% do nothing
else
val = repmat(1./obj.err(:),1,obj.Nr).*val;
end
if obj.isZeroAtDmax
val = val(:,1:(end-1));
end
if obj.isZeroAtZero
val = val(:,2:end);
end
end
function val = get.b(obj)
val = obj.int(:)./obj.err(:);
end
function [w,alpha] = ift(obj,alpha)
Lmat = obj.L;
Amat = obj.A;
AA = Amat'*Amat;
LL = full(Lmat'*Lmat);
if isempty(alpha)
alpha = trace(AA)/trace(LL);
end
w = (AA + alpha*LL)\(Amat'*obj.b);
if obj.isZeroAtDmax
w = [w;0];
end
if obj.isZeroAtZero
w = [0;w];
end
end

function [y,y0,w0] = make_test_data(obj,sigma)
if nargin==1 || isempty(sigma)
sigma = obj.err;
end
x = obj.r/obj.dmax;
w0 = 1-(2*x-1).^2;
w0 = w0.*x.*exp(-2*x);
w0 = w0 / (4*pi*obj.dr*sum(w0)); % normalize (I(0) = 1)
y0 = obj.F*w0;
if ~isempty(sigma)
y = y0 + randn([obj.Nq,1]).*sigma;
else
y = [];
end
end
end

methods(Static)
function [r,w,ireg,alpha] = sprite(q,int,err,varargin)

N = Nifty();
N.q = q;
N.int = int;
N.err = err;

% assign optional arguments
for j=1:2:length(varargin)
N.(varargin{j}) = varargin{j+1};
end

[w,alpha] = N.ift([]);
r = N.r;
ireg = N.F*w;
end
end
end

Loading

0 comments on commit 68a842f

Please sign in to comment.