home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / CLIPPER / MISC / BAYESBLF.ZIP / BAYES.PAS
Encoding:
Pascal/Delphi Source File  |  1991-10-05  |  12.0 KB  |  430 lines

  1. program BayesNet(NodeFile,LinkFile,Output);
  2.  
  3.   This code is a basic implementation of Judea Pearl's belief
  4.   propagation algorithm for tree-structured Bayesian belief
  5.   networks.  The procedures and functions can be divided into
  6.   three basic groups:
  7.   
  8.   Math Support:
  9.      Normalize
  10.      MakeIdentityVector
  11.      TermProduct
  12.      TermQuotient
  13.      MatMult
  14.   Core:
  15.      ReviseBelief
  16.      UpdateNode
  17.      SubmitEvidence
  18.   General Support:
  19.      ReadString
  20.      FindNode
  21.      DumpNetwork
  22.         DumpNode
  23.      ReadNet
  24.         ReadNodes
  25.         ReadLinks
  26.      
  27.   The Core routines are described in the August AI Expert article.
  28.   The main program is set up to run the example from the May AI
  29.   Expert article.  It reads the net from two data files which are
  30.   described in ReadNodes and ReadLinks.  Be sure to figure out how
  31.   to RESET these files so that they get picked up correctly by those
  32.   procedures.   
  33.  
  34. const
  35.   MaxString      = 15;
  36.   MaxValues      =  5;
  37.         
  38. Type
  39.   StringRange = 1..MaxString;
  40.   ValueRange  = 1..MaxValues;
  41.   StringType  = packed array[StringRange] of char;
  42.   NetVector   = record
  43.                    Data:  array [ValueRange] of real;
  44.                    NVals: ValueRange
  45.                 end;
  46.   CPType      = record
  47.                    Data:        array[ValueRange,ValueRange] of real;
  48.                    NRows,NCols: ValueRange
  49.                 end;
  50.   NetNodePtr  = ^NetNode;
  51.   NetNode     = record
  52.                    Name:                       StringType;
  53.                    NumValues:                  ValueRange;
  54.                    Values:                     array[ValueRange] of StringType;
  55.                    Belief,Pi,IncomingPi,
  56.                    ExternalLambda,
  57.                    Lambda,OutgoingLambda:      NetVector;
  58.                    Parent,NextNode,
  59.                    NextSibling,FirstChild:     NetNodePtr;
  60.                    CPMatrix,TransCPMatrix:     CPType
  61.                 end;
  62.                        
  63. Var      NodeFile,LinkFile:            Text;
  64.         NetRoot,NodeList:             NetNodePtr;
  65.         EvidenceVector:               NetVector;
  66. { ******************** Math Support ******************** }
  67.        
  68. Procedure Normalize(var Vector: NetVector);
  69.  Scales incoming Vector so that it sums to unity }
  70. Var i:   ValueRange;
  71.    Sum: real;
  72. begin
  73. Sum := 0;
  74. With Vector do
  75.   begin
  76.   for i := 1 to NVals do
  77.      Sum := Sum + Data[i];
  78.   for i := 1 to NVals do
  79.      Data[i] := Data[i] / Sum
  80.   end
  81. End;
  82.         
  83. Procedure MakeIdentityVector(var Vector: NetVector;Length: ValueRange);
  84.  Makes incoming Vector into an identity vector of specified length}
  85. Var i: ValueRange;
  86. begin
  87. With Vector do
  88.   begin   
  89.   NVals := Length;
  90.   for i := 1 to Length do
  91.      Data[i] := 1.0   
  92.   end   
  93. End;
  94.  
  95. procedure TermProduct(var V1,V2,Result: NetVector);
  96.  Returns term product of V1 and V2 in Result }                      
  97. Var i:  ValueRange;
  98. begin
  99. If v1.NVals <> v2.Nvals then
  100.   writeln('*** Dimension error in TermProduct ***');
  101. With Result do
  102.   begin
  103.   Nvals := V1.Nvals;
  104.   for i := 1 to NVals do
  105.      Data[i] := V1.Data[i] * V2.Data[i]
  106.   end
  107. end;
  108.  
  109. procedure TermQuotient(var V1,V2,Result: NetVector);
  110.  Returns term quotient of V1 and V2 in Result }                               
  111.             
  112. var i:  ValueRange;
  113. begin
  114. if v1.NVals <> v2.Nvals then
  115.   writeln('*** Dimension error in TermQuotient ***');
  116. with Result do
  117.   begin
  118.   Nvals := V1.Nvals;
  119.   for i := 1 to NVals do
  120.      Data[i] := V1.Data[i] / V2.Data[i]
  121.   end
  122. end;
  123.  
  124. procedure MatMult(var InMat:  CPType;var InVec:  NetVector;var OutVec: SetVector);
  125.  Simplified matrix multiplication matrix routine.  Multiplies InMat * InVec
  126.  to produce OutVec.  Interprets InVec to be a NVals X 1 matrix. }
  127. Var Row,Col: ValueRange;
  128. begin
  129. if InMat.NCols <> InVec.NVals then
  130.   writeln('*** Dimension error in MatMult ***');
  131. with InMat do
  132.   begin
  133.   OutVec.NVals := NRows;
  134.   for Row := 1 to NRows do
  135.      begin
  136.      OutVec.Data[Row] := 0.0;
  137.      for Col := 1 to NCols do
  138.         OutVec.Data[Row] := OutVec.Data[Row] + Data[Row,Col] * InVec.Data[Col]
  139.       end
  140.   end
  141. end;
  142.  
  143. { ******************** Core ******************** }
  144. procedure ReviseBelief(Node: NetNodePtr);
  145. var Child:  NetNodePtr;
  146. begin
  147. with Node^ do
  148.   begin
  149.   { Part (a) of Figure 4 }
  150.   if Parent <> nil then
  151.      MatMult(TransCPMatrix,IncomingPi,Pi);
  152.   { Part (b) of Figure 4 }
  153.   Lambda := ExternalLambda;
  154.   Child := FirstChild;
  155.   while Child <> nil do
  156.      begin
  157.      TermProduct(Child^.OutgoingLambda,Lambda,Lambda);
  158.      Child := Child^.NextSibling
  159.      end;
  160.   { Shaded part of Figure 4 }
  161.   TermProduct(Lambda,Pi,Belief);   
  162.   Normalize(Belief)
  163.   end
  164. end;
  165. procedure UpdateNode(Node,Sender: NetNodePtr);
  166. var Child:  NetNodePtr;
  167. begin
  168. with Node^ do
  169.   begin
  170.   ReviseBelief(Node);
  171.   { Update OutgoingLambda & send update message to parent
  172.     (part (c) of Figure 4) }
  173.   if (Parent <> Sender) and (Parent <> nil) then
  174.      begin
  175.      MatMult(CPMatrix,Lambda,OutgoingLambda);
  176.      UpdateNode(Parent,Node)
  177.      end;
  178.   { Update IncomingPi and send update message to children
  179.     (part (d) of Figure 4) }
  180.   Child := FirstChild;
  181.   while Child <> nil do
  182.      begin
  183.      if Child <> Sender then
  184.         begin
  185.         TermQuotient(Belief,Child^.OutgoingLambda,Child^.IncomingPi);
  186.         UpdateNode(Child,Node)
  187.         end;
  188.      Child := Child^.NextSibling
  189.      end
  190.   end
  191. end;
  192.  
  193. procedure SubmitEvidence(Node: NetNodePtr;var Evidence: NetVector);
  194. var i: ValueRange;
  195. begin
  196. with node^ do
  197.   begin
  198.   writeln('Submitting evidence to ',Node^.Name,', evidence is:');
  199.   for i := 1 to Evidence.NVals do
  200.      writeln('[',Values[i],'] = ',Evidence.Data[i]);
  201.   TermProduct(Evidence,ExternalLambda,ExternalLambda);
  202.   UpdateNode(Node,nil)
  203.   end
  204. end;
  205.  
  206. { ******************** General Support ******************** }
  207. function ReadString(var InFile: Text;var InString: StringType): boolean;
  208.  Reads InFile, returning next string in InString.  Returns FALSE upon
  209.  encountering end of file, otherwise returns TRUE. }
  210. var i,j:  StringRange;
  211. begin
  212. if eof(InFile) then
  213.   ReadString := false
  214. else
  215.   begin
  216.   i := 1;
  217.   while not eoln(InFile) do
  218.      begin
  219.      read(InFile,InString[i]);
  220.      i := i + 1
  221.      end;
  222.   readln(InFile);
  223.   for j := i to MaxString do
  224.      InString[j] := ' ';
  225.   ReadString := true   
  226.   end;   
  227. end;
  228.         
  229. function FindNode(NodeName: StringType):NetNodePtr;
  230.  Searches network for node having specified NodeName. }                  
  231. var CurrentNode:   NetNodePtr;
  232. begin
  233. CurrentNode := NodeList;
  234. While (CurrentNode^.Name <> NodeName) and (CurrentNode <> nil) do
  235.   CurrentNode := CurrentNode^.NextNode;
  236. If CurrentNode = nil then
  237.   begin
  238.   writeln('*** Error in FindNode -- cannot find ',NodeName);
  239.   FindNode := nil
  240.   end
  241. Else
  242.   FindNode := CurrentNode
  243. End;
  244.        
  245. Procedure DumpNetwork(Node: NetNodePtr);
  246.  Recursive procedure to dump network, given pointer to root }
  247. procedure DumpNode(Node: NetNodePtr);
  248.  Simple procedure to dump a single node }
  249. Const Stars = '*************************************************';
  250. var CurrentValue,NumRows,NumCols,Row,Col:  ValueRange;
  251. begin
  252. Writeln(Stars);
  253. With Node^ do
  254.   begin
  255.   writeln('Dumping ',Name);
  256.   for CurrentValue := 1 to NumValues do
  257.      writeln('Pi[',Values[CurrentValue],'] = ',Pi.Data[CurrentValue]);
  258.   for CurrentValue := 1 to NumValues do
  259.      writeln('Lambda[',Values[CurrentValue],'] = ',Lambda.Data[CurrentValue]);
  260.   for CurrentValue := 1 to NumValues do
  261.      writeln('Belief[',Values[CurrentValue],'] = ',Belief.Data[CurrentValue]);
  262.   if Parent <> nil then
  263.      begin
  264.      writeln;
  265.      writeln('CP Matrix:');
  266.      for Row := 1 to CPMatrix.NRows do
  267.         begin
  268.         for Col := 1 to CPMatrix.NCols do
  269.            write(CPMatrix.Data[Row,Col]);
  270.         writeln
  271.         end
  272.      end
  273.   end;
  274. Writeln(Stars);
  275. Writeln('Type <cr> to continue...');
  276. Readln(nd);   { of DumpNode }
  277. var CurrentNode: NetNodePtr;
  278. begin
  279. if Node <> nil then
  280.   begin
  281.   DumpNode(Node);
  282.   CurrentNode := Node^.FirstChild;
  283.   while CurrentNode <> nil do
  284.      begin
  285.      DumpNetwork(CurrentNode);
  286.      CurrentNode := CurrentNode^.NextSibling
  287.      end 
  288.   end  
  289. end;
  290. procedure ReadNet(var NodeFile,LinkFile: Text);
  291.       
  292. Procedure ReadNodes(Var NodeFile: Text);
  293.  This procedure reads the NodeFile.  Format of file is as follows:
  294.    Node 1 name
  295.   Node 1 number of values
  296.   Node 1 value 1 name
  297.   Node 1 value 1 prior probability (ignored except for root node)
  298.   Node 1 value 2 name
  299.   Node 1 value 2 prior probability (ignored except for root node)
  300.           .....
  301.   Node 1 value n name
  302.   Node 1 value n prior probability (ignored except for root node)
  303.   Node 2 name
  304.           .....
  305.           etc.
  306.    
  307. Var NodeName:      StringType;
  308.    CurrentValue:  ValueRange;
  309.    eofStatus:     boolean;
  310.    CurrentNode:   NetNodePtr;
  311.                    
  312. Begin
  313. Reset(NodeFile);
  314. NodeList := nil;
  315. While ReadString(NodeFile,NodeName) do
  316.   begin
  317.   new(CurrentNode);
  318.   with CurrentNode^ do
  319.      begin
  320.      Name := NodeName;
  321.      readln(NodeFile,NumValues);
  322.      for CurrentValue := 1 to NumValues do
  323.         begin
  324.         eofStatus := ReadString(NodeFile,Values[CurrentValue]);
  325.         readln(NodeFile,Pi.Data[CurrentValue])
  326.         end;
  327.      Pi.NVals := NumValues;
  328.      Parent := nil;
  329.      NextSibling := nil;
  330.      FirstChild := nil;
  331.      NextNode := NodeList;
  332.      NodeList := CurrentNode;
  333.      MakeIdentityVector(ExternalLambda,NumValues);
  334.      MakeIdentityVector(Lambda,NumValues)
  335.      end
  336.   end;
  337. Close(NodeFile)
  338. End;   { or ReadNodes }
  339. procedure ReadLinks(var LinkFile: Text);
  340.  This procedure reads the NodeFile.  Be careful here, upper/lower case
  341.  must match identically the node names in NodeFile.  Format of file is
  342.  as follows:
  343.    Top Node name for first link
  344.   Bottom Node name for first link
  345.   1st row of CP matrix
  346.   2nd row of CP matrix
  347.          ....
  348.   nth row of CP matrix
  349.   Top Node name for second link
  350.   Bottom Node name for second link
  351.   1st row of CP matrix
  352.   2nd row of CP matrix
  353.          ....
  354.   nth row of CP matrix
  355.       etc.
  356.  
  357. Var TopNodeName,BottomNodeName:      StringType;
  358.    TopNode,BottomNode:              NetNodePtr;
  359.    Row,Col:                         ValueRange;
  360.    eofStatus:                       boolean;
  361.                    
  362. Begin
  363. Reset(LinkFile);
  364. While ReadString(LinkFile,TopNodeName) do
  365.   begin
  366.   TopNode := FindNode(TopNodeName);
  367.   eofStatus := ReadString(LinkFile,BottomNodeName);
  368.   BottomNode := FindNode(BottomNodeName);
  369.   with BottomNode^ do
  370.      begin
  371.      CPMatrix.NRows := TopNode^.NumValues;
  372.      CPMatrix.NCols := NumValues;
  373.      TransCPMatrix.NRows := CPMatrix.Ncols;
  374.      TransCPMatrix.NCols := CPMatrix.NRows;
  375.      for Row := 1 to CPMatrix.NRows do
  376.         begin
  377.         for Col := 1 to CPMatrix.Ncols do
  378.            begin
  379.            read(LinkFile,CPMatrix.Data[Row,Col]);
  380.            TransCPMatrix.Data[Col,Row] := CPMatrix.Data[Row,Col]
  381.            end;
  382.         readln(LinkFile)
  383.         end;
  384.      NextSibling := TopNode^.FirstChild;
  385.      Parent := TopNode;
  386.      MakeIdentityVector(OutgoingLambda,TopNode^.NumValues)
  387.      end;
  388.   TopNode^.FirstChild := BottomNode
  389.   end
  390. End;   { of ReadLinks }
  391. begin
  392. ReadNodes(NodeFile);
  393. ReadLinks(LinkFile);
  394.  Find root of network. }
  395. NetRoot := NodeList;
  396. While NetRoot^.Parent <> nil do
  397.   NetRoot := NetRoot^.NextNode;
  398.  Initialize network }   
  399. UpdateNode(NetRoot,nil)
  400. End;
  401.     
  402. Begin
  403. { Read network in }
  404. ReadNet(NodeFile,LinkFile);
  405. { Take a look }
  406. DumpNetwork(NetRoot);
  407. { Store evidence from rain alarm in EvidenceVector }
  408. With EvidenceVector do
  409.   begin
  410.   Data[1] := 0.8;
  411.   Data[2] := 0.04;
  412.   NVals := 2
  413.   end;
  414. { Submit EvidenceVector to Rain node }   
  415. SubmitEvidence(FindNode('Rain           '),EvidenceVector);
  416. { Take a look }
  417. DumpNetwork(NetRoot);
  418. { Store evidence from telephone call in EvidenceVector }
  419. With EvidenceVector do
  420.   begin
  421.   Data[1] := 1.0;
  422.   Data[2] := 0.02;
  423.   NVals := 2
  424.   end;
  425. { Submit EvidenceVector to Sunburn node }   
  426. SubmitEvidence(FindNode('Sunburn        '),EvidenceVector);
  427. { Take a look }
  428. DumpNetwork(NetRoot)
  429. end.
  430.