about summary refs log tree commit diff
path: root/sourcecodes/bnt-master/SLP/scoring/score_family.m
blob: 085fcf48db25d219ab5baa526f57fac68ea60988 (plain)
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
function [score, cache] = score_family(j, ps, node_type, scoring_fn, ns, discrete, data, args, cache)
% SCORE_FAMILY_COMPLETE Compute the score of a node and its parents given completely observed data
% score = score_family(j, ps, node_type, scoring_fn, ns, discrete, data, args, cache)
%
% data(i,m) is the value of node i in case m (can be a cell array, if contain missing value, it uses only available complete cases for an entry)
% args is a cell array containing optional arguments passed to the constructor,
% or is [] if none
% cache is a data structure used to memorize local score computations
% (cf. SCORE_INIT_CACHE function)
%
% We create a whole Bayes net which only connects parents to node,
% where node has a CPD of the specified type (with default parameters).
% We then evaluate its score ('bic' or 'bayesian')
% We should use a cache to avoid unnecessary computation.
% In particular, log_marginal_prob_node for tabular CPDs calls gammaln
% and compute_counts, both of which are slow.
%
% (Caching implementation : ofrancois.olivier.c.h@gmail.com, philippe.leray@univ-nantes.fr)
%

if (nargin<9 || isempty(cache)) , c=0; else c=1; end
%tic
if c==1
    [b,score]=score_find_in_cache(cache,j,ps,scoring_fn);
else
    b=0;
end
%Tfind=toc
ccc = iscell(data);
ps = unique(ps);

if b==0
    misv = -9999;
    if ccc, data = bnt_to_mat(data,misv); end
    [n ncases] = size(data);
    dag = zeros(n,n);
    % SML added to sort ps b/c mk_bnet, learn_params use sorted ps to make
    % CPTs    % Kevin had: if ~isempty(ps), dag(ps, j) = 1; end
    if ~isempty(ps), dag(ps, j) = 1;, ps = sort(ps);, end

    bnet = mk_bnet(dag, ns, 'discrete', discrete);
    fname = sprintf('%s_CPD', node_type);
    if isempty(args)
        bnet.CPD{j} = feval(fname, bnet, j);
    else
        bnet.CPD{j} = feval(fname, bnet, j, args{:});
    end
    %tic
    switch scoring_fn
    case 'bic',
        fam = [ps j];
        if ccc,
	    [tmp, available_case] = find(data(fam,:)==misv);
	    available_case = mysetdiff(1:ncases, available_case);
	else available_case = 1:ncases;
	end
        bnet.CPD{j} = learn_params(bnet.CPD{j}, fam, data(:,available_case), ns, bnet.cnodes);
        %L = log_prob_node(bnet.CPD{j}, data(j,:), data(ps,:));
	L = log_prob_node(bnet.CPD{j}, data(j,available_case), data(ps,available_case));
        S = struct(bnet.CPD{j}); % violate object privacy
        score = L - 0.5*S.nparams*log(length(available_case));
    case 'bicmod',
        fam = [ps j];
        if ccc,
	    [tmp, available_case] = find(data(fam,:)==misv);
	    available_case = mysetdiff(1:ncases, available_case);
	else available_case = 1:ncases;
	end
        bnet.CPD{j} = learn_params(bnet.CPD{j}, fam, data(:,available_case), ns, bnet.cnodes);
	L = log_prob_node(bnet.CPD{j}, data(j,available_case), data(ps,available_case));
        S = struct(bnet.CPD{j}); % violate object privacy
        score = L - S.nparams*log(length(available_case));
    case 'bayesian',
        fam = [ps j];
        if ccc,
	    [tmp, available_case] = find(data(fam,:)==misv);
	    available_case = mysetdiff(1:ncases, available_case);
	else available_case = 1:ncases;
	end
        score = log_marg_prob_node(bnet.CPD{j}, data(j,available_case), data(ps,available_case));
    otherwise,
        error(['unrecognized scoring fn ' scoring_fn]);
    end
    %Tcalc=toc
    %tic
    if c==1
