[comp.ai.neural-nets] PDP 3 Bug Fixes

richardd@syma.sussex.ac.uk (Richard Dallaway) (01/11/90)

These are the bug fixes for the R&M PDP software that
were posted to the net last year.

Richard
________________
Richard Dallaway
School of Cognitive and Computing Sciences,
University of Sussex, Falmer, Brighton, UK.
richardd@cogs.sussex.ac.uk

----------------------------------------------------------------------
/home/csuna/richardd/misc/pdp3bugfixes
last update: April 21, 1989

         CONTENTS - (Use <ENTER> g to access required sections)

 -- CHANGES TO PDP SOFTWARE AND HANDBOOK
 -- DESCRIPTION OF CHANGES TO SOURCE FILES
 -- DIFF RESULTS
 -- DIFFERENCES BETWEEN OLD AND NEW VERSIONS OF bp/cas.tem
 -- ERRATA BY PAGE NUMBER IN THE HANDBOOK
 -- PROBLEMS IN VERSION 1.1 OF THE SOFTWARE
 -- FIX FOR WEIGHTS.C (April 21, 1989)

-- CHANGES TO PDP SOFTWARE AND HANDBOOK ------------------------------

For the second printing of the book "Explorations in parallel
distributed processing: A handbook of models, programs
and exercises", by J. L. McClelland and D. E. Rumelhart,
a small number of changes to the PDP software
have been made, and version 1.1 of the software has
been constructed.

The changes to the software are all either bug fixes or
minor cosmetic changes.  The most important fixes
resolve a set of related problems with constraints
on weights in the bp program:  In the old version,
chaos could result when weights were linked
together if the network contained more than 100 units,
or if more than 100 weights were linked together
or constrained to be positive or negative.
The necessary changes are all in the file weights.c,
and are listed below.

In addition to the changes to the sources, a few errors
in the handbook text have been detected, and many of these
errors have been corrected in the second printing.
The bp template file cas.tem has also been changed to
allow logging of activations to occur as described in
the handbook text.

Here is a description of the changes to the source files,
followed by the results of running the unix diff program
on the old and new versions.  Then a diff of the old
and new versions of bp/cas.tem is provided.  Following this,
the errors in the text of the handbook are described.

At the very end, fixes for new problems that have arisen
with Version 1.1 are described.

-- DESCRIPTION OF CHANGES TO SOURCE FILES -----------------------------         

in all .c files:

    removed "(void)" cast on all calls to install_var and 
    install_command. (these functions are now declared as void 
    explicitly, see below).

bp.c:
    in logistic (lines 217,220):
    
	replaced constant 16.0 with 15.935773
        (15.935773 is the number whose logistic is equal
        to .99999988, which is retured if 15.935773 is exceeded)

command.c:

    declared install_command as void.
    
    in do_command:
    
	the second argument to do_command was usually passed
	as a pointer but interpreted by do_command as an integer.
	This can lead to problems when pointers and integers have
	different sizes.  To rectify the problem, do_command now
	interprets its second argument as a pointer, than converts
	it explicitly to an integer, storing the result in the 
	integer variable Currrent_Menu, which is then used inside
	the body of the do_command function.
    
    in contin_test, do_comfile, and file_error:
    
	cast second argument to do_command as (int *)

    in do_comfile:
    
	fixed bug that caused programs to crash
	if a return was entered in response to "How many times?"
	query.

command.h:

    declared install_command as void.

general.c:

    in emalloc and erealloc:
    
    	caused the program to exit when it runs out of
	memory rather than returning a null pointer to
	the caller.

main.c:

    changed version number to 1.1
    
    deleted "stuct Variable *install_var ();" declaration.
    
    in main:
    
    	cast second argument to do_command as (int *)
	both times it is called.

variable.c:

    declared install_var as void and commented out return statement
    in this function.

variable.h:

    declared install_var as void here too.

weights.c:

    in define_network (line 620):
    
	fixed program to null constraint vectors up to
	constraints[i].max rather than nunits.

    above enlarge_constraints (old line 787):
    
    	added #define CON_INCR 100
    
    in enlarge_constraints:
    
    	replaced 100 with CON_INCR throughout
	old lines 789,796, replaced '=' with '==' in
		conditionals.
	added code for nulling fresh portions of 
		positive_constraints,
		negative_constraints,
		constraints[i].cvec,ivec

-- DIFF RESULTS -------------------------------------------------------    

