Skip to content

Commit

Permalink
Updated with new methods
Browse files Browse the repository at this point in the history
  • Loading branch information
abhranildas committed May 17, 2024
1 parent 07d3325 commit 3b16ed4
Show file tree
Hide file tree
Showing 53 changed files with 1,276 additions and 1,533 deletions.
Binary file modified Generalized chi-square distribution.mltbx
Binary file not shown.
19 changes: 19 additions & 0 deletions Phibar_ray_split.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
function [f_big,f_small]=Phibar_ray_split(z,dim)
% returns the complementary cdf of the standard multinormal on a ray, split into a big chunk, which is
% 0, +/-2 or +/-1, and a small part, which is a tail CDF. This is to
% prevent the small part vanishing when the two are added together due to machine imprecision.

z_c=chi2inv(0.5,dim); % above this criterion, we'll use chi2cdf('upper').
f_big=(z<=-z_c)+(z<z_c); % 2 when z<=-z_c, 1 when -z_c<z<z_c, 0 when z>=z_c

f_small=chi2cdf(z.^2,dim);
f_small_upper=chi2cdf(z.^2,dim,'upper');
f_small(abs(z)>=z_c)=f_small_upper(abs(z)>=z_c);
f_small=f_small.*(1-2*((z<=-z_c)|((z>0)&(z<z_c)))); % if z<=-z_c or 0<z<z_c, invert sign

% if any z is nan (non-existent root),
% prob. contribution is 0.
f_big(isnan(f_big))=0;
f_small(isnan(f_small))=0;

end
Binary file modified doc/GettingStarted.mlx
Binary file not shown.
27 changes: 17 additions & 10 deletions gx2.prj
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
<param.authnamewatermark>Abhranil Das</param.authnamewatermark>
<param.email>abhranil.das@utexas.edu</param.email>
<param.company>The University of Texas at Austin</param.company>
<param.summary>Compute the statistics, pdf, cdf, inverse cdf and random numbers of the generalized chi-square distribution.</param.summary>
<param.summary>Compute the statistics, pdf, cdf, inverse cdf, random numbers and characteristic function of the generalized chi-square distribution.</param.summary>
<param.description>The generalized chi-square variable is a quadratic form of a normal variable, or equivalently, a linear sum of independent non-central chi-square variables and a normal variable. Try the Getting Started guide for a quick demo of all the functions.</param.description>
<param.screenshot>${PROJECT_ROOT}\gx2_icon.png</param.screenshot>
<param.version>1.8.8</param.version>
<param.version>2.0.0</param.version>
<param.output>${PROJECT_ROOT}\Generalized chi-square distribution.mltbx</param.output>
<param.products.name>
<item>Statistics and Machine Learning Toolbox</item>
Expand Down Expand Up @@ -123,24 +123,31 @@ LICENSE
<fileset.rootfiles>
<file>${PROJECT_ROOT}\demos.xml</file>
<file>${PROJECT_ROOT}\doc</file>
<file>${PROJECT_ROOT}\gx2_ellipse.m</file>
<file>${PROJECT_ROOT}\gx2_ifft.m</file>
<file>${PROJECT_ROOT}\gx2_imhof.m</file>
<file>${PROJECT_ROOT}\gx2_imhof_integrand.m</file>
<file>${PROJECT_ROOT}\gx2_ray_integrand.m</file>
<file>${PROJECT_ROOT}\gx2_ruben.m</file>
<file>${PROJECT_ROOT}\gx2_to_norm_quad_params.m</file>
<file>${PROJECT_ROOT}\gx2cdf.m</file>
<file>${PROJECT_ROOT}\gx2cdf_imhof.m</file>
<file>${PROJECT_ROOT}\gx2cdf_imhof_integrand.m</file>
<file>${PROJECT_ROOT}\gx2cdf_pearson.m</file>
<file>${PROJECT_ROOT}\gx2cdf_ray.m</file>
<file>${PROJECT_ROOT}\gx2cdf_ruben.m</file>
<file>${PROJECT_ROOT}\gx2char.m</file>
<file>${PROJECT_ROOT}\gx2inv.m</file>
<file>${PROJECT_ROOT}\gx2pdf.asv</file>
<file>${PROJECT_ROOT}\gx2pdf.m</file>
<file>${PROJECT_ROOT}\gx2pdf_conv.m</file>
<file>${PROJECT_ROOT}\gx2pdf_ifft.m</file>
<file>${PROJECT_ROOT}\gx2pdf_imhof.m</file>
<file>${PROJECT_ROOT}\gx2pdf_imhof_integrand.m</file>
<file>${PROJECT_ROOT}\gx2pdf_ray_fast.m</file>
<file>${PROJECT_ROOT}\gx2pdf_ray.m</file>
<file>${PROJECT_ROOT}\gx2rnd.m</file>
<file>${PROJECT_ROOT}\gx2stat.m</file>
<file>${PROJECT_ROOT}\int_norm_ray.m</file>
<file>${PROJECT_ROOT}\log_gx2cdf.m</file>
<file>${PROJECT_ROOT}\norm_prob_across_angles.m</file>
<file>${PROJECT_ROOT}\norm_prob_across_rays.m</file>
<file>${PROJECT_ROOT}\norm_quad_to_gx2_params.m</file>
<file>${PROJECT_ROOT}\phi_ray.m</file>
<file>${PROJECT_ROOT}\Phibar_ray_split.m</file>
<file>${PROJECT_ROOT}\standard_quad.m</file>
</fileset.rootfiles>
<fileset.depfun.included />
<fileset.depfun.excluded />
Expand Down
104 changes: 104 additions & 0 deletions gx2_ellipse.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
function [p,p_rel_err]=gx2_ellipse(x,w,r,lambda,m,varargin)