%        fprintf('a\n')
        cache=score_add_to_cache(cache,j,ps,score,scoring_fn);
    end
    %Tecr=toc
% else
%     fprintf('*\n')
end

%===========================Inner functions

function [cache, place] = score_add_to_cache(cache,j,ps,score,scoring_fn)
% [cache place] = score_add_to_cache(cache,j,ps,score,scoring_fn)
%
% j is the son node,
% ps is the list of parents of j, for example [12 5 7],
% score is the score to add for this familly.
% scoring_fn is 'bic' or 'bayesian'.
%
% place = where the entry was add.
%
% example for 2 nodes with cache of size 5 :
%
% cache =
%   Nw  b        0      0      0 --> Nw=number of writings in cache (+1) and b==1 iff the cache is full
%   0   0        1   -239.12   1  --> 1st familly in the cache (node 1 without parents) calculate with bic
%   0   0        2   -318.98   1
%   1   0        2   -189.23   2  --> 3rd familly in the cache (node 2 with 1 as parent) calculate with bay�sian
%   0   1        1   -251.09   1
% .ps2bool.      j    score  1or2 --> new entry
%   |   |        |      |      |
%   |   |        |      |      |___> scoring function : 1 for 'bic' or 2 for 'bayesian'
%   |   |        |      |__________> score of the familly
%   |   |        |_________________> son node of the familly
%   |   |__________________________> ==1 iff node 2 is parent of son node
%   |______________________________> ==1 iff node 1 is parent of son node
%
% If the cache is FULL then the new place is RanDomly choose.
%
% V1.1 : 5 may 2003 (O. Francois, Ph. Leray)

N=size(cache,2)-3;
L=size(cache,1)-1;
cache_full=cache(1,2) ;

place=0;

if ismember(j,ps)
    disp('This is a cyclic entry, nothing was done.');
elseif j>N || j<=0
    disp('This entry is not valid, nothing was done.');
else
    switch scoring_fn
    case 'bic',
        fn=1;
    case 'bayesian',
        fn=2;
    otherwise,
        fn=3;
        %error(['unrecognized scoring fn ' scoring_fn]);
    end

    if ~cache_full
        place=cache(1,1);
    else
    [ignore place]=max(rand(1,L)); place=place+1;
    end

    cache(place,:)=0;
    cache(place,ps)=1;
    cache(place,N+1)=j;
    cache(place,N+2)=score;
    cache(place,N+3)=fn;

    cache(1,1)=place+1;
    if place>L || cache(1,2)~=0
        cache(1,2)=1;
    end
end


%=========================================================================================
function [bool, score] = score_find_in_cache(cache,j,ps,scoring_fn)
% cache = score_find_in_cache(cache,j,ps,scoring_fn)
%
% V1.1 : 5 may 2003 (O. Francois, Ph. Leray)


%tic
L=size(cache,1)-1;
N=size(cache,2)-3;

if N<1
    bool=0;
    score=0;
    return
end

parents=zeros(1,N);
parents(ps)=1;
%parents(N+1)=j;

switch scoring_fn
case 'bic',
    fn=1;
case 'bayesian',
    fn=2;
otherwise,
    fn=3;
    %error(['unrecognized scoring fn ' scoring_fn]);
end

tmp=find(cache(2:L+1,N+3)==fn);
tmp=tmp+1;
tmp2=find(cache(tmp,N+1)==j);
candidats=tmp(tmp2);

i=1;
while i<=N & ~isempty(candidats)
    tmp=find(cache(candidats,i)==parents(i));
    candidats=candidats(tmp);
    i=i+1;
end

%Tpre=toc

bool=~isempty(candidats);

if bool
    score=cache(candidats(1),N+2);
else
    score=0;
end