wolffd@0
|
1 function CPD = boolean_CPD(bnet, self, ftype, fname, pfail)
|
wolffd@0
|
2 % BOOLEAN_CPD Make a tabular CPD representing a (noisy) boolean function
|
wolffd@0
|
3 %
|
wolffd@0
|
4 % CPD = boolean_cpd(bnet, self, 'inline', f) uses the inline function f
|
wolffd@0
|
5 % to specify the CPT.
|
wolffd@0
|
6 % e.g., suppose X4 = X2 AND (NOT X3). Then we can write
|
wolffd@0
|
7 % bnet.CPD{4} = boolean_CPD(bnet, 4, 'inline', inline('(x(1) & ~x(2)'));
|
wolffd@0
|
8 % Note that x(1) refers pvals(1) = X2, and x(2) refers to pvals(2)=X3.
|
wolffd@0
|
9 %
|
wolffd@0
|
10 % CPD = boolean_cpd(bnet, self, 'named', f) assumes f is a function name.
|
wolffd@0
|
11 % f can be built-in to matlab, or a file.
|
wolffd@0
|
12 % e.g., If X4 = X2 AND X3, we can write
|
wolffd@0
|
13 % bnet.CPD{4} = boolean_CPD(bnet, 4, 'named', 'and');
|
wolffd@0
|
14 % e.g., If X4 = X2 OR X3, we can write
|
wolffd@0
|
15 % bnet.CPD{4} = boolean_CPD(bnet, 4, 'named', 'any');
|
wolffd@0
|
16 %
|
wolffd@0
|
17 % CPD = boolean_cpd(bnet, self, 'rnd') makes a random non-redundant bool fn.
|
wolffd@0
|
18 %
|
wolffd@0
|
19 % CPD = boolean_CPD(bnet, self, 'inline'/'named', f, pfail)
|
wolffd@0
|
20 % will put probability mass 1-pfail on f(parents), and put pfail on the other value.
|
wolffd@0
|
21 % This is useful for simulating noisy boolean functions.
|
wolffd@0
|
22 % If pfail is omitted, it is set to 0.
|
wolffd@0
|
23 % (Note that adding noise to a random (non-redundant) boolean function just creates a different
|
wolffd@0
|
24 % (potentially redundant) random boolean function.)
|
wolffd@0
|
25 %
|
wolffd@0
|
26 % Note: This cannot be used to simulate a noisy-OR gate.
|
wolffd@0
|
27 % Example: suppose C has parents A and B, and the
|
wolffd@0
|
28 % link of A->C fails with prob pA and the link B->C fails with pB.
|
wolffd@0
|
29 % Then the noisy-OR gate defines the following distribution
|
wolffd@0
|
30 %
|
wolffd@0
|
31 % A B P(C=0)
|
wolffd@0
|
32 % 0 0 1.0
|
wolffd@0
|
33 % 1 0 pA
|
wolffd@0
|
34 % 0 1 pB
|
wolffd@0
|
35 % 1 1 pA * PB
|
wolffd@0
|
36 %
|
wolffd@0
|
37 % By contrast, boolean_CPD(bnet, C, 'any', p) would define
|
wolffd@0
|
38 %
|
wolffd@0
|
39 % A B P(C=0)
|
wolffd@0
|
40 % 0 0 1-p
|
wolffd@0
|
41 % 1 0 p
|
wolffd@0
|
42 % 0 1 p
|
wolffd@0
|
43 % 1 1 p
|
wolffd@0
|
44
|
wolffd@0
|
45
|
wolffd@0
|
46 if nargin==0
|
wolffd@0
|
47 % This occurs if we are trying to load an object from a file.
|
wolffd@0
|
48 CPD = tabular_CPD(bnet, self);
|
wolffd@0
|
49 return;
|
wolffd@0
|
50 elseif isa(bnet, 'boolean_CPD')
|
wolffd@0
|
51 % This might occur if we are copying an object.
|
wolffd@0
|
52 CPD = bnet;
|
wolffd@0
|
53 return;
|
wolffd@0
|
54 end
|
wolffd@0
|
55
|
wolffd@0
|
56 if nargin < 5, pfail = 0; end
|
wolffd@0
|
57
|
wolffd@0
|
58 ps = parents(bnet.dag, self);
|
wolffd@0
|
59 ns = bnet.node_sizes;
|
wolffd@0
|
60 psizes = ns(ps);
|
wolffd@0
|
61 self_size = ns(self);
|
wolffd@0
|
62
|
wolffd@0
|
63 psucc = 1-pfail;
|
wolffd@0
|
64
|
wolffd@0
|
65 k = length(ps);
|
wolffd@0
|
66 switch ftype
|
wolffd@0
|
67 case 'inline', f = eval_bool_fn(fname, k);
|
wolffd@0
|
68 case 'named', f = eval_bool_fn(fname, k);
|
wolffd@0
|
69 case 'rnd', f = mk_rnd_bool_fn(k);
|
wolffd@0
|
70 otherwise, error(['unknown function type ' ftype]);
|
wolffd@0
|
71 end
|
wolffd@0
|
72
|
wolffd@0
|
73 CPT = zeros(prod(psizes), self_size);
|
wolffd@0
|
74 ndx = find(f==0);
|
wolffd@0
|
75 CPT(ndx, 1) = psucc;
|
wolffd@0
|
76 CPT(ndx, 2) = pfail;
|
wolffd@0
|
77 ndx = find(f==1);
|
wolffd@0
|
78 CPT(ndx, 2) = psucc;
|
wolffd@0
|
79 CPT(ndx, 1) = pfail;
|
wolffd@0
|
80 if k > 0
|
wolffd@0
|
81 CPT = reshape(CPT, [psizes self_size]);
|
wolffd@0
|
82 end
|
wolffd@0
|
83
|
wolffd@0
|
84 clamp = 1;
|
wolffd@0
|
85 CPD = tabular_CPD(bnet, self, CPT, [], clamp);
|
wolffd@0
|
86
|
wolffd@0
|
87
|
wolffd@0
|
88
|
wolffd@0
|
89 %%%%%%%%%%%%
|
wolffd@0
|
90
|
wolffd@0
|
91 function f = eval_bool_fn(fname, n)
|
wolffd@0
|
92 % EVAL_BOOL_FN Evaluate a boolean function on all bit vectors of length n
|
wolffd@0
|
93 % f = eval_bool_fn(fname, n)
|
wolffd@0
|
94 %
|
wolffd@0
|
95 % e.g. f = eval_bool_fn(inline('x(1) & x(3)'), 3)
|
wolffd@0
|
96 % returns 0 0 0 0 0 1 0 1
|
wolffd@0
|
97
|
wolffd@0
|
98 ns = 2*ones(1, n);
|
wolffd@0
|
99 f = zeros(1, 2^n);
|
wolffd@0
|
100 bits = ind2subv(ns, 1:2^n);
|
wolffd@0
|
101 for i=1:2^n
|
wolffd@0
|
102 f(i) = feval(fname, bits(i,:)-1);
|
wolffd@0
|
103 end
|
wolffd@0
|
104
|
wolffd@0
|
105 %%%%%%%%%%%%%%%
|
wolffd@0
|
106
|
wolffd@0
|
107 function f = mk_rnd_bool_fn(n)
|
wolffd@0
|
108 % MK_RND_BOOL_FN Make a random bit vector of length n that encodes a non-redundant boolean function
|
wolffd@0
|
109 % f = mk_rnd_bool_fn(n)
|
wolffd@0
|
110
|
wolffd@0
|
111 red = 1;
|
wolffd@0
|
112 while red
|
wolffd@0
|
113 f = sample_discrete([0.5 0.5], 2^n, 1)-1;
|
wolffd@0
|
114 red = redundant_bool_fn(f);
|
wolffd@0
|
115 end
|
wolffd@0
|
116
|
wolffd@0
|
117 %%%%%%%%
|
wolffd@0
|
118
|
wolffd@0
|
119
|
wolffd@0
|
120 function red = redundant_bool_fn(f)
|
wolffd@0
|
121 % REDUNDANT_BOOL_FN Does a boolean function depend on all its input values?
|
wolffd@0
|
122 % r = redundant_bool_fn(f)
|
wolffd@0
|
123 %
|
wolffd@0
|
124 % f is a vector of length 2^n, representing the output for each bit vector.
|
wolffd@0
|
125 % An input is redundant if there is no assignment to the other bits
|
wolffd@0
|
126 % which changes the output e.g., input 1 is redundant if u(2:n) s.t.,
|
wolffd@0
|
127 % f([0 u(2:n)]) <> f([1 u(2:n)]).
|
wolffd@0
|
128 % A function is redundant it it has any redundant inputs.
|
wolffd@0
|
129
|
wolffd@0
|
130 n = log2(length(f));
|
wolffd@0
|
131 ns = 2*ones(1,n);
|
wolffd@0
|
132 red = 0;
|
wolffd@0
|
133 for i=1:n
|
wolffd@0
|
134 ens = ns;
|
wolffd@0
|
135 ens(i) = 1;
|
wolffd@0
|
136 U = ind2subv(ens, 1:2^(n-1));
|
wolffd@0
|
137 U(:,i) = 1;
|
wolffd@0
|
138 f1 = f(subv2ind(ns, U));
|
wolffd@0
|
139 U(:,i) = 2;
|
wolffd@0
|
140 f2 = f(subv2ind(ns, U));
|
wolffd@0
|
141 if isequal(f1, f2)
|
wolffd@0
|
142 red = 1;
|
wolffd@0
|
143 return;
|
wolffd@0
|
144 end
|
wolffd@0
|
145 end
|
wolffd@0
|
146
|
wolffd@0
|
147
|
wolffd@0
|
148 %%%%%%%%%%
|
wolffd@0
|
149
|
wolffd@0
|
150 function [b, iter] = rnd_truth_table(N)
|
wolffd@0
|
151 % RND_TRUTH_TABLE Construct the output of a random truth table s.t. each input is non-redundant
|
wolffd@0
|
152 % b = rnd_truth_table(N)
|
wolffd@0
|
153 %
|
wolffd@0
|
154 % N is the number of inputs.
|
wolffd@0
|
155 % b is a random bit string of length N, representing the output of the truth table.
|
wolffd@0
|
156 % Non-redundant means that, for each input position k,
|
wolffd@0
|
157 % there are at least two bit patterns, u and v, that differ only in the k'th position,
|
wolffd@0
|
158 % s.t., f(u) ~= f(v), where f is the function represented by b.
|
wolffd@0
|
159 % We use rejection sampling to ensure non-redundancy.
|
wolffd@0
|
160 %
|
wolffd@0
|
161 % Example: b = [0 0 0 1 0 0 0 1] is indep of 3rd input (AND of inputs 1 and 2)
|
wolffd@0
|
162
|
wolffd@0
|
163 bits = ind2subv(2*ones(1,N), 1:2^N)-1;
|
wolffd@0
|
164 redundant = 1;
|
wolffd@0
|
165 iter = 0;
|
wolffd@0
|
166 while redundant & (iter < 4)
|
wolffd@0
|
167 iter = iter + 1;
|
wolffd@0
|
168 b = sample_discrete([0.5 0.5], 1, 2^N)-1;
|
wolffd@0
|
169 redundant = 0;
|
wolffd@0
|
170 for i=1:N
|
wolffd@0
|
171 on = find(bits(:,i)==1);
|
wolffd@0
|
172 off = find(bits(:,i)==0);
|
wolffd@0
|
173 if isequal(b(on), b(off))
|
wolffd@0
|
174 redundant = 1;
|
wolffd@0
|
175 break;
|
wolffd@0
|
176 end
|
wolffd@0
|
177 end
|
wolffd@0
|
178 end
|
wolffd@0
|
179
|