parser=inputParser;
parser.KeepUnmatched=true;
addRequired(parser,'x',@(x) isreal(x));
addRequired(parser,'w',@(x) isreal(x) && isrow(x) && (all(x>0)||all(x<0)) );
addRequired(parser,'k',@(x) isreal(x) && isrow(x));
addRequired(parser,'lambda',@(x) isreal(x) && isrow(x));
addRequired(parser,'m',@(x) isreal(x) && isscalar(x));
addOptional(parser,'side','lower',@(x) strcmpi(x,'lower') || strcmpi(x,'upper') );
addParameter(parser,'output','cdf',@(x) strcmpi(x,'cdf') || strcmpi(x,'pdf') );
addParameter(parser,'x_scale','linear',@(x) strcmpi(x,'linear') || strcmpi(x,'log') );

parse(parser,x,w,r,lambda,m,varargin{:});
side=parser.Results.side;
x_scale=parser.Results.x_scale;
w_pos=true;

% flatten x:
x_flat=x(:);

if all(w<0)
w=-w; m=-m; w_pos=false;
if strcmpi(x_scale,'linear')
x_flat=-x_flat;
end
end

% find ellipse parameters
ellipse_center=arrayfun(@(lambda,k) [sqrt(lambda) zeros(1,k-1)],lambda,r,'un',0);
ellipse_center=[ellipse_center{:}];
ellipse_weights=arrayfun(@(w,k) w*ones(1,k),w,r,'un',0);
ellipse_weights=[ellipse_weights{:}];

dim=sum(r);
% factor corr. to the density of the chosen point:
% if norm(ellipse_center) % if non-central
% point_factor=exp(-norm(ellipse_center)^2/2);
% else % if central
% point_factor=exp(-x_flat/(4*min(ellipse_weights)));
% end

% common factor to cdf and pdf:
% a=point_factor/(2^(dim/2)*gamma(dim/2+1)*sqrt(prod(ellipse_weights)));
a=exp(-norm(ellipse_center)^2/2)/(2^(dim/2)*gamma(dim/2+1)*sqrt(prod(ellipse_weights)));
% ellipse_vol=x_flat.^(dim/2)*pi^(dim/2)/(gamma(dim/2+1)*sqrt(prod(ellipse_weights)));

if strcmpi(x_scale,'linear')
if strcmpi(parser.Results.output,'cdf')
% p=mvnpdf(ellipse_center)*ellipse_vol;
p=a.*(x_flat-m).^(dim/2);

% flip if necessary
if (w_pos && strcmpi(side,'upper')) || (~w_pos && strcmpi(side,'lower'))
p=1-p;
end
elseif strcmpi(parser.Results.output,'pdf')
p=(a*dim/2)*(x_flat-m).^(dim/2-1);
end

% compute log p for log x
else
log10_x=x_flat;
% factor corr. to the density of the chosen point:
% if norm(ellipse_center) % if non-central
% point_factor=norm(ellipse_center)^2;
% else % if central
% point_factor=-10.^log10_x/(2*min(ellipse_weights));
% end

