% Detailed description of the factor data structure and related functions
% -----------------------------------------------------------------------
% We will use structures to implement the factor datatype. The code
%
% phi = struct('var', [3 1 2], 'card', [2 2 2], 'val', ones(1, 8));
%
% creates a factor over variables X_3, X_1, X_2, which are all binary
% valued, because phi.card(1) (the cardinality of X_3, |Val(X_3)|) is 2,
% and likewise for X_1 and X_2. phi has been initialized so that
% phi(X_3, X_1, X_2) = 1 for any assignment to the variables.
%
% A factor's values are stored in a row vector in the .val field
% using an ordering such that the left-most variables as defined in the
% .var field cycle through their values the fastest. More concretely, for
% the factor phi defined above, we have the following mapping from variable
% assignments to the index of the row vector in the .val field:
%
% -+-----+-----+-----+-------------------+
% | X_3 | X_1 | X_2 | phi(X_3, X_1, X_2)|
% -+-----+-----+-----+-------------------+
% | 1 | 1 | 1 | phi.val(1) |
% -+-----+-----+-----+-------------------+
% | 2 | 1 | 1 | phi.val(2) |
% -+-----+-----+-----+-------------------+
% | 1 | 2 | 1 | phi.val(3) |
% -+-----+-----+-----+-------------------+
% | 2 | 2 | 1 | phi.val(4) |
% -+-----+-----+-----+-------------------+
% | 1 | 1 | 2 | phi.val(5) |
% -+-----+-----+-----+-------------------+
% | 2 | 1 | 2 | phi.val(6) |
% -+-----+-----+-----+-------------------+
% | 1 | 2 | 2 | phi.val(7) |
% -+-----+-----+-----+-------------------+
% | 2 | 2 | 2 | phi.val(8) |
% -+-----+-----+-----+-------------------+
%
%
% We have provided the AssignmentToIndex and IndexToAssignment functions
% that compute the mapping between the assignments A and the variable indices I,
% given D, the cardinality of the variables. Concretely, given a factor phi, if
% phi.val(I) corresponds to the assignment A, i.e. phi(X = A) = phi.val(I) then
%
% I = AssignmentToIndex(A, D)
% A = IndexToAssignment(I, D)
%
% For instance, for the factor phi as defined above, with the assignment
%
% A = [2 1 2]
%
% to X_3, X_1 and X_2 respectively (as defined by phi.var = [3 1 2]), I = 6
% as phi.val(6) corresponds to the value of phi(X_3 = 2, X_1 = 1, X_2 = 2).
% Thus, AssignmentToIndex([2 1 2], [2 2 2]) returns 6, and conversely,
% IndexToAssignment(6, [2 2 2]) returns the vector [2 1 2]. The second
% argument in the function calls corresponds to the cardinality of the
% sample factor phi, phi.card, which is [2 2 2].
%
% More generally, the assignment vector A is a row vector that corresponds
% to assignments to the variables in a factor, with an understanding that the
% variables for which the assignments refer to are given by the .var field
% of the factor.
%
% Giving AssignmentToIndex a matrix A, one assignment per row, will cause it
% to return a vector of indices I, such that I(k) is the index
% corresponding to the assignment in A(k, :) (row k).
%
% Similarly, giving IndexToAssignment a vector I of indices will yield a
% matrix A of assignments, one per row, such that A(k, :) (the kth row of A)
% corresponds to the assignment mapped to by index I(k).
%
% Getting and setting values to factors
% -------------------------------------
%
% We have provided the convenience functions GetValueOfAssignment and
% SetValueOfAssignment so you do not need to manipulate the .val field
% directly for getting and setting values.
%
% For instance, calling
%
% GetValueOfAssignment(phi, [1 2 1])
%
% yields the value phi(X_3 = 1, X_1 = 2, X_2 = 1). Again, the variables for
% which the assignments refer to are given by the .var field of the factor.
%
% Similarly, executing
%
% phi = SetValueOfAssignment(phi, [2 2 1], 6)
%
% causes the value of phi(X_3 = 2, X_1 = 2, X_2 = 1) to be set to 6. Note
% that because MATLAB/Octave passes function arguments by value (not by
% reference), SetValueOfAssignment *does not modify* the factor that you
% passed in. Instead, it returns a new factor with the modified value for
% the specified assignment; this is why we reassigned phi to the result of
% SetValueOfAssignment.
%
% More details about these functions are provided in their respective .m
% files.
% Sample Factors and Outputs
% --------------------------
%
% In the following, we have provided you with some sample input factors, as
% well as the output that you should receive when various operations are
% performed on these factors. You may find these sample inputs and outputs
% helpful in debugging your implementation. For instance, FACTORS.PRODUCT
% is the factor you should get when you execute
%
% FactorProduct(FACTORS.INPUT(1), FACTORS.INPUT(2))
%
% These sample factors define a simple chain Bayesian network over binary
% variables: X_1 -> X_2 -> X_3
%
% FACTORS.INPUT(1) contains P(X_1)
FACTORS.INPUT(1) = struct('var', [1], 'card', [2], 'val', [0.11, 0.89]);
% FACTORS.INPUT(2) contains P(X_2 | X_1)
FACTORS.INPUT(2) = struct('var', [2, 1], 'card', [2, 2], 'val', [0.59, 0.41, 0.22, 0.78]);
% FACTORS.INPUT(3) contains P(X_3 | X_2)
FACTORS.INPUT(3) = struct('var', [3, 2], 'card', [2, 2], 'val', [0.39, 0.61, 0.06, 0.94]);
% Factor Product 对两个表做并运算,以两表都存在的变量为依据,相应行概率做乘积
% FACTORS.PRODUCT = FactorProduct(FACTORS.INPUT(1), FACTORS.INPUT(2));
% The factor defined here is correct to 4 decimal places.
FACTORS.PRODUCT = struct('var', [1, 2], 'card', [2, 2], 'val', [0.0649, 0.1958, 0.0451, 0.6942]);
% Factor Marginalization 删除一个表的某些变量,剩下变量有重复值的,把重复值概率相加
% FACTORS.MARGINALIZATION = FactorMarginalization(FACTORS.INPUT(2), [2]);
FACTORS.MARGINALIZATION = struct('var', [1], 'card', [2], 'val', [1 1]);
% Observe Evidence 一个变量的值为已知,和已知值不等的行,概率置0
% FACTORS.EVIDENCE = ObserveEvidence(FACTORS.INPUT, [2 1; 3 2]);
FACTORS.EVIDENCE(1) = struct('var', [1], 'card', [2], 'val', [0.11, 0.89]);
FACTORS.EVIDENCE(2) = struct('var', [2, 1], 'card', [2, 2], 'val', [0.59, 0, 0.22, 0]);
FACTORS.EVIDENCE(3) = struct('var', [3, 2], 'card', [2, 2], 'val', [0, 0.61, 0, 0]);
% Compute Joint Distribution 重复利用上面的FactorProduct()做Joint,注意传入参数长度的判断
% FACTORS.JOINT = ComputeJointDistribution(FACTORS.INPUT);
FACTORS.JOINT = struct('var', [1, 2, 3], 'card', [2, 2, 2], 'val', [0.025311, 0.076362, 0.002706, 0.041652, 0.039589, 0.119438, 0.042394, 0.652548]);
% Compute Marginal 如果有evidence,做完后re-normalize。Joint,非保留的marginalization,re-normalize.
% FACTORS.MARGINAL = ComputeMarginal([2, 3], FACTORS.INPUT, [1, 2]);
FACTORS.MARGINAL = struct('var', [2, 3], 'card', [2, 2], 'val', [0.0858, 0.0468, 0.1342, 0.7332]);