home *** CD-ROM | disk | FTP | other *** search
/ Windoware / WINDOWARE_1_6.iso / miscprog / atre20 / atree.txt < prev    next >
Text File  |  1991-08-08  |  55KB  |  1,376 lines

  1.  
  2.  
  3.  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11.  
  12.  
  13.  
  14. An Implementation of Adaptive Logic Networks
  15.  
  16. Copyright W.W. Armstrong, Andrew Dwelly, Rolf Manderscheid, Monroe Thomas
  17. November 11, 1990
  18.  
  19. initial implementation on Unix (TM AT&T)
  20.  
  21. bug-fixes and initial port to DOS
  22. Rolf Manderscheid
  23. April 15, 1991
  24.  
  25. ported to Microsoft Windows
  26. Monroe M. Thomas
  27. May 31, 1991
  28.  
  29. revised for Version 2.0
  30. Monroe M. Thomas
  31. July 17, 1991
  32.  
  33.  
  34.  
  35.  
  36. 1     Introduction
  37.  
  38. The C library atree.c contains an implementation of an unconventional kind of
  39. learning algorithm for adaptive logic networks[Arms], which can be used in
  40. place of the backpropagation algorithm for multilayer feedforward artificial
  41. neural networks [Hech], [Rume].
  42.  
  43. The ability of a logic network to learn or adapt to produce an arbitrary 
  44. boolean function specified by some empirical "training" data is certainly 
  45. important for the success of the method, but there is another property of 
  46. logic networks which is also essential.  It is the ability to generalize 
  47. their responses to new inputs, presented after training is completed.  The 
  48. successful generalization properties of these logic networks are based on the 
  49. observation, backed up by a theory [Boch], that trees of two-input logic 
  50. gates of types AND, OR, LEFT, and RIGHT are very insensitive to changes of 
  51. their inputs.
  52.  
  53. Some experiments on handwritten numeral recognition and satellite image 
  54. classification have been successfully carried out. [Arms3, Arms4].  Recent 
  55. experiments have shown this algorithm to learn quickly on some problems 
  56. requiring learning of integer or continuous-valued functions where 
  57. backpropagation has reportedly led to long training times; and it functions 
  58. very well on boolean data [Arms5].
  59.  
  60. At the present time, only limited comparisons have been made with the 
  61. conventional approach to neurocomputing, so the claims necessarily have to be 
  62. muted.  This situation should rapidly be overcome as users of this software 
  63. (or improved variants of it yet to come) begin experimentation.  However one 
  64. property of these networks in comparison to others is an absolute, and will 
  65. become apparent to computer scientists just by examining the basic 
  66. architecture of the networks.  Namely, when special hardware is available, 
  67. this technique, because it is based on combinational logic circuits of 
  68. limited depth (e. g. 10 to 20 propagation delays), can potentially offer far 
  69. greater execution speeds than other techniques which depend on floating point 
  70. multiplications, additions, and computation of sigmoidal functions.
  71.  
  72. A description of the class of learning algorithms and their hardware 
  73. realizations can be found in [Arms, Arms2], but we will briefly introduce the 
  74. concepts here. An atree (Adaptive TREE) is a binary tree with nodes of two 
  75. types: (1) adaptive elements, and (2) leaves.  Each element can operate as an 
  76. AND, OR, LEFT, or RIGHT gate, depending on its state.  The state is 
  77. determined by two counters which change only during training.  The leaf nodes 
  78. of the tree serve only to collect the inputs to the subtree of elements.  
  79. Each one takes its input bit from a boolean input vector or from the vector 
  80. consisting of the complemented bits of the boolean input vector.  The tree
  81. produces a single bit as its output.
  82.  
  83. Despite the apparent limitation to boolean data, simple table-lookups permit
  84. representing non-boolean input values (integers or reals for example) as bit
  85. vectors, and these representations are concatenated and complemented to form
  86. the inputs at the leaves of the tree.  For computing non-boolean outputs,
  87. several trees are used in parallel to produce a vector of bits representing
  88. the output value.
  89.  
  90. This software contains everything needed for a programmer with knowledge of C
  91. and Windows 3.x to create, train, evaluate, and print out adaptive logic
  92. networks. It has been written for clarity rather than speed in the hope that
  93. it will aid the user in understanding the algorithms involved.  The intention
  94. was to try make this version faster than variants of the backpropagation
  95. algorithm for learning, and to offer much faster evaluation of learned
  96. functions than the standard approach given the same general-purpose computing
  97. hardware.  Users of the software are requested to provide some feedback on
  98. this point to the authors.
  99.  
  100. This software also includes a language "lf" that allows a non-programmer to
  101. conduct experiments using atrees, as well as a number of demonstrations.
  102.  
  103. A version of this software which is both faster and contains a more effective
  104. learning algorithm is planned for the near future.
  105.  
  106.  
  107.                                   Y
  108.                                   │
  109.                           ┌───────┴───────┐
  110.                           │  Random Walk  │
  111.                           │    Decoder    │
  112.                           └───────┬───────┘
  113.                           ┌───────┴───────┐
  114.                           │ Output Vector │
  115.                           └┬─────────────┬┘
  116.                            │             │
  117.     Trees - one per       (O)           (O)
  118.     output bit             │             │
  119.                      ┌─────┴────┐      ┌─┴─┐
  120.                  (O)─┘          └─(O)
  121.                   │                │
  122.                 ┌─┴─┐            ┌─┴─┐
  123.              (O)┘   └(O)      (O)┘   └(O)
  124.             ┌─┴─┐   ┌─┴─┐    ┌─┴─┐   ┌─┴─┐
  125.             │   │   │   │    │   │   │   │
  126.             b1 ~b1  b2 ~b1  ~b1 ~b2  b2 ~b2    Random Connections
  127.                                ┌──────┬──────┐
  128.                                │ ~b1  │ ~b2  │ Complements
  129.                                └──┬───┴──┬───┘
  130.                ┌──────────────────┘      │
  131.                │      ┌──────────────────┘
  132.             ┌──┴───┬──┴───┐
  133.             │  b1  │  b2  │ Input Vector
  134.             └──┬───┴──┬───┘
  135.           ┌────┴──────┴───┐
  136.           │  Random Walk  │
  137.           │    Encoder    │
  138.           └───┬───────┬───┘
  139.               │       │
  140.              X1       X2
  141.  
  142.  
  143.     Figure 1: Using several trees to compute Y = f(X1, X2)
  144.  
  145.  
  146.  
  147. 2     Writing Applications With atree
  148.  
  149. Writing applications that perform a simple classification (yes or no) is 
  150. relatively easy (within the constraints of Windows programming). The 
  151. programmer creates a training set, then creates a tree using atree_create().  
  152. The tree is trained using atree_train() and then it can be used to evaluate 
  153. new inputs using atree_eval().  Examples of this can be seen in the files
  154. mosquito.c, and mult.c, both of which hide most of Windows' dressings for 
  155. clarity.
  156.  
  157. Writing applications where the tree has to learn real number valued functions 
  158. is a little more complex, as the programmer has to come to grips with the 
  159. encoding problem.
  160.  
  161. Because a single tree produces only one bit, the programmer must train 
  162. several trees on the input data, each one responsible for one bit of the 
  163. output data. This is made slightly simpler by the choice of parameters for 
  164. atree_train() which takes an array of bit vectors as the training set, and an 
  165. array of bit vectors for the result set. The programmer provides an integer 
  166. which states which bit column of the result set the current tree is being
  167. trained on. Typical code might look as follows:-
  168.  
  169. ....
  170. {
  171.     int i;
  172.     int j;
  173.     LPBIT_VEC train;    /* LPBIT_VEC is a long (far) pointer to a bit_vec */
  174.     LPBIT_VEC result;
  175.     LPATREE *forest;    /* LPATREE is a long (far) pointer to an atree */
  176.  
  177.     /* Create the training set using your own domain function */
  178.     train = domain();
  179.  
  180.     /* Create the result set */
  181.     result = codomain();
  182.  
  183.     /*
  184.      * Make enough room for the set of trees - one tree per bit in the
  185.      * codomain
  186.      */
  187.     forest = (LPATREE *) Malloc((unsigned)sizeof(LPATREE) * NO_OF_TREES);
  188.  
  189.     /* Now create and train each tree in turn */
  190.     for (i = 0; i < NO_OF_TREES; i++)
  191.     {
  192.         forest[i] = atree_create(variables,width);
  193.         atree_train(forest[i], train,  result, i, TRAIN_SET_SIZE,
  194.                     MIN_CORRECT, MAX_EPOCHS, VERBOSITY);
  195.     }
  196.  
  197.     /*
  198.      * Where TRAIN_SET_SIZE is the number of elements in train,
  199.      * MIN_CORRECT is the minimum number of elements the tree should
  200.      * get correct before stopping, MAX_EPOCHS is the absolute maximum
  201.      * length of training and VERBOSITY controls the amount of
  202.      * diagnostic information produced.
  203.      */
  204. ......
  205.  
  206. The standard encoding of integers into binary numbers does not work well with
  207. this algorithm since it tends to produce functions which are sensitive to the 
  208. values of the least significant bit. So instead we use the routine 
  209. atree_rand_walk() to produce an array of bit vectors where each vector is 
  210. picked at random and is a specified Hamming distance away from the previous
  211. element. Picking the width of the encoding vector, and the size of the step
  212. in Hamming space is currently a matter of experimentation, although some 
  213. theory is currently under development to guide this choice.
  214.  
  215. Real numbers are encoded by dividing the real number line into a number of 
  216. quantization levels, and placing each real number to be encoded into a 
  217. particular quantization. Obviously, the more quantization levels there are, 
  218. the more accurate the encoding will be.  Essentially this procedure turns 
  219. real numbers into integers for the purposes of training. The quantizations
  220. are then turned into bit vectors using the random walk technique again.
  221.  
  222. Once the trees are trained, we can evaluate them with new inputs. Despite 
  223. their training, the trees may not be totally accurate, and we need some way 
  224. of dealing with error. The normal approach taken is to produce a result from 
  225. the set of trees, then search through the random walk for the closest bit
  226. vector. This is taken as the true result. Typical code might be as follows:-
  227.  
  228. ....
  229.     /* Continued from previous example */
  230.     int closest_elem;
  231.     int smallest_diff;
  232.     int s;
  233.     LPBIT_VEC test;
  234.     LPBIT_VEC tree_result;
  235.  
  236.     /* Now create the (single in this example) test vector */
  237.  
  238.     test = test_element();
  239.  
  240.     /* Now create some room for the tree results */
  241.  
  242.     tree_result = bv_create(No_OF_TREES);
  243.  
  244.     /* Evaluate the trees */
  245.  
  246.     for (i = 0; i < NO_OF_TREES; i++)
  247.     {
  248.         /*
  249.          * Set bit i of tree_result, the result of evaluating
  250.          * the ith tree.
  251.          */
  252.  
  253.         bv_set(i, tree_result, atree_eval(forest[i], test));
  254.     }
  255.  
  256.     /*
  257.      * tree_result probably has a few bits wrong, so we will look
  258.      * for the closest element in the result array
  259.      */
  260.  
  261.     closest_elem = 0;
  262.     smallest_diff = MAX_INT;
  263.  
  264.     for (i = 0; i < TRAIN_SET_SIZE; i++)
  265.     {
  266.         if ((s = bv_diff(tree_result, result[i])) < smallest_diff)
  267.         {
  268.             smallest_diff = s;
  269.             closest_elem = i;
  270.         }
  271.     }
  272.  
  273.     /*
  274.      * At this point, result[closest_elem] is the correct bit vector,
  275.      * and smallest_diff is the amount of error produced by the tree.
  276.      */
  277.  
  278.    do_something_with(result[closest_elem]);
  279.  
  280.     /* Etc. */
  281. }
  282. ....
  283.  
  284.  
  285. 3     The Windows atree Library
  286.  
  287. The atree library consists of a single include file atree.h, which must be
  288. included in all software making calls on the library, and a library of
  289. routines atree.c.  The routines permit the creation, training, evaluation and
  290. testing of adaptive logic networks in a Windows environment, and there are a
  291. number of utility routines designed to make this task easier.
  292.  
  293. Important note:  the module definition file for your application must include
  294. in its EXPORT section the name of the atree Status window procedure: 
  295. VerbosityWndProc, along with any other window procedures your application may 
  296. have - see mosquito.def for an example.
  297.  
  298.  
  299. 3.1   Naming Conventions
  300.  
  301. Throughout this software, the following conventions have been used :-
  302.  
  303. Publicly available functions are called atree_something(). If the routine is 
  304. primarily concerned with bit vectors rather than atrees, it will be named 
  305. bv_something() instead. The exceptions to this occur for functions that are 
  306. directly responsible for maintaining performance of the atree software in the 
  307. Windows environment.
  308.  
  309. Variables are always in lower case. The variables i, j, and k are reserved as 
  310. iterators in "for" loops. The variable verbosity is reserved for controlling 
  311. the amount of diagnostic information produced.
  312.  
  313.  
  314. 3.2   Public Macros
  315.  
  316. The following macros are defined in atree.h and are available to any 
  317. application using the atree library.
  318.  
  319. The macro MEMCHECK allows us to check the validity of a pointer.  For 
  320. example, if the pointer p in MEMCHECK(p) is NULL, then a message box pops up
  321. with appropriate notification, and the application is terminated.
  322.  
  323. The macro RANDOM allows us to conveniently produce a random number between 0
  324. and some user- specified x in the program. For example, in order to produce a 
  325. random true or false value (0 or 1) we write RANDOM(2).
  326.  
  327. The macro Malloc serves as a front end for the atree memory allocation 
  328. routine WinMem_Malloc().  To allocate a chunk of 16 bytes to a pointer p, use
  329. p = Malloc(16).
  330.  
  331. The macro Free serves as a front end for the atree memory routine 
  332. WinMem_Free().  To free the memory pointed by a pointer p that was allocated
  333. with WinMem_Malloc() (or the macro Malloc), use Free(p).
  334.  
  335.  
  336.  
  337. 3.3   void atree_init(hInstance)
  338.  
  339. HANDLE hInstance;
  340.  
  341. This routine should be called by the user before making calls to any other 
  342. atree library routine.  The parameter hInstance should be the instance handle 
  343. given to your application by Windows in your WinMain() procedure.  Currently, 
  344. atree_init() calls the srand() routine to initialize the random number 
  345. generator and initializes the atree Status window.
  346.  
  347.  
  348. 3.4   void atree_quit()
  349.  
  350. This routine sets the internal atree_quit_flag variable to TRUE to notify all 
  351. atree procedures that it is time to drop whatever it is they are doing and 
  352. quit.  This procedure should be called before your application exits so that 
  353. any running atree procedures are not left in memory.
  354.  
  355.  
  356. 3.5  LPBIT_VEC atree_rand_walk(num,width,p)
  357.  
  358. int num;
  359. int width;
  360. int p;
  361.  
  362. The standard encoding of integers into binary is not suitable for adaptive 
  363. logic networks, since the least significant bits vary quickly during 
  364. incrementations of the integer compared to the more significant bits.  The 
  365. effect of binary number encoding is easy to see when we consider the result 
  366. of a single bit error occurring in the output of a collection of trees (a 
  367. forest): how important the error is depends on the position of the bit in the 
  368. output vector. An error in the least significant bit of the vector makes a 
  369. difference of one unit in the output integer; an error in the most 
  370. significant bit causes a large difference in the output integer depending on 
  371. the width of the vector.
  372.  
  373. A better encoding is one where each bit varies at about the same rate; and we 
  374. can create such an encoding by taking a random walk through Hamming space
  375. [Smit].  A randomly produced vector is chosen to represent the first integer 
  376. value in a sequence.  For each subsequent integer, a specified number of 
  377. bits, picked a random, are changed to create the next vector.
  378.  
  379. The routine atree_rand_walk() does this job, with the additional guarantee
  380. that each vector produced is unique. The parameter num gives the number of
  381. vectors, or "steps" in the walk, required, the parameter width gives the
  382. width in bits of each vector, and the parameter p is the distance of each
  383. step in the Hamming metric (the number of bits which change).
  384.  
  385. The uniqueness requirement makes the routine rather more complex than one
  386. might expect. Because we expect to be using large random walks, it was felt
  387. that a simple check against all the previously created vectors would not be
  388. efficient enough. Instead all vectors with the same weight (the weight of a 
  389. bit vector is the number of 1s in it; e. g., the weight of 10110 is 3) are 
  390. chained together, and only those vectors with a weight equal to the one 
  391. currently being checked for uniqueness are examined.  If the vector is not 
  392. unique, the routine will go back to the previous unique vector and try 
  393. randomly changing some other bits. In order to avoid an infinite loop, it 
  394. will only try MAX_RETRY times to do this. If it cannot proceed, the routine 
  395. aborts.  A better version of the software would check to assure a minimum 
  396. distance between points.
  397.  
  398. A bit of thought must go into the choice of width, the length of the 
  399. bitstring used to encode a quantity, and to the stepsize p.  Suppose we want 
  400. num quantization levels for a variable x.  Then the width used to code x must 
  401. be at least as large as the logarithm base 2 of num to make the codes unique.  
  402. A thermometer code would use num - 1 bits, where the quantization level i
  403. (starting at 0 and increasing to num - 1) is represented by i 1s at the left
  404. of the vector completed by 0s at the right.  For example, if num = 100, then
  405. width must be at least 7, while the thermometer code would use 99 bits.  The
  406. width for an input variable is not as critical as for an output variable,
  407. since we need to train one tree for each bit in the output.
  408.  
  409. Suppose some training data contains vectors with two variables in the domain.  
  410. Two domain vectors (x1, x2) could be (3.14, 9.33), corresponding to levels
  411. (11, 17), and (3.18, 9.48), corresponding to (13, 18), say.  The function
  412. y=f(x1,x2) to be learned is supposed continuous, so the two function values
  413. could be 34.6 and 33.9, with neighboring quantization levels 67 and 66
  414. respectively.  If the training set has been learned perfectly, then we shall
  415. get the correct boolean codes for levels 67 and 66 from the trained forest of
  416. trees on input of these vectors.  When we only have vectors close to the
  417. above two vectors in Euclidean distance, then problems arise.
  418.  
  419. Suppose we have an input (3.15, 9.34), corresponding also to levels (11, 17).  
  420. Obviously, the trees give the same response as for (3.14, .933), namely level 
  421. 67 of y.  As long as this is an acceptable approximation to the desired 
  422. function, there is no problem.  If the quantized function value varied too
  423. much within the set of real vectors corresponding to (11,17), we would have
  424. to use a finer quantization on the inputs.
  425.  
  426. Next suppose that the training set contained no sample with quantization 
  427. levels (12, 17).  Then we would like the system to be able to take advantage 
  428. of the similarity between the concatenated codes for (12, 17) and (11, 17) to 
  429. be able to extrapolate its output.  This can occur if the codes for levels 11 
  430. and 12 of x1 are close in Hamming distance.  If, on the other hand, they were 
  431. 1/2 of the width of the code for x1 apart, then the system could just as well 
  432. extrapolate from a training point with levels (96, 17) as from (11, 71).  So 
  433. we would take p for the random walk for x1 to be less than, say, 1/4 width to 
  434. make sure levels 11 and 12 of x1 are close.  This is because random pairs of
  435. points in the Hamming space tend to be 1/2 width apart.
  436.  
  437. If neighboring training samples tended to have x1 values which are four
  438. levels apart, e. g. (11, 17) and (15, 17), then 4 * p would have to be less
  439. than 1/4 width.  Now if we vary both x1 and x2, then neighboring input
  440. vectors might tend to be four levels apart in x1 and 7 levels apart in x2.
  441. Then the values px1, px2 chosen for p for the two walks should be such that 4
  442. * px1 + 7 * px2 is less than 1/4 the sum of the widths wx1, wx2 for coding
  443. x1, x2.
  444.  
  445. There is a good reason for using a large value of the p for the output 
  446. variable y.  Namely, for a given input vector, some of the width trees may 
  447. produce an output bit that is different from that of a code of the correct 
  448. quantization level of y as one varies the input a bit.  If fewer than p/2 
  449. output bits are changed, we are still close to the original code, and the 
  450. same output quantization level would still be recovered by minimum distance 
  451. decoding.  Consider the case where there are only num = 2 levels of y, and 
  452. they are encoded 00000 and 11111, with width = 5 and p = 5.  As we move away 
  453. from inputs resulting in a correct response, say 00000, to those having two 
  454. bits different, like 01001, the decoded output will maintain its value.
  455.  
  456. So for the output variables, choosing larger values for p and width can
  457. provide error correction just as taking a majority vote does for a boolean
  458. output.
  459.  
  460. We are aware that this only touches the surface of the questions involved
  461. with choosing the Hamming codes for continuous variables.  The general
  462. assumptions are that real intervals are mapped into random walks in a way
  463. that locally preserves "proximity", and it is the proximity of elements in
  464. the domain and codomain that determines the quality of extrapolation.
  465. Instead of using random walks, some work has been done using algebraic codes,
  466. in particular Golay codes.  This will be discussed in a thesis at U. of A. by
  467. Allen Supynuk, which is soon to be completed.
  468.  
  469.  
  470. 3.6   public LPATREE atree_create(numvars,leaves)
  471.  
  472. int numvars;
  473. int leaves;
  474.  
  475. This is the routine used to create an atree of a given size. The parameter
  476. leaves gives the number of leaves or output leads to the tree, and hence 
  477. controls its size, which is one less than this.  A balanced tree is chosen if 
  478. possible.
  479.  
  480. The parameter numvars is the number of boolean variables in the bit vector 
  481. input to the tree.  It is used during initialization of the (random) 
  482. connections between leaf nodes of the tree and the input bit vector.  Usually 
  483. the bits of the input vector, and their complements will be required as 
  484. inputs to the tree since there are no NOT nodes in the tree itself. It is
  485. therefore recommended that there be at least twice as many inputs to the tree
  486. as there are bits in the input vector for a given problem:
  487.  
  488.                         leaves >= 2 * numvars
  489.  
  490. The atree library maintains two free lists, one each for leaves and nodes.    
  491. atree_create() always uses memory from these lists.  In the event that a free 
  492. list is empty, a large block is allocated and added to the list.  The size of 
  493. the block can be adjusted by editing the compile-time constant NEWMALLOC 
  494. defined in atree.c.
  495.  
  496.  
  497. 3.7   void atree_free(tree)
  498.  
  499. LPATREE tree;
  500.  
  501. This routine returns memory used by nodes and leaves  of tree to the 
  502. appropriate free list.  Note that memory is not freed from the free lists.
  503.  
  504.  
  505. 3.8   BOOL atree_eval(tree,vec)
  506.  
  507. LPATREE tree;
  508. LPBIT_VEC vec;
  509.  
  510. This routine is responsible for calculating the output of a tree from a given 
  511. bit vector. It takes advantage of the standard C definition of && and || to 
  512. do this in the required parsimonious  fashion [Meis][Arms5].
  513.  
  514. This routine also marks subtrees that are unevaluated, and sets the internal 
  515. atree.n_sig_left and atree.n_sig_right values for a node. This information is
  516. used when atree_eval() is used from within atree_train().
  517.  
  518.  
  519. 3.9   BOOL atree_train(tree,tset,...)
  520.  
  521. LPATREE tree
  522. LPBIT_VEC tset;
  523. LPBIT_VEC correct_result;
  524. int bit_col;
  525. int tset_size;
  526. int no_correct;
  527. int epochs;
  528. int verbosity;
  529.  
  530. This is the routine that adapts a tree to learn a particular function.  It is 
  531. a little more complex than you might expect as it has been arranged to make 
  532. it convenient to train multiple trees on the same training set.
  533.  
  534. The parameter tree is the tree to be trained, and the parameter tset is the 
  535. array of bit vectors which the tree is to be trained on (the training set). 
  536. An atree only produces a single bit, so in principle all that is needed for 
  537. the correct_result parameter is an array of bits, with one bit corresponding 
  538. to each bit vector in the training set. In training multiple trees (when 
  539. learning a quantized real-valued function, for example), it is more 
  540. convenient to keep the correct results in an array of bit vectors, and 
  541. specify which column of the array a tree is supposed to be learning. This is 
  542. the purpose of the array correct_result and the integer bit_col.
  543.  
  544. The next parameter tset_size gives the number of elements in tset and  
  545. correct_result (which have to be the same --- there must be a result for
  546. every input to the function).
  547.  
  548. The next two parameters control the amount of training that is to be done.  
  549. We train on the vectors of the training set in pseudo-random order.  The term 
  550. epoch here is used to mean a number of input vector presentations equal to 
  551. the size of the training set.  The parameter epochs states how many epochs 
  552. may be completed before training halts.  The parameter no_correct states how 
  553. many elements in the training set the tree must get correct
  554. before training halts.  The routine will therefore stop at whichever of these 
  555. two conditions is true first. For example given that we have a training set 
  556. with 10 elements and we wish to train for 15 epochs or until 90% of the 
  557. elements in the training set have been responded to correctly. We can 
  558. achieve this by setting no_correct to 9 and epochs to 15.
  559.  
  560. The verbosity parameter controls how much diagnostic information the routine 
  561. will produce. At the moment only 0 (silent) or 1 (progress information) is 
  562. implemented.  The progress information consists of an atree Status window 
  563. that shows the progress of training.
  564.  
  565. The routine decides which vector is the next to be presented to the tree and extracts the result bit from the 
  566. correct_result array. It also keeps track of the number of epochs, and the number of correct responses 
  567. from the tree.
  568.  
  569. 3.10   void atree_print(tree,verbosity)
  570.  
  571. LPATREE tree;
  572. int verbosity;
  573.  
  574. This routine allows the programmer to output an atree to disk before, during, 
  575. or after training, in a form suitable for printing. The parameter tree is the
  576. tree to be printed, and verbosity is the amount of information produced.  The 
  577. disk file is currently hard coded as "atree.out" (future versions of the 
  578. software will allow user selected output streams).
  579.  
  580.  
  581. 3.11   int atree_store(tree, filename)
  582.  
  583. LPATREE tree;
  584. LPSTR filename; (LPSTR is Windows for "char far *")
  585.  
  586. This routine stores tree to filename. This routine is used to store a single 
  587. tree, if you want to store a forest use atree_write().  Returns 0 for 
  588. success, non-zero on failure.
  589.  
  590.  
  591. 3.12   LPATREE atree_load(filename)
  592.  
  593. LPSTR filename;
  594.  
  595. This routine reads filename and returns the tree stored therein.  
  596. atree_load() reads exactly one tree from filename, if you want to read 
  597. multiple trees use atree_read().  A NULL pointer is returned if any error or 
  598. EOF is encountered.
  599.  
  600.  
  601. 3.13   LPATREE atree_read(stream)
  602.  
  603. FILE *stream;
  604.  
  605. This routine reads a single tree from the file referenced by stream and 
  606. returns a pointer to it.  Subsequent calls to atree_read() will read further 
  607. trees from stream.  A NULL pointer is returned if any error or EOF is
  608. encountered.
  609.  
  610. 3.14   int atree_write(stream, tree)
  611.  
  612. FILE *stream;
  613. LPATREE tree;
  614.  
  615. This routine writes tree onto the file referenced by stream.  Trees are 
  616. stored in postfix notation, with the characters `&', `--', `L', `R' 
  617. representing the node functions AND, OR, LEFT, RIGHT respectively.  Leaves 
  618. are stored as a number, representing the bit index, optionally preceded by a 
  619. `!' for negation.  The end of the tree is marked by a semicolon.  Returns 0 
  620. for success, 1 on failure.
  621.  
  622.  
  623. 3.15   LPATREE atree_fold(tree)
  624.  
  625. LPATREE tree;
  626.  
  627. This routine removes all LEFT and RIGHT nodes from tree and returns the 
  628. result.  This does not change the function represented by the tree, but the 
  629. resulting tree may be considerably smaller and hence faster to execute.
  630. Nodes and leaves that are discarded are added to the free lists.
  631.  
  632.  
  633. 3.16   LPFAST_TREE atree_compress(tree)
  634.  
  635. LPATREE tree;
  636.  
  637. This routine returns the fast_tree derived from tree.  A fast_tree is 
  638. essentially a list of leaves; each leaf includes two pointers to subsequent 
  639. leaves to evaluate, one for each possible result of evaluating the current 
  640. leaf.  It is the function of atree_compress() to calculate these two "next" 
  641. pointers for each leaf.  Experiments show that evaluating a fast_tree is 
  642. almost twice as fast as evaluating the corresponding folded atree.  This is 
  643. due to the fact that recursion is eliminated.  Fast_trees  are also slightly 
  644. more compact than the equivalent atree. Note that there is no "decompression" 
  645. routine, and there are no fast_tree  I/O routines.
  646.  
  647.  
  648. 3.17  int atree_fast_eval(tree, vec)
  649.  
  650. LPFAST_TREE tree;
  651. LPBIT_VEC vec;
  652.  
  653. This routine is the equivalent of atree_eval, but for fast_trees.
  654.  
  655.  
  656. 3.18   void atree_fast_print(tree)
  657.  
  658. LPFAST_TREE tree;
  659.  
  660. This routine writes a representation of tree to the file "fasttree.out".  
  661. Each line of output corresponds to a leaf and includes the leaf index, bit 
  662. numbers (possible preceded by a `!' to indicate negation), and the two "next" 
  663. pointers (shown as indices).  NULL pointers are represented by -1.
  664.  
  665.  
  666. 3.19   int atree_set_code(code, high, low, ...)
  667.  
  668. LPCODE_T code;
  669. double low;
  670. double high,
  671. int vector_count;
  672. int width;
  673. int dist;
  674.  
  675. This is the constructor function for the type code_t. If width is greater 
  676. than one, atree_set_code() calls atree_rand_walk() to get a random walk 
  677. containing vector_count vectors, with adjacent vectors having a Hamming 
  678. distance of dist between them.  This random walk will represent numbers in 
  679. the closed interval [low, high].  The functions atree_encode() and 
  680. atree_decode() translate floating point quantities and bit vectors, 
  681. respectively, into an index into the random walk.  atree_set_code() also 
  682. calculates the (real) distance between adjacent vectors and stores this in 
  683. the step field of code.
  684.  
  685. If width is equal to one, the code represents a boolean variable, and no 
  686. random walk is produced.  In this case low , high , and vector_count are 
  687. taken to be 0, 1, and 2 respectively.  The vector field will be set to point 
  688. to bit vectors of length one having the appropriate values.
  689.  
  690.  
  691. 3.20   int atree_encode (x, code)
  692.  
  693. double x;
  694. LPCODE_T code;
  695.  
  696. This routine returns the quantization level of x as represented by code.  To 
  697. obtain the corresponding bit vector, use the expression:
  698.  
  699.             my_bit_vec = code -> vector + atree_encode(x, code)
  700.  
  701. If the code is boolean (code -> width == 1), then atree_encode() returns 0 if
  702. x is 0, otherwise it returns 1.  For non-boolean codes, atree_encode() issues
  703. a warning if x  is out of range, and the output is clipped so that it lies
  704. within the range 0 .. code -> vector  - 1.
  705.  
  706. 3.21   int atree_decode(vec, code)
  707.  
  708. LPBIT_VEC vec;
  709. LPCODE_T code;
  710.  
  711. This routine returns the quantization level of vec as represented by code.
  712. To obtain the corresponding floating point value, use the expression:
  713.  
  714.         my_value = code -> low + (code -> step * atree_decode(vec, code)
  715.  
  716. The quantization level corresponds to the first bit vector stored in the 
  717. random walk having the smallest Hamming distance from vec.  If the code is 
  718. boolean, the quantization level is simply the value of vec (whose length must 
  719. be 1).
  720.  
  721.  
  722. 3.22   LPCODE_T atree_read_code(stream, code)
  723.  
  724. FILE *stream;
  725. LPCODE_T code;
  726.  
  727. This routine reads a coding from stream and fills the entries of the code 
  728. structure.  A NULL pointer is returned if any error or EOF is encountered.
  729.  
  730.  
  731. 3.23   int atree_write_code(stream, code)
  732.  
  733. FILE *stream;
  734. LPCODE_T code;
  735.  
  736. This routine writes the contents of code onto stream.  If the code is 
  737. boolean, the vector field is not written.  Returns 0 for success, 1 for 
  738. failure.
  739.  
  740.  
  741. 4   The bv Library
  742.  
  743. 4.1   LPBIT_VEC bv_create(length)
  744.  
  745. int length;
  746.  
  747. Creates a vector of length bits, where each bit is initialized to 0, and
  748. returns a long pointer to the bit vector.
  749.  
  750.  
  751. 4.2   LPBIT_VEC bv_pack(unpacked,length)
  752.  
  753. LPSTR unpacked;   
  754. int length;
  755.  
  756. This routine has been provided to make it easy for the programmer to produce 
  757. bit vectors. The routine is handed an array of characters containing the 
  758. value 0 or 1 (unpacked) and an integer length giving the number of bits. The 
  759. routine returns a long pointer to a bit_vec.
  760.  
  761.  
  762. 4.3   int bv_diff(v1,v2)
  763.  
  764. LPBIT_VEC v1;
  765. LPBIT_VEC v2;
  766.  
  767. This routine calculates the Hamming distance between v1 and v2, i.e.
  768.  
  769.                         weight (v1 XOR v2)
  770.  
  771. where weight is the number of 1 bits in a vector and XOR is the bitwise 
  772. exclusive-or operation. This routine is used to find the closest vector in a 
  773. random walk array to some arbitrary vector. Just search through the random 
  774. walk for the vector with the smallest difference from the vector of tree 
  775. output bits.  (Inefficient, but easier to understand than decoding an 
  776. algebraic code!).
  777.  
  778.  
  779. 4.4   LPBIT_VEC bv_concat(n,vectors)
  780.  
  781. int n;
  782. LPBIT_VEC far *vectors;
  783.  
  784. This routine is used by the programmer to join several bit vectors end-to-end 
  785. to give the string concatenation of the vectors. This routine is most 
  786. frequently used during the construction of training sets when elements of 
  787. several random walks have to be joined together to obtain an input vector to 
  788. a tree.
  789.  
  790. The parameter vectors is an array of LPBIT_VEC pointers, and the parameter n 
  791. states how many of them there are. Vector pointers are used to make this 
  792. routine a little faster since there is less copying involved.  A long pointer 
  793. to the concatenated bit_vec is returned.
  794.  
  795.  
  796. 4.5  void bv_print(stream, vector)
  797.  
  798. FILE *stream;
  799. LPBIT_VEC vector;
  800.  
  801. This is a diagnostic routine used to print out a bit_vec.
  802.  
  803.  
  804. 4.6   void bv_set(n,vec,bit)
  805.  
  806. int n;
  807. LPBIT_VEC vec;
  808. BOOL bit;
  809.  
  810. This routine allows the programmer to explicitly set (or reset) the nth bit 
  811. (0 to bit_vec.len - 1) bit in the vector vec to have the value in the 
  812. parameter bit.
  813.  
  814.  
  815. 4.7   BOOL bv_extract(n,vec)
  816.  
  817. int n;
  818. LPBIT_VEC vec;
  819.  
  820. This routine returns the value of the nth bit (0 to bit_vec.len - 1) in the
  821. bit vector vec.
  822.  
  823.  
  824. 4.8   BOOL bv_equal(v1,v2)
  825.  
  826. LPBIT_VEC v1;
  827. LPBIT_VEC v2;
  828.  
  829. This routine tests two bit vectors for equality.
  830.  
  831.  
  832. 4.9   LPBIT_VEC bv_copy(vec)
  833.  
  834. LPBIT_VEC vec;
  835.  
  836. This routine returns a copy of vec.
  837.  
  838.  
  839. 4.10   void bv_free(vector)
  840.  
  841. LPBIT_VEC vector;
  842.  
  843. This routine frees the memory used by a bit_vec; accessing a bit_vec after it
  844. has been freed is usually disastrous.
  845.  
  846.  
  847. 5   Windows Support Library
  848.  
  849. 5.1   void Windows_Interrupt(cElapsed)
  850.  
  851. DWORD cElapsed;     (DWORD is Windows for "unsigned long")
  852.  
  853. When called, this procedure allows Windows to multitask an atree application 
  854. with other Windows applications. This is accomplished with a PeekMessage() 
  855. call (see the Windows Programmer's Reference for more details). The 
  856. programmer may want to use this procedure during long tree evaluation and 
  857. training set generation loops, or during other processing where control may 
  858. not be passed back to the application's window procedure for lengthy periods 
  859. of time (the price you pay for non-preemptive multitasking!).  Since 
  860. PeekMessage() calls can be quite time consuming, this procedure will only 
  861. call PeekMessage() after cElapsed milliseconds have passed since the last 
  862. call to PeekMessage().  Experimentation has shown a value for cElapsed of 
  863. about 1500 to work fairly well.
  864.  
  865.  
  866. 5.2   LPSTR WinMem_Malloc(wFlags, wBytes)
  867.  
  868. WORD wFlags;    (WORD is Windows for "unsigned int(16-bit)")
  869. WORD wBytes;
  870.  
  871. Since the segmented memory architecture of DOS based PC's can cause great 
  872. grief when allocating large amounts of memory, the atree package includes its 
  873. own memory manager.  Requests for memory are obtained from dynamically 
  874. allocated segments from the global heap in which local heaps have been 
  875. initialized.  The memory is actually allocated by Windows' local heap 
  876. manager, and the resultant near (16 bit) pointer is combined with the global 
  877. segment descriptor of the corresponding global heap segment to form a long 
  878. (32 bit) pointer suitable for use in atree applications.  wFlags indicates 
  879. the kind of memory to allocate, usually LMEM_MOVEABLE, and wBytes indicate 
  880. the number of bytes to allocate.  See the Windows Programmer's Reference 
  881. LocalAlloc() routine for more information on the different values wFlags may 
  882. take.  For ease of use, the programmer may simply wish to use the 
  883. Malloc(wBytes) macro, which expands to
  884.  
  885. WinMem_Malloc (LMEM_MOVEABLE | LMEM_ZEROINIT, wBytes).
  886.  
  887.  
  888. 5.3  LPSTR WinMem_Free(lpfree)
  889.  
  890. LPSTR lpfree;
  891.  
  892. This function frees the block of memory pointed to by lpfree, which is 
  893. decomposed into a segment selector, which is used to identify the global 
  894. segment from which the near pointer was allocated from, and a near pointer, 
  895. which is used by Windows' LocalFree() to free memory from the local heap in 
  896. the dynamically allocated segment.  If there remains no more allocated memory 
  897. in the local heap the global segment is deallocated.  For ease of use, the 
  898. Free(lp) macro expands to WinMem_Free((LPSTR) lp).
  899.  
  900. The function returns NULL if successful, otherwise it returns lpfree.
  901.  
  902.  
  903. 6     The Language lf
  904.  
  905. The second major product included in the current release is the "lf" language 
  906. interpreter that allows a non- programmer to experiment with tree adaptation.  
  907. The user specifies a training set, and a test set, and selects the encoding 
  908. and quantization levels for a particular experiment. The interpreter checks 
  909. the statements for errors then executes the desired experiment, finally 
  910. outputting a table comparing the desired function with the function actually 
  911. learned. Various post-processors can use the information to produce 
  912. histograms of error or plots of the functions.
  913.  
  914. It is recommended that the user read and understand [Arms5] before using this
  915. language.
  916.  
  917. There are two versions of lf: LF.EXE and LFEDIT.EXE.  LF.EXE inputs a file
  918. "lf.in" and outputs to a file "lf.out".  LFEDIT.EXE is an interactive editor,
  919. but can only handle files of about 48K.  Use LF.EXE to test SPHERE.LF (after
  920. copying it to "lf.in") or other lf files larger than 48K.
  921.  
  922.  
  923. 6.1  multiply.lf
  924.  
  925. The language is best learned by examining an example. The file multiply.lf 
  926. contains a simple experiment where we are trying to teach the system the 
  927. multiplication table. The program is divided into a "tree" section which 
  928. describes the tree and the length of training, and a "function" section which 
  929. describes the data to be learned. Comments are started with a `#' mark and 
  930. continue to the end of the line.
  931.  
  932. # A comment.
  933. tree
  934.         size = 4000
  935.         min correct  = 144
  936.         max epochs  = 20
  937.  
  938. The tree and function sections can be in any order, in this particular 
  939. example the tree is described first.  Apart from comments, tabs and newlines 
  940. are not significant; the arrangement chosen above is only for readability.  
  941. The first line after tree tells the system how large the atree is going to 
  942. be. In this case we are choosing a tree with 4000 leaves (3999 nodes). We are
  943. going to train it until it gets 144 correct from the training set, or for 20
  944. complete presentations of the training set, whichever comes first.
  945.  
  946. Trees may also be read from a file with the "load tree from" statement.  If 
  947. this statement is specified, the tree size will be ignored and lf will output 
  948. a warning message.  Trees can be written to files using either the "save tree 
  949. to" or "save folded tree to" statements.
  950.  
  951. The statements in the tree section may be in any order.
  952.  
  953. function
  954.         domain dimension = 2
  955.         coding = 32:12 32:12 32:7
  956.         quantization = 12 12 144
  957.         training set size = 144
  958.         training set =
  959.  
  960.  
  961. 1       1       1
  962. 1       2       2
  963. 1       3       3
  964. 1       4       4
  965. ....
  966.  
  967.         test set size = 144
  968.         test set =
  969.  
  970. 1       1       1
  971. 1       2       2
  972. 1       3       3
  973. 1       4       4
  974. ....
  975.  
  976. The training set must start with a dimension statement which gives the number 
  977. of columns in the function table.  The domain dimension refers to the number 
  978. of input columns.  Lf supports training of multiple functions using the same 
  979. inputs.  This is done using the codomain dimension statement.  If the 
  980. codomain dimension statement is not specified, the number of codomains is 
  981. assumed to be 1 (as in the above example).  The total number of columns in 
  982. the training and test sets must equal the sum of the domain and codomain 
  983. dimensions (this doesn't mean a restriction on the format, just on what the 
  984. number of elements in the table must be).  In the above example, we are
  985. defining a problem with three columns: two input and one output.
  986.  
  987. The other statements may come in any order; note however that the definition
  988. of the training set size must be defined before the training set. This also
  989. applies to the test set definition.
  990.  
  991. The coding statement defines is a series of <width>:<step> definitions, one 
  992. for each column. The <width> is the number of bits in the bit vector for that
  993. column, the <step> is the step size of the walk in Hamming space that defines
  994. the encoding of this column. Because a tree only produces a single bit in
  995. response to an input vector, the <width> of the codomain columns (which come
  996. after the domain columns) actually defines how many trees will be learning 
  997. output bits of this function.
  998.  
  999. The quantization statement defines for each column the total number of coded 
  1000. bit vectors for that column.  Entries in the test and training sets are 
  1001. encoded into the nearest step, so this statement defines the accuracy 
  1002. possible.
  1003.  
  1004. Codings may also be read from a file using the "loading code from" statement.  
  1005. If this is specified, coding and quantization statements are ignored, and lf 
  1006. will warn the user.  Note that codings must be specified by a "read coding 
  1007. from" statement, or combinations of coding and quantization statements.
  1008.  
  1009. Codings can be saved to a file with the "save coding to" statement, which may
  1010. be placed anywhere in the function section.
  1011.  
  1012. The training set statement defines the actual function to be learned by the 
  1013. system. An entry in a table can be either a real number or an integer.  If 
  1014. the width of the a column (as specified by the coding) is 1, then that column 
  1015. is boolean.  For boolean columns, zero values are FALSE, and any non-zero 
  1016. value is considered TRUE.
  1017.  
  1018. The test set statement defines the test that is run on the trees at the end 
  1019. of training to see how well the learned function performs. Like the training 
  1020. set, reals or integers are acceptable.
  1021.  
  1022. After lf has executed, it produces a table of output showing how each element 
  1023. in the test set was quantized, and the value the trained tree returned.  
  1024. Consider the following results that multiply.lf produced.  Note that the 
  1025. quantization level is one less than the number represented.  This is because 
  1026. the range of numbers is from 1 to 144, and 0 corresponds to the first
  1027. quantization level.
  1028.  
  1029. 1
  1030. .....
  1031. 3.000000 2  11.000000 10    33.000000 32    33.000000 32
  1032. 3.000000 2  12.000000 11    36.000000 35    36.000000 35
  1033. 4.000000 3  1.000000 0      4.000000 3      4.000000 3
  1034. 4.000000 3  2.000000 1      8.000000 7      8.000000 7
  1035. 4.000000 3  3.000000 2      12.000000 11    12.000000 11
  1036. .....
  1037.  
  1038. Each column consists of two numbers, the entry specified by the user, and an
  1039. integer describing the quantization level it was coded into.
  1040.  
  1041. The fourth column is the result produced by the trained tree.  It shows the 
  1042. quantization level produced (the second figure) and how this may be 
  1043. interpreted in the space of the codomain (the first figure).
  1044.  
  1045.  
  1046. 6.2  sphere.lf
  1047.  
  1048. This lf example uses a spherical harmonic function Y2 defined by:
  1049.  
  1050.             Y2(µ, φ)  =  A0(3µ²/2 - 1/2)
  1051.                        + 3µ(1 - µ²)^½ [A1 cos φ + B1 sin φ]
  1052.                        + 3(1 - µ²) [A2 cos 2φ + B2 sin 2φ]
  1053.  
  1054. where A0 = 1.0, A1 = 0.4, B1 = 0.9, A2 = 2.4, B2 = 7.9.  The values of µ were
  1055. in the interval [0.0, 1.0], and the values of φ were in [0.0, π].  The values
  1056. of Y2 range between -26.0 and 26.0.
  1057.  
  1058. The µ and φ intervals were quantized into 100 levels each; the random walks
  1059. had 64 bits and a stepsize of 3.  The Y2 values were quantized into 100
  1060. levels, the random walk having 64 bits with a stepsize of 3.  Training 64
  1061. networks of 8191 elements on 1000 samples resulted in a function which,
  1062. during test on 1000 new samples, was decoded to the correct quantization
  1063. level, plus or minus three, 88.6\% of the time.  The error in the quantized
  1064. result was no more than nine quantization levels for all of the test samples.
  1065. (A slightly better learning algorithm got within three levels 95.8\% of the
  1066. time, and was always within eight levels.)
  1067.  
  1068. The function section introduces the optional "largest" and "smallest"
  1069. statements.  These may be used if the user needs to explicitly define the
  1070. largest and smallest values in the test and training sets. If they are
  1071. missing, lf will just use the largest and smallest values for each column in
  1072. both the test and training sets.
  1073.  
  1074. This problem takes about 80 minutes of CPU time on a Sun Sparcstation 1.  We
  1075. have included a sample set of results in the file sphere.out.  You will need 
  1076. to have at least 12Mb of RAM (either real or virtual) to run this problem!
  1077.  
  1078.  
  1079. 5.3  The Syntax of lf
  1080.  
  1081. The syntax has been defined using YACC. Tokens have been written in quotes to
  1082. distinguish them. Note that the following tokens are synonyms :-
  1083.  
  1084. dimension, dimensions
  1085. max, maximum
  1086. min, minimum
  1087.  
  1088. The syntax is defined as follows :-
  1089.  
  1090. program :       function_spec tree_spec
  1091.                 | tree_spec function_spec
  1092.  
  1093. function_spec : "function" dim codim function_statements
  1094.  
  1095. dim :           "domain dimension =" integer
  1096.  
  1097. codim:          /* empty */
  1098.                 | "codomain dimension =" integer
  1099.  
  1100. function_statements :   function_statement
  1101.                         | function_statements function_statement
  1102.  
  1103. function_statement :    quantization
  1104.                         | coding
  1105.                         | coding_io
  1106.                         | train_table_size
  1107.                         | train_table
  1108.                         | test_table_size
  1109.                         | test_table
  1110.                         | largest
  1111.                         | smallest
  1112.  
  1113. quantization :  "quantization =" quant_list
  1114.  
  1115. quant_list :    integer
  1116.                 | quant_list  integer
  1117.  
  1118. coding :        "coding =" code_list 
  1119.  
  1120. code_list :     integer ":" integer
  1121.                 | code_list integer ":" integer
  1122.  
  1123. coding_io:      "save coding to" string
  1124.                 | "load coding from" string
  1125.  
  1126. train_table_size :  "training set size =" integer
  1127.  
  1128. train_table :   "training set =" table
  1129.  
  1130. test_table_size :   "test set size =" integer
  1131.  
  1132. test_table  :   "test set =" table
  1133.  
  1134. table :         num
  1135.                 | table num
  1136.  
  1137. num :           real
  1138.                 | integer
  1139.  
  1140. largest :       "largest =" largest_list
  1141.  
  1142. largest_list :  num
  1143.                 | largest_list num
  1144.  
  1145. smallest :      "smallest =" smallest_list
  1146.  
  1147. smallest_list : num
  1148.                 | smallest_list num
  1149.  
  1150. tree_spec :     "tree" tree_statements
  1151.  
  1152. tree_statements :   tree_statement
  1153.                     | tree_statements tree_statement
  1154.                 
  1155. tree_statement :    tree_size
  1156.                     | tree_io
  1157.                     | min_correct
  1158.                     | max_correct
  1159.                     | max_epochs
  1160.  
  1161. tree_size :     "size =" integer
  1162.  
  1163. tree_io:        "save tree to" string
  1164.                 | "save folded tree to" string
  1165.                 | "load tree from" string
  1166.  
  1167. max_correct :   "min correct =" integer
  1168.  
  1169. max_epochs :    "max epochs =" integer
  1170.  
  1171.  
  1172. 7     Other Demonstrations
  1173.  
  1174. In this section we briefly present some boolean function problems which
  1175. atrees have solved.
  1176.  
  1177.  
  1178. 7.1   The Multiplexor Problem
  1179.  
  1180. A multiplexor is a digital logic circuit which behaves as follows: there are 
  1181. k input leads called control leads, and 2k leads called the "other" input 
  1182. leads.  If the input signals on the k control leads represent the number j in 
  1183. binary arithmetic, then the output of the circuit is defined to be equal to 
  1184. the value of the input signal on the jth one of the other leads (in some 
  1185. fixed order).  A multiplexor is thus a boolean function of n = k + 2k 
  1186. variables and is often referred to as an  n-multiplexor.
  1187.  
  1188. Here is a program to define a multiplexor with three control leads, v[2], 
  1189. v[1] and v[0], the fact that they are these particular variables being 
  1190. irrelevant due to randomization in the programs:
  1191.  
  1192. /* Windows window procedure and initialization omitted for clarity */
  1193.  
  1194. /* An eleven input multiplexor function test */
  1195.  
  1196. #include <stdio.h>
  1197. #include <stdlib.h>
  1198. #include <windows.h>
  1199. #include "atree.h"
  1200.  
  1201. #define TRAINSETSIZE 2000
  1202. #define WIDTH 11
  1203. #define TESTSETSIZE 1000
  1204. #define TREESIZE 2047
  1205.  
  1206. char multiplexor(v)
  1207.  
  1208.      char *v;
  1209.  
  1210. {
  1211.       return(v[ v[2]*4 + v[1]*2 + v[0] + 3]);
  1212. }
  1213.  
  1214. main(hInstance)
  1215.  
  1216. HANDLE hInstance;
  1217.  
  1218. {
  1219.     int i;
  1220.     int j;
  1221.     LPBIT_VEC training_set;
  1222.     LPBIT_VEC icres;
  1223.     LPBIT_VEC test;
  1224.     char vec[WIDTH];
  1225.     char ui[1];
  1226.     int correct = 0;
  1227.     LPATREE tree;
  1228.     char szBuffer[80];
  1229.  
  1230.     /* Initialise */
  1231.  
  1232.     training_set = (LPBIT_VEC) Malloc (TRAINSETSIZE * sizeof(bit_vec));
  1233.     MEMCHECK(training_set);
  1234.  
  1235.     icres = (LPBIT_VEC) Malloc (TRAINSETSIZE * sizeof(bit_vec));
  1236.     MEMCHECK(icres);
  1237.  
  1238.     atree_init(hInstance);
  1239.  
  1240.     /* Create the test data */
  1241.  
  1242.     MessageBox(NULL, "Creating training data", "Multiplexor", MB_OK);
  1243.  
  1244.     for (i = 0; i < TRAINSETSIZE; i++)
  1245.     {
  1246.         for (j = 0; j < WIDTH; j++)
  1247.         {
  1248.             vec[j] = RANDOM(2);
  1249.         }
  1250.         training_set[i] = *(bv_pack(vec,WIDTH));
  1251.         ui[0] = multiplexor(vec);
  1252.         icres[i] = *(bv_pack(ui,1));
  1253.     }
  1254.  
  1255.     /* Create a tree and train it */
  1256.  
  1257.     MessageBox(NULL,"Training tree", "Multiplexor", MB_OK);
  1258.  
  1259.     tree = atree_create(WIDTH,TREESIZE);
  1260.     (void) atree_train(tree,training_set,icres,0,TRAINSETSIZE,
  1261.                        TRAINSETSIZE-1,100,1);
  1262.  
  1263.     /* Test the trained tree */
  1264.  
  1265.     MessageBox(NULL,"Testing the tree", "Multiplexor", MB_OK);
  1266.  
  1267.     for (i = 0; i < TESTSETSIZE; i++)
  1268.     {
  1269.         for (j = 0; j < WIDTH; j++)
  1270.         {
  1271.             vec[j] = RANDOM(2);
  1272.         }
  1273.         test = bv_pack(vec,WIDTH);
  1274.         if (atree_eval(tree,test) == multiplexor(vec))
  1275.         {
  1276.             correct++;
  1277.         }
  1278.         bv_free(test);
  1279.     }
  1280.  
  1281.     wsprintf(szBuff,"%d correct out of %d in final test",correct,TESTSETSIZE);
  1282.  
  1283.     /* discard training set */
  1284.     for (i = 0; i < TESTSETSIZE; i++)
  1285.         {
  1286.         Free(training_set[i].bv);
  1287.         Free(icres[i].bv);
  1288.         }
  1289.  
  1290.     Free(training_set);
  1291.     Free(icres);
  1292.  
  1293.     /* Discard tree */
  1294.     atree_free(tree);
  1295.  
  1296.     return;
  1297. }
  1298.  
  1299. This problem was solved to produce a circuit testing correctly on 99.4% of 
  1300. 1000 test vectors in 19 epochs, or about 530 seconds on a Sun 3/50.  The time 
  1301. may vary considerably depending on the random numbers used.  It is possible 
  1302. to learn multiplexors with twenty inputs (four control leads) with a 
  1303. straightforward but improved adaptation procedure, and multiplexors with up 
  1304. to 521 leads (nine of them control leads) using much more elaborate 
  1305. procedures which change the tree structure during learning [Arms5].
  1306.  
  1307.  
  1308. 7.2   The Mosquito Problem
  1309.  
  1310. Suppose we are conducting medical research on malaria, and we don't know yet 
  1311. that malaria is caused by the bite of an anopheles mosquito, unless the 
  1312. person is taking quinine (in Gin and Tonics, say) or has sickle-cell anaemia.  
  1313. We are inquiring into eighty boolean-valued factors of which "bitten by 
  1314. anopheles mosquito", "drinks Gin and Tonics", and "has sickle-cell anaemia" 
  1315. are just three.  For each of 500 persons in the sample, we also determine 
  1316. whether or not the person has malaria, represented by another boolean value, 
  1317. and we train a network on that data.  We then test the learned function to 
  1318. see if it can predict, for a separately-chosen test set, whether person whose 
  1319. data were not used in training has malaria.
  1320.  
  1321. Suppose on the test set, the result is 100% correct. (Training and test can 
  1322. be done in about five seconds on a Sun Sparcstation 1.)  Then it would be 
  1323. reasonable to analyze the function produced by the tree, and note all the 
  1324. variables among the eighty that are not involved in producing the result.  A 
  1325. complete data analysis system would have means of eliminating subtrees "cut 
  1326. off" by LEFT or RIGHT functions (such as atree_compress()), to produce a 
  1327. simple function which would help the researcher understand some factors 
  1328. important for the presence of the disease.  If there were extraneous 
  1329. variables still left in the function in one trial, perhaps they would not 
  1330. show up in a second trial, so that one could see what variables are 
  1331. consistently important in drawing conclusions about malaria.
  1332.  
  1333. We apologize for the simplistic example, however we feel the technique of 
  1334. data analysis using these trees may be successful in cases where there are 
  1335. complex interactions among features which tend to mask the true aetiology of 
  1336. the disease.
  1337.  
  1338. The code for the problem can be found in mosquito.c.
  1339.  
  1340.  
  1341. 8     References
  1342.  
  1343. [Arms]  W. W. Armstrong, J. Gecsei: Adaptation Algorithms for Binary Tree 
  1344.     Networks, IEEE Trans. on Systems, Man and Cybernetics, SMC-9 (1979), pp. 
  1345.     276 - 285.
  1346.  
  1347. [Arms2] W. W. Armstrong, Adaptive Boolean Logic Element, U. S. Patent 
  1348.     3,934,231, Jan. 20, 1976, assigned to Dendronic Decisions Limited.
  1349.  
  1350. [Arms3] W. W. Armstrong, J. Gecsei, Architecture of a Tree-Based Image 
  1351.     Processor, 12th Asilomar Conf.  on Circuits, Systems and Computers, IEEE 
  1352.     Cat. No. 78CHI369-8 C/CAS/CS Nov. 1978, 345-349.
  1353.  
  1354. [Arms4] W. W. Armstrong, G. Godbout, Properties of binary trees of flexible 
  1355.     elements useful in pattern recognition, Proc. IEEE Int'l. Conf. on 
  1356.     Cybernetics and Society, San Francisco (1975) 447 - 450.
  1357.  
  1358. [Arms5] W. W. Armstrong, Jiandong Liang, Dekang Lin, Scott Reynolds, 
  1359.     Experiments using Parsimonious Adaptive Logic, Technical Report  TR 
  1360.     90-30, Department of Computing Science, University of Alberta, September 
  1361.     1990.
  1362.  
  1363. [Boch]  G. v. Bochmann, W. W. Armstrong: Properties of Boolean Functions with 
  1364.     a Tree Decomposition, BIT 14 (1974), pp. 1 - 13.
  1365.  
  1366. [Hech]  Robert Hecht-Nielsen, Neurocomputing, Addison-Wesley,  1990.
  1367.  
  1368. [Meis]  William S. Meisel, Parsimony in Neural Networks, Proc. 
  1369.     IJCNN-90-WASH-DC, vol. I, pp. 443 - 446.
  1370.  
  1371. [Rume]  D. E. Rumelhart and J. L. McClelland: Parallel Distributed 
  1372.     Processing, vols. 1&2, MIT Press, Cambridge, Mass. (1986).
  1373.  
  1374. [Smit]  Derek Smith, Paul Stanford: A Random Walk in Hamming Space, Proc. 
  1375.     Int. Joint Conf. on Neural Networks, San Diego, vol. 2  (1990) 465 - 470.
  1376.