if strcmpi(parser.Results.output,'cdf')
log10_p=dim/2*(log10_x-log10(2)) - norm(ellipse_center)^2/log(100) - log10(gamma(dim/2+1)) - sum(log10(ellipse_weights))/2;
elseif strcmpi(parser.Results.output,'pdf')
log10_p=(dim/2-1)*log10_x-(dim/2+1)*log10(2) +log10(dim) - norm(ellipse_center)^2/log(100) - log10(gamma(dim/2+1)) - sum(log10(ellipse_weights))/2;
end
p=log10_p;
end

% compute the lower and upper bounds for the estimate

if norm(ellipse_center) % if non-central
if strcmpi(x_scale,'linear')
r=sqrt(x_flat-m)/sqrt(sum(ellipse_center.^2.*ellipse_weights));
p_rel_err=norm(ellipse_center)^2*r;
% compute log (rel err) for log x
else
p_rel_err=2*log10(norm(ellipse_center))+log10_x/2-log10(sum(ellipse_center.^2.*ellipse_weights))/2;
end
else % if central
p_rel_err=(x_flat-m)/2*min(ellipse_weights);
% compute log (rel err) for log x
if strcmpi(x_scale,'log')
p_rel_err=log10_x-log10(2*min(ellipse_weights));
end
end

% p_rel_err(x_flat<0)=log10_p_rel_err;

% p_rel_err_neg=1-exp((norm(ellipse_center)^2-vecnorm(rect_far,2,2).^2)/2);
% p_rel_err_pos=exp((norm(ellipse_center)^2-vecnorm(rect_near,2,2).^2)/2)-1;

% reshape outputs to input shape
p=reshape(p,size(x));
p_rel_err=reshape(p_rel_err,size(x));
85 changes: 85 additions & 0 deletions gx2_ifft.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
function [p,xgrid]=gx2_ifft(x,w,k,lambda,s,m,varargin)

parser=inputParser;
parser.KeepUnmatched=true;
addRequired(parser,'x',@(x) isreal(x) || strcmpi(x,'full'));
addRequired(parser,'w',@(x) isreal(x) && isrow(x));
addRequired(parser,'k',@(x) isreal(x) && isrow(x));
addRequired(parser,'lambda',@(x) isreal(x) && isrow(x));
addRequired(parser,'s',@(x) isreal(x) && isscalar(x));
addRequired(parser,'m',@(x) isreal(x) && isscalar(x));
addOptional(parser,'side','lower',@(x) strcmpi(x,'lower') || strcmpi(x,'upper') );
addParameter(parser,'output','cdf',@(x) strcmpi(x,'cdf') || strcmpi(x,'pdf') );
% range of x over which to compute ifft:
if strcmpi(x,'full')
% default span is upto 100 sd from mean
[mu,v]=gx2stat(w,k,lambda,s,m);
span=max(abs(mu+[-1 1]*100*sqrt(v)));
else
% default span is 100* input span
span=100*max(abs(x(:)));
end
addParameter(parser,'span',span,@(x) isscalar(x) && (x>0));
% number of grid points for ifft
addParameter(parser,'n_grid',1e6+1,@(x) isscalar(x) && (x>0));
addParameter(parser,'ft_type','cft');
parse(parser,x,w,k,lambda,s,m,varargin{:});
span=parser.Results.span;
n_grid=round(parser.Results.n_grid);

% make n_grid odd
if ~mod(n_grid,2)
n_grid=n_grid+1;
end

n=(n_grid-1)/2;
idx=-n:n;
dx=span/n;

xgrid=idx*dx; % grid of x over which this cdf/pdf is computed

if strcmpi(parser.Results.ft_type,'dft')
% compute non-central and normal pdf's over this span
w_idx=find(w); % non-zero w indices
ncpdfs=nan(nnz(w),length(xgrid));
for i=1:nnz(w)
if i==1 % add the offset m to the first term
pdf=gx2pdf(xgrid,w(w_idx(i)),k(w_idx(i)),lambda(w_idx(i)),0,m);
else
pdf=gx2pdf(xgrid,w(w_idx(i)),k(w_idx(i)),lambda(w_idx(i)),0,0);
end
pdf(isinf(pdf))=max(pdf(~isinf(pdf)));
ncpdfs(i,:)=pdf;
end
if s
ncpdfs=[ncpdfs;normpdf(xgrid,0,abs(s))];
end
phi=prod(fft(ifftshift(ncpdfs,2),[],2),1);
p=fftshift(ifft(phi));

