% This is a small script to compute the Bethe approximation of a small
% factor graph. The factor graph is generated by taking the Bayes net of
% Lauritzen and Spiegelhalter (1988) and setting the following random
% variables as evidence
%
%   visit to Asia  = false
%   positive X-ray = true
%   smoking        = true
%
% For this particular set of evidence, the Bethe approximation is exact
% because the corresponding factor graph does not have any cycles.
%
% For more information, see: Lauritzen and Speigelhalter (1988). "Local
% computations with probabilities on graphical structures and their
% application to expert systems." Journal of the Royal Statistical
% Society, Series B, Vol. 50, No. 2, pp. 157-194.
%
% Copyright (C) 2007 Peter Carbonetto. All Rights Reserved.
% This code is published under the Common Public License.
%
% Author: Peter Carbonetto
%         Dept. of Computer Science
%         University of British Columbia
%         May 19, 2007

verbose = true;

% The remaining random variables can either be true or false. They are:
%   1. tuberculosis
%   2. lung cancer
%   3. bronchitis
%   4. tuberculosis or lung cancer
%   5. dysponoea
nv = 5;             % The number of random variables.
nf = 6;             % The number of factors.
K  = 2*ones(nv,1);  % The number of possible discrete assignments to the 
                    % random variable at each site. 
C = cell(nf,1);     % The factor neighbourhoods.
f = cell(nf,1);     % The factors.

% Set up the factors.
% ------------------
% This is p(tuberculosis | visit to Asia = false).
C{1} = 1;
f{1} = [ 0.01 0.99 ]';

% This is p(lung cancer | smoking = true).
C{2} = 2;
f{2} = [ 0.1 0.9 ]';

% This is p(bronchitis | smoking = true).
C{3} = 3;
f{3} = [ 0.6 0.4 ]';

% This is p(tuberculosis or lung cancer | tuberculosis, lung cancer).
C{4}     = [4 1 2];
p        = zeros(2,2,2);
p(:,1,1) = [ 0.99 0.01 ]';
p(:,1,2) = [ 0.99 0.01 ]';
p(:,2,1) = [ 0.99 0.01 ]';
p(:,2,2) = [ 0.99 0.01 ]';
f{4}     = p;

% This is p(positive X-ray = true | tuberculosis or lung cancer).
C{5} = 4;
f{5} = [ 0.98 0.5 ]';

% This is p(dyspnoea | tuberculosis or lung cancer, bronchitis).
C{6}     = [5 4 3];
p        = zeros(2,2,2);
p(:,1,1) = [ 0.9 0.1 ]';
p(:,1,2) = [ 0.7 0.3 ]';
p(:,2,1) = [ 0.8 0.2 ]';
p(:,2,2) = [ 0.1 0.9 ]';
f{6}     = p;
clear p

% Set up the junction graph.
% -------------------------
% These are the large regions of the junction graph.
Rv{1} = [1 2 4];
Rf{1} = [1 2 4];
NR{1} = [1];

Rv{2} = [3 4 5];
Rf{2} = [3 5 6];
NR{2} = [1];

% These are the small regions of the junction graph.
Sv{1} = [4];
Sf{1} = [];
NS{1} = [1 2];

nr = length(Rv);  % The number of large regions.
ns = length(Sv);  % The number of small regions.

% Infer the region marginals.
% --------------------------
% Compute the junction graph approximation to the variational free
% energy. Since the junction graph is a tree in this case (trivially,
% because there are only two large regions), the junction graph
% approximation is exact.
qR = bopt(K,C,f,Rv,Rf,Sv,Sf,NS,verbose);

% Output the marginal probabilities.
% ---------------------------------
p = ndsum(qR{1},[2 3]);
fprintf('Pr(tuberculosis | evidence) = %0.2f \n', p(1));

p = ndsum(qR{1},[1 3]);
fprintf('Pr(lung cancer | evidence)  = %0.2f \n', p(1));

p = ndsum(qR{2},[2 3]);
fprintf('Pr(bronchitis | evidence)   = %0.2f \n', p(1));

p = ndsum(qR{2},[1 2]);
fprintf('Pr(dysponoea | evidence)    = %0.2f \n', p(1));
