-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCME_Fit.jl
More file actions
144 lines (101 loc) · 3.48 KB
/
Copy pathCME_Fit.jl
File metadata and controls
144 lines (101 loc) · 3.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# Fits Compositional data to the CME model using L2 regularization gamma (gamma=0 in the associated manuscript)
using Statistics, Optim, SpecialFunctions, LinearAlgebra, LineSearches, Zygote
using IrrationalConstants:twoπ,halfπ,sqrtπ,sqrt2π,invπ,inv2π,invsqrt2,invsqrt2π,logtwo,logπ,log2π
import ChainRulesCore
import SpecialFunctions.logerfc
logerfc(x::Real) = _logerfc(float(x))
function _logerfc(x::Real)
if x> 0.0
return log(erfcx(x)) - x^2
else
return log(erfc(x))
end
end
ChainRulesCore.@scalar_rule(erf(x, y), (- (2 * exp(-x^2)) / sqrtπ, (2 * exp(-y^2)) / sqrtπ))
ChainRulesCore.@scalar_rule(logerfc(x), - (2 * exp(-x^2 - Ω)) / sqrtπ)
ChainRulesCore.@scalar_rule(logerf(x,y), (- (2 * exp(-x^2 - Ω)) / sqrtπ, (2 * exp(-y^2 - Ω)) / sqrtπ))
function CME_Fit(Data,gamma)
# Inputs and Outputs
#=
Here the input Data is an NxD matrix
N is the number of variables of interest, D is the number of samples
The sum of the N variables (for each sample) needs to be normalized
to 1
The output Params is an N x N-1 matrix of maximum entropy parameters
=#
# Optimization
#removing the redundant Nth variable
sz=size(Data);
Data=Data[1:sz[1]-1,:];
M=mean(Data,dims=2);
Chi=Data*Data';
Chi=Chi/sz[2];
Params=zeros(Float64,sz[1],sz[1]-1);
Threads.@threads for i in 1:sz[1]-1
L=copy(Data);
L[i,:].=1.0;
CC=copy([Chi[:,i];Chi[i,i]]);
CC[i]=copy(M[i]);
ic=zeros(Float64,1,sz[1]);
V=Chi[i,i]-M[i]^2;
ic[i]=M[i]/V-1/M[i];
ic[end]=-1/(V);
c=2 .-sum(L,dims=1);
obj(Par)=LogPseudo(Par,CC,L,c,gamma,i);
function g!(G,x)
G.=obj'(x)
end
res=optimize(obj,g!, ic,LBFGS(; m=5, linesearch=BackTracking(order=3))); #slow lin
count=0;
temp=Optim.minimizer(res)
temp=temp[1]
while isnan(temp)&&count<20
count+=1;
ic=ic+mean(ic)/5*(rand(Float64, 1,sz[1])-0.5*ones(Float64,1,sz[1]));
res=optimize(obj,g!, ic,LBFGS(; m=5, linesearch=BackTracking(order=3)));
temp=Optim.minimizer(res)
temp=temp[1]
end
Params[:,i]=Optim.minimizer(res);
Params[end,i]=2*Params[end,i];
end
P=zeros(Float64,sz[1],sz[1]);
h=[diag(Params); 0];
P[1:sz[1]-1,1:sz[1]-1]=Params[1:sz[1]-1,1:sz[1]-1];
for i in 1:sz[1]-1
P[i,i]=Params[sz[1],i];
end
Params=zeros(Float64,sz[1],sz[1]);
for i in 1:sz[1]
for j in 1:sz[1]
Params[i,j]=P[i,j]+P[j,i]-P[i,i]-P[j,j];
end
end
K=Params./2;
h=h.-K[1:sz[1],sz[1]];
return K,h
end
function LogPseudo(Param,Cons,Data,c,gamma,ii)
PP=copy(Param[end]);
PPP=copy([Param[1:1:ii-1]; Param[ii+1:1:end-1];0]);
PPP=PPP.-2*copy(PP); #to compensate for the ignored 1/2 term in the probability distribution
if PP<0
b=Param[1:1,1:end-1]*Data;
c=2*sqrt(-PP)^2*c;
c=b-c;
c=c/(2*sqrt(-PP));
b=b/(2*sqrt(-PP));
c=logerf.(c,b); #slow line
D=length(c);
return -1*dot(Param,Cons)-.5*log(-4*PP/pi)+sum(b.^2)/D+sum(c)/D+gamma*norm(PPP)^2
else
b=Param[1:1,1:end-1]*Data;
c=2*sqrt(PP)^2*c;
c=c+b;
c=c/(2*sqrt(PP));
b=b/(2*sqrt(PP));
c=log.(erfi.(c)-erfi.(b));
D=length(c);
return -1*dot(Param,Cons)-.5*log(4*PP/pi)-sum(b.^2)/D+sum(c)/D+gamma*norm(PPP)^2
end
end