p=p/(sum(p)*dx); % normalize
elseif strcmpi(parser.Results.ft_type,'cft')
dt=2*pi/(n_grid*dx);
t=idx*dt; % grid of points to evaluate characteristic function
phi=gx2char(-t,w,k,lambda,s,m);

if strcmpi(parser.Results.output,'pdf')
p=fftshift(ifft(ifftshift(phi)))/dx;
elseif strcmpi(parser.Results.output,'cdf')
phi=phi./(1i*t);
phi(isinf(phi))=0;
p=0.5+fftshift(ifft(ifftshift(phi)))/dx;
end
end

if strcmpi(parser.Results.output,'cdf') && strcmpi(parser.Results.side,'upper')
p=1-p;
end


if ~strcmpi(x,'full')
F=griddedInterpolant(xgrid,p);
p=F(x);
end

% f=max(f,0);
51 changes: 51 additions & 0 deletions gx2_imhof.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
function [p,errflag]=gx2_imhof(x,w,k,lambda,s,m,varargin)

parser=inputParser;
parser.KeepUnmatched=true;
addRequired(parser,'x',@(x) isreal(x));
addRequired(parser,'w',@(x) isreal(x) && isrow(x));
addRequired(parser,'k',@(x) isreal(x) && isrow(x));
addRequired(parser,'lambda',@(x) isreal(x) && isrow(x));
addRequired(parser,'s',@(x) isreal(x) && isscalar(x));
addRequired(parser,'m',@(x) isreal(x) && isscalar(x));
addOptional(parser,'side','lower',@(x) strcmpi(x,'lower') || strcmpi(x,'upper') );
addParameter(parser,'output','cdf',@(x) strcmpi(x,'cdf') || strcmpi(x,'pdf') );
addParameter(parser,'vpa',false,@islogical);
addParameter(parser,'AbsTol',1e-10,@(x) isreal(x) && isscalar(x) && (x>=0));
addParameter(parser,'RelTol',1e-2,@(x) isreal(x) && isscalar(x) && (x>=0));

parse(parser,x,w,k,lambda,s,m,varargin{:});
output=parser.Results.output;
side=parser.Results.side;
vpaflag=parser.Results.vpa;
AbsTol=parser.Results.AbsTol;
RelTol=parser.Results.RelTol;

% compute the integral
if ~vpaflag
imhof_integral=arrayfun(@(x) integral(@(u) gx2_imhof_integrand(u,x,w',k',lambda',s,m,output),0,inf,'AbsTol',AbsTol,'RelTol',RelTol),x);
else
syms u
imhof_integral=arrayfun(@(x) vpaintegral(@(u) gx2_imhof_integrand(u,x,w',k',lambda',s,m,output),...
u,0,inf,'AbsTol',AbsTol,'RelTol',RelTol),x);
end

if strcmpi(output,'cdf')
if strcmpi(side,'lower')
p=double(0.5-imhof_integral/pi);
elseif strcmpi(side,'upper')
p=double(0.5+imhof_integral/pi);
end
errflag = p<0 | p>1;
p=min(p,1);
elseif strcmpi(output,'pdf')
p=imhof_integral/(2*pi);
errflag = p<0;
end

if any(errflag)
p=max(p,0);
warning('Imhof method output(s) too close to limit to compute exactly, so clipping. Check the flag output, and try stricter tolerances.')
end

end
10 changes: 10 additions & 0 deletions gx2_imhof_integrand.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
function f=gx2_imhof_integrand(u,x,w,k,lambda,s,m,output)
% define the Imhof integrand (w, k, lambda must be column vectors here)
theta=sum(k.*atan(w*u)+(lambda.*(w*u))./(1+w.^2*u.^2),1)/2+u*(m-x)/2;
rho=prod(((1+w.^2*u.^2).^(k/4)).*exp(((w.^2*u.^2).*lambda)./(2*(1+w.^2*u.^2))),1) .* exp(u.^2*s^2/8);
if strcmpi(output,'cdf')
f=sin(theta)./(u.*rho);
elseif strcmpi(output,'pdf')
f=cos(theta)./rho;
end
end
Loading

0 comments on commit 3b16ed4

Please sign in to comment.