Here follows a diff of versions 1.0 and 1.1.  This diff was
performed after all of the "(void)" casts on calls to
install_var and install_command were deleted from
version 1.0.  Only files with changes are listed.

The diff tells what should be done to the old version to
convert it into the new.  Lines beginning with < come from
version 1.0, lines beginning with > come from version 1.1.

====================
bp.c
====================
217c217
<       if (x > 16.0)
---
>       if (x > 15.935773)
220c220
< 	if (x < -16.0)
---
> 	if (x < -15.935773)
====================
command.c
====================
50c50
< int     current_menu;
---
> int     *current_menu;
60c60
<     Current_Menu = current_menu;
---
>     Current_Menu = (int) current_menu;
62c62
<     	do_help(str,current_menu);
---
>     	do_help(str,Current_Menu);
68c68
<     	if (current_menu == BASEMENU) return(POP);
---
>     	if (Current_Menu == BASEMENU) return(POP);
75c75
< 	if ((!Command[i].menutype) || (Command[i].menutype == current_menu)) {
---
> 	if ((!Command[i].menutype) || (Command[i].menutype == Current_Menu)) {
87c87
< 		    (Command[i].menutype == current_menu)) {
---
> 		    (Command[i].menutype == Current_Menu)) {
101c101
< 	if( current_menu == DISPLAYMENU) {
---
> 	if( Current_Menu == DISPLAYMENU) {
104c104
< 	        if ((Command[i].menutype == current_menu)) {
---
> 	        if ((Command[i].menutype == Current_Menu)) {
123c123
< 		        (Command[i].menutype == current_menu)) {
---
> 		        (Command[i].menutype == Current_Menu)) {
260c260,264
< install_command(str, func, menu, intp)
---
> /* declare this as void so we don't have to cast all calls to install_command 
> 	abh - 2/15/88 
> */
> 
> void install_command(str, func, menu, intp)
422c426
<         	if (do_command(subprompt, BASEMENU) == POP) break;
---
>         	if (do_command(subprompt, (int *) BASEMENU) == POP) break;
435,436c439,440
< 	int echeck;
< 	int nreps,i;
---
> 	int echeck,i;
> 	int nreps = 0;
449d452
< 	nreps = 1;
451c454,458
< 	sscanf(str,"%d",&nreps);
---
> 	echeck = 0;
> 	if (str) echeck = sscanf(str,"%d",&nreps);
> 	if (echeck == 0) {
> 	  return(put_error("Integer argument missing in do command."));
> 	}
456c463
< 	    if (do_command(Prompt, BASEMENU) == BREAK) {
---
> 	    if (do_command(Prompt, (int *) BASEMENU) == BREAK) {
492c499
<         	if (do_command(subprompt, BASEMENU) == BREAK) break;
---
>         	if (do_command(subprompt, (int *) BASEMENU) == BREAK) break;
====================
command.h
====================
56c56
< int     install_command ();
---
> void     install_command ();
====================
general.c
====================
79c79
<     if (p == 0)
---
>     if (p == 0) {
80a81,83
> 	end_display();
> 	exit(0);
>     }
94c97
<     if (p == 0)
---
>     if (p == 0) {
95a99,101
> 	end_display();
> 	exit(0);
>     }
104c110
<     if (p == 0) 
---
>     if (p == 0) {
105a112,114
> 	end_display();
> 	exit(0);
>     }
====================
main.c
====================
29c29
< char	version[10] = "1.0";
---
> char	version[10] = "1.1";
36d35
<     struct Variable *install_var ();
74c73
< 		   do_command(Prompt, BASEMENU);
---
> 		   do_command(Prompt, (int *) BASEMENU);
94c93
< 	do_command(Prompt, BASEMENU);
---
> 	do_command(Prompt, (int *) BASEMENU);
====================
variable.c
====================
55c55
< 
---
> /* we replace this:
57c57,60
< install_var (s, t, varptr, max_x, max_y, menutype)
---
>    with void to avoid the hassle of casting to void on each call 
>    abh - 2/15/88
> */
> void install_var (s, t, varptr, max_x, max_y, menutype)
84a88
> /*
85a90
> */
====================
variable.h
====================
31c31
< struct Variable *install_var ();
---
> void install_var ();
====================
weights.c
====================
620c620
< 	    for (j = 0; j < nunits; j++) {
---
> 	    for (j = 0; j < constraints[i].max; j++) {
787a788,789
> #define CON_INCR 100 /* increment in size of constriant */
> 
789,790c791,794
<     if (con_index = ENLARGE_POS) {
< 	maxpos += 100;
---
>     int j;
>     
>     if (con_index == ENLARGE_POS) {
> 	maxpos += CON_INCR;
793c797
< 	      (unsigned int) ((maxpos - 100) * sizeof(float *)),
---
> 	      (unsigned int) ((maxpos - CON_INCR) * sizeof(float *)),
794a799,801
> 	for (j = maxpos - CON_INCR; j < maxpos; j++) {
> 		positive_constraints[j] = NULL;
> 	}
796,797c803,804
<     else if (con_index = ENLARGE_NEG) {
< 	maxneg += 100;
---
>     else if (con_index == ENLARGE_NEG) {
> 	maxneg += CON_INCR;
800c807
< 	     (unsigned int) ((maxneg -100) * sizeof (float *)),
---
> 	     (unsigned int) ((maxneg -CON_INCR) * sizeof (float *)),
801a809,811
> 	for (j = maxneg - CON_INCR; j < maxneg; j++) {
> 		negative_constraints[j] = NULL;
> 	}
804c814
< 	constraints[con_index].max += 100;
---
> 	constraints[con_index].max += CON_INCR;
808c818
< 	     ((constraints[con_index].max - 100) * sizeof(float *)),
---
> 	     ((constraints[con_index].max - CON_INCR) * sizeof(float *)),
814c824
< 	     ((constraints[con_index].max - 100) * sizeof(float *)),
---
> 	     ((constraints[con_index].max - CON_INCR) * sizeof(float *)),
816a827,831
> 	for (j = constraints[con_index].max - CON_INCR;
> 		j < constraints[con_index].max; j++) {
> 	    constraints[con_index].cvec[j] = NULL;
> 	    constraints[con_index].ivec[j] = NULL;
> 	}

-- DIFFERENCES BETWEEN OLD AND NEW VERSIONS OF bp/cas.tem -------------    

All of the changes apply only to the dlevel variable:  
dlevels of epochno and tss are set to 2 and dlevels of 
cpname, cycleno, and output are set to 1.  Once again, 
the diff indicates how to change the old version
into the new version.  

15,17c15,17
< epochno	variable 1	$	n	epochno	5 1.0
< tss	floatvar 1	$   	n	tss	7 1.0 
< cpname	variable 2	$	5	cpname -5 1.0
---
> epochno	variable 2	$	n	epochno	5 1.0
> tss	floatvar 2	$   	n	tss	7 1.0 
> cpname	variable 1	$	5	cpname -5 1.0
19c19
< cycleno variable 2	$	n	cycleno 5 1.0
---
> cycleno variable 1	$	n	cycleno 5 1.0
27c27
< output	 vector	 2	$	n	activation v 3  100.0 2  3
---
> output	 vector	 1	$	n	activation v 3  100.0 2  3


-- ERRATA BY PAGE NUMBER IN THE HANDBOOK ------------------------------        

[Those marked with * have been corrected in the second printing]

Page 51: In Equation 2, each pair of units contributes only 
one time to the first summation (w sub ij is assumed equal 
to w sub ji).  The total goodness is not the sum of the goodnesses
of the individual units.  It is best to think of the
goodness of unit i as the set of terms in the total
goodness to which the activation of unit i contribues.

*Page 67, lines 6 and 7:  Two occurrences of "display/ ipattern"
should read "display/ env".

*Page 85, 2nd line from bottom:  The word "two" should be "four".

Page 148, first lines 6 and 10: In both cases the word "left"
should be replaced with the word "right".

*Page 148, 12th line from bottom:  The first word on the line,
"weights", should be "bias".

*Page 296, 8th line of Q.4.1.1:  The word "input" should be "output".

-- PROBLEMS IN VERSION 1.1 OF THE SOFTWARE ----------------------------          

The file jets.str in the cs directory uses the names 20s 30s and 40s
for three of the units.  The names should be changed (by editing the
file) to in20s in30s and in40s respectively.  As is these units
names cannot be used because the program interprets them as unit
numbers.  NOTE:  This only applies to the file jets.str in the cs
directory.  The file jets.str in the iac directory is ok as is.


-- FIX FOR WEIGHTS.C (April 21, 1989) ---------------------------------

Subject: Cure for a PDP large .net bug.
From:    jdg@antique.UUCP (John Gabbe)
Organization: AT&T Bell Labs, Holmdel, NJ
Date:    Fri, 24 Feb 89 09:19:34 -0500

James McClelland accepted this fix for later versions of the PDP program.
For those of you who have earlier versions the following may be helpful.

The PDP software file ../src/weights.c, contains errors in the segments
reproduced below.  The pertinent lines are marked with an E# or C in the
left margin, which serves as a key to the comments that follow the code.

                * * * * * * * * * * * * * * * * * * * *

/*

       This file is part of the PDP software package.

       Copyright 1987 by James L. McClelland and David E. Rumelhart.

       Please refer to licensing information in the file license.txt,
       which is in the same directory with this source file and is
       included here by reference.
*/


/* file: weights.c

        read in network descriptions, and set up constraints.

        First version implemented by Elliot Jaffe.

        Date of last revision: 8-12-87/JLM.
*/
line 21
 .
 .
 .
line 609
    if (nlinks) {
        constraints = (struct constraint   *)
          emalloc ((unsigned int)(sizeof (struct constraint) * (nlinks + 1)));
        for (i = 0; i < nlinks; i++) {
            constraints[i].num = 0;
            constraints[i].max = MAXCONSTRAINTS;
            constraints[i].cvec = ((float **)
                emalloc((unsigned int)(sizeof(float *) * MAXCONSTRAINTS)));
            constraints[i].ivec = ((float **)
                emalloc((unsigned int)(sizeof(float *) * MAXCONSTRAINTS)));
E1      for (j = 0; j < nunits; j++) {
                constraints[i].cvec[j] = NULL;
                constraints[i].ivec[j] = NULL;
            }
        }
    }
line 626
 .
 .
 .
line 783
/* realloc positive_constraints, negative_constraints, and link constraints
   this is called whenever the allocated constraint lists run out of
   space for additional constraints  14-May-87 MAF / 15-May-87 JLM */

enlarge_constraints(con_index) int con_index; {
E2  if (con_index = ENLARGE_POS) {
C       maxpos += 100;
        positive_constraints = ((float **) erealloc
          ((char *) positive_constraints,
C             (unsigned int) ((maxpos - 100) * sizeof(float *)),
              (unsigned int) (maxpos * sizeof(float *))));
    }
E3  else if (con_index = ENLARGE_NEG) {
C       maxneg += 100;
        negative_constraints = ((float **) erealloc
          ((char *) negative_constraints,
C            (unsigned int) ((maxneg -100) * sizeof (float *)),
             (unsigned int) (maxneg * sizeof(float *))));
    }
    else {
C       constraints[con_index].max += 100;
        constraints[con_index].cvec = ((float **) erealloc
          ((char *)constraints[con_index].cvec,
           (unsigned int)
C            ((constraints[con_index].max - 100) * sizeof(float *)),
           (unsigned int)
             (constraints[con_index].max * sizeof(float *))));
        constraints[con_index].ivec = ((float **) erealloc
          ((char *)constraints[con_index].ivec,
           (unsigned int)
C            ((constraints[con_index].max - 100) * sizeof(float *)),
           (unsigned int)
             (constraints[con_index].max * sizeof(float *))));
E4 }
}

                * * * * * * * * * * * * * * * * * * * *

E1.     At the line marked E1, MAXCONSTRAINTS items have just been emalloc'd
        but nunits items are NULL'ed. When nunits > MAXCONSTRAINTS, unallocated
        memory is overwritten destroying (on Suns and Vaxen) malloc's
        bookkeeping and leading to a segmentation error.  This line should
        be replaced by:

                for (j = 0; j < nunits && j < MAXCONSTRAINTS; j++) {

E2, E3. These should be compares, not assignments. Replace `=' with `=='.
        As the code is written, positive_constraints is always erealloc'd
        and nothing else is ever erealloc'd. 

E4.     If it is necessary to NULL the pointers at E1, then it should
        be necessary to NULL the additional space allocated here.
C.      For consistency, all the 100's should be replaced by MAXCONSTRAINTS.
        The following code inserted at E4 will then initialize the
        additional space:

                for (j = constraints[con_index].max - MAXCONSTRAINTS;
                                j < constraints[con_index].max;
                                j++) {
                        constraints[con_index].cvec[j] = NULL;
                        constraints[con_index].ivec[j] = NULL;
                }


These comments are provided as a courtesy; no claims are made or
responsibility assumed regarding their correctness.

Yours truly,

John D. Gabbe
AT&T Bell Laboratories

copy to netnews comp.ai.neural-nets

P.S.  In the official revision, McClelland introduced an additional constant
      to implement comment C, instead of overloading MAXCONSTRAINTS as is
      done above. - JDG 2/24/89.