| 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 |