home *** CD-ROM | disk | FTP | other *** search
- /***********************************************************
- OUTSTAR Simulator
-
- ⌐Maureen Caudill
- Adaptics
- 16776 Bernardo Center Drive
- Suite 110 B
- San Diego, CA 92128
- (617) 451-3752
- July, 1988
-
- Written in Lightspeed C (v. 2.15) on Macintosh
- Version 1.0
-
- This outstar learns to reproduce a specified pattern
- on a grid of border neurodes. The pattern to be learned
- is read from an external text file called "pattern"
-
- ---------------------------------------------------------------------------------
- OPERATION OF THIS SIMULATOR
-
- Read AI Expert Article, November, 1988 for discussion of Grossberg
- Learning, Activation Equations and Instar/Outstar.
-
- This simulator models a biological system. The outstar neurode stimulates
- each of a 7x10 grid of neurodes at the same time as an external pattern
- stimulates the grid. The initial weight connections between the outstar
- and the grid neurodes are randomly set between -0.5 and +0.5. The initial
- activity in the grid is also randomly set to activity between 0.0 and 0.5.
-
- To run the simulator, you need to have a pattern file which contains the
- pattern you want the grid to learn to reproduce when stimulated by the outstar.
- The program begins by initializing the weights, activations, reading the data
- file and so on. Then the values of the Grossberg learning/activation constants
- are checked, with default values assumed initially. I suggest that you leave
- these values as they are until you are sure you understand the learning laws.
-
- The menu gives you four choices.
- 1. Train the network for some specified number of time units.
- 2. Allow the activation in the grid to decay for some number of time units
- 3. Test the network by having the outstar stimulate the grid for some number
- of time units.
- -1. Reset the network (and possibly change the network constants) to either
- quit or start over.
- When asked to enter the number of time units (for 1 or 2 or 3), remember that
- a negative number will only display the grid activation after that number of time
- units has passed, while a positive number displays the grid activation after each
- time unit.
-
- The proper operation of the simulator is as follows:
- train the network for some number of time periods. Remember that the
- transmission constant t0 determines how many time units must pass before
- the grid even sees the outstar's stimulus (the external pattern is applied
- immediately). Thus, if the t0 constant is 3, you should do NOTHING in less
- than 4 time units (3 to travel, and 1 to have an effect on the grid).
- allow the grid activation levels to decay to zero. No weight changes occur
- during this time (why?), so you are effectively just "clearing the slate".
- test the network (again for t0+1 time units). If the performance is inadequate,
- train again for additional time periods, then allow decay, then test.
- Have fun!
- -----------------------------------------------------------------------------------------
-
- ---------------------------------------------------------------------------------
- STRUCTURE OF THIS FILE
-
- include files
- constant definitions
- general global storage
- Grossberg activation/learning constant storage (global)
-
- ╤╤ Major functions ╤╤
- initialize() ;; initialize network operations
- train() ;; train the grid for a specified time
- decay() ;; allow grid activation to decay a specified time
- test() ;; test the grid for a specified time
- ╤╤ Utility functions ╤╤
- compute_activation() ;; compute current activation for a grid neurode
- change_weight() ;; modify weight of a grid neurode
- read_pattern() ;; read pattern from data file
- parseline() ;; parse one line of data from file
- randomize_wts() ;; randomize weights on grid neurodes
- randomize_activity() ;; randomize activity of grid neurodes
- set_constants() ;; set Grossberg equation constants
- show_constants() ;; show current values of learning/activity constants
- show_wts() ;; print current weights on grid neurodes
- displaygrid() ;; print the current grid activation
- print_menu() ;; print menu of operator choices (train, test, decay, quit)
- ╤╤ Main control function ╤╤
- main() ;; main control function
- ------------------------------------------------------------------------------------
- *****************************************************************************************/
-
- #include <math.h>
- #include <stdio.h>
-
- #define ROWSIZE 7
- #define COLSIZE 10
- #define STRINGSIZE 80
- #define QUIT -1
- #define STIMMASK 1
- #define ACTIVATION 1
- #define LEARNING 2
- #define DISPLAYON 1
- #define DISPLAYOFF 2
- #define STIM 1
- #define NOSTIM 0
-
- /************* General Global Storage ***************************************************/
- double gridwts[ROWSIZE][COLSIZE]; /* this stores only the weights from the
- single outstar neurode to the grid of rim
- neurodes. */
- int pattern[ROWSIZE][COLSIZE]; /* this contains the pattern to be impressed
- on the grid of rim neurodes */
- double activity[ROWSIZE][COLSIZE]; /* this contains the current activation levels
- of each grid neurode */
- int curtime; /* current time (in integral units) */
- double outstar; /* activation level of outstar */
- char *inpath = "pattern"; /* file containing pattern to be learned */
- unsigned int outstar_save; /* saves history of outstar's output */
-
- /*****************************************************************************************/
-
- /************* Grossberg Activation Constants (set by user or default values) ************/
- double A = 0.9; /* activation decay constant */
- double T = 0.0; /* threshold */
- int t0 = 1; /* transmission time between neurodes */
- double F = 0.01; /* forgetting constant for long term memory */
- double G = 0.2; /* learning constant for Hebbian learning term */
-
- /*************************************************************
- initialize()
- initializes the system by:
- 1. reading in the pattern file
- 2. randomizing the weights
- 3. setting the current time to 0
- 4. establishing the activation/learning constants
- **************************************************************/
- initialize()
- {
- read_pattern(); /* read in training pattern from file */
- randomize_wts(); /* randomize weights to grid neurodes from outstar */
- randomize_activity(); /* randomize activity of grid neurodes */
- show_wts(); /* display resulting grid neurode weights */
- curtime = 0; /* reset current time to 0 */
- displaygrid(curtime); /* display the initial activity of the grid */
- set_constants(); /* set the constants to user specified values */
- return;
- }
- /*************************************************************
- train(duration)
- trains the outstar and grid for "duration" timeunits.
- weights are modified during this training period
- After each synchronous update of the grid, the
- activations are displayed.
-
- If training time is negative, only the grid status after
- all "duration" time units will be displayed.
-
- **************************************************************/
- train()
- {
- int duration, displayflag;
- int stoptime;
- int i,j;
- int stim_grid, stim_outstar;
-
- /* ask how many time units to train */
- printf("\n How many time units do you want the network to train? ");
- printf("\n (Integer value < 32767, negative suppresses all but final display) ");
- scanf("%d", &duration);
-
- displayflag = DISPLAYON;
-
- if (duration<0)
- {
- duration = -duration;
- displayflag = DISPLAYOFF;
- }
- stoptime = curtime+duration;
- for ( ; curtime<stoptime; curtime++)
- {
- stim_grid = STIM;
- stim_outstar = STIM;
- printf("\n Current time = %d",curtime);
- save_outstim(stim_outstar);
- for (i=0; i<ROWSIZE; i++)
- {
- for (j=0; j<COLSIZE; j++)
- {
- compute_activation(i,j,stim_grid, stim_outstar);
- change_weight(i,j,stim_outstar);
- }
- }
- if(displayflag == DISPLAYON)
- displaygrid();
- }
- curtime--; /* decrement to avoid using an extra time unit */
-
- /* when complete, if have not been updating display, do a final display of status */
- if (displayflag == DISPLAYOFF)
- displaygrid();
-
- return;
- }
-
- /*************************************************************
- decay(duration)
- allows grid activation to decay for "duration" timeunits.
- no weights are modified during this period, since
- stimulations from the outstar are 0.0
- After each synchronous update of the grid, the
- activations are displayed.
-
- If decay time is negative, only the grid status after
- all "duration" time units will be displayed.
-
- **************************************************************/
- decay()
- {
- int duration;
- int displayflag;
- int stoptime;
- int i,j;
- int stim_grid, stim_outstar;
-
- /* ask how many time units to decay */
- printf("\n How many time units do you want the network to decay? ");
- printf("\n (Integer value < 32767, negative suppresses all but final display) ");
- scanf("%d", &duration);
-
- displayflag = DISPLAYON;
- if (duration<0)
- {
- duration = -duration;
- displayflag = DISPLAYOFF;
- }
-
- stim_grid = NOSTIM; /* during decay, no external stimulation of grid */
- stim_outstar = NOSTIM; /* during decay, the outstar does not stimulate grid */
-
- stoptime = curtime+duration;
- for ( ; curtime<stoptime; curtime++)
- {
- printf("\n Current time = %d",curtime);
- save_outstim(stim_outstar);
- for (i=0; i<ROWSIZE; i++)
- {
- for (j=0; j<COLSIZE; j++)
- {
- compute_activation(i,j,stim_grid, stim_outstar);
- }
- }
- if(displayflag == DISPLAYON)
- displaygrid();
- }
- curtime--;
- /* when complete, if have not been updating display, do a final display of status */
- if (displayflag == DISPLAYOFF)
- displaygrid();
-
- return;
- }
-
- /*************************************************************
- test(duration)
- tests the outstar and grid for "duration" timeunits.
- weights are not modified during this training period
- After each synchronous update of the grid, the
- activations are displayed.
-
- If testing time is negative, only the grid status after
- all "duration" time units will be displayed.
-
- **************************************************************/
- test()
- {
- int duration, displayflag;
- int stoptime;
- int i,j;
- int stim_grid, stim_outstar;
-
- /* ask how many time units to test */
- printf("\n How many time units do you want the network to test? ");
- printf("\n (Integer value < 32767, negative suppresses all but final display) ");
- scanf("%d", &duration);
-
- displayflag = DISPLAYON;
- if (duration<0)
- {
- duration = -duration;
- displayflag = DISPLAYOFF;
- }
-
- stim_grid = NOSTIM; /* no external stimulation of grid during testing */
- stim_outstar = STIM; /* outstar does stimulate grid during testing */
-
- stoptime = curtime+duration;
- for ( ; curtime<stoptime; curtime++)
- {
- printf("\n Current time = %d",curtime);
- save_outstim(stim_outstar);
- for (i=0; i<ROWSIZE; i++)
- {
- for (j=0; j<COLSIZE; j++)
- {
- compute_activation(i,j,stim_grid, stim_outstar);
- }
- }
- if(displayflag == DISPLAYON)
- displaygrid();
- }
- curtime-- ; /* decrement to avoid using an extra time unit */
-
- /* when complete, if have not been updating display, do a final display of status */
- if (displayflag == DISPLAYOFF)
- displaygrid();
-
- return;
- }
- /****************************************************************************
- save_outstim(stimout)
- Parameter stimout either has the value STIM (1) or NOSTIM (0).
- save_outstim keeps a 16-time-unit historical record of
- the outputs of the outstar by modifying the global unsigned
- integer "outstar_save".
- Each time unit the outstar is stimulating the grid is represented
- by a "1" in outstar_save; if there is no stimulus, outstar_save has
- a "0". The 0th bit has the most recent record, the 15th bit has
- the oldest record.
- *****************************************************************************/
- save_outstim(stimout)
- int stimout;
- {
- outstar_save = outstar_save << 1; /* left shift one bit. A zero fills
- the lowest order bit, and the oldest
- (highest order bit) is lost */
- outstar_save += stimout; /* add current stimulus value
- (0 if no stim, 1 if stim) */
- return;
- }
-
- /**************************************************************
- compute_activation(row,col,grid_on,out_on)
- compute the current activation for the specified
- grid neurode
- Parameter "grid_on" is a flag to tell whether or not
- the external stimulus is impressing the pattern on
- the grid.
- Parameter "out_on" is a flag indicating whether the
- outstar is currently stimulating the grid.
-
- Note that differential equation is calculated as an
- incremental difference equation.
- ***************************************************************/
- compute_activation(row,col,grid_on,out_on)
- int row,col,grid_on,out_on;
- {
- double change;
- unsigned int status;
- int outstim; /* effective outstar stimulation at current time */
-
- change = -A*activity[row][col] ; /* no matter what, activity will tend to try to decay */
- if (grid_on == STIM) /* if there is external stimulus, it will counter decay */
- change += pattern[row][col];
- if (out_on == STIM) /* if there is outstar stimulus... */
- {
- status = outstar_save; /* Be sure not to change the global version */
- status = status >> t0; /* right shift by t0 time units to allow for
- transmission time from outstar */
- outstim = status & STIMMASK;
- change += gridwts[row][col]*outstim - T;
- }
- activity[row][col] += change; /* new activity = old plus incremental change */
- return;
- }
-
- /**************************************************************************************
- change_weight(row,col,out_on)
- modify the weight of the specified grid neurode synapse
- Parameter "out_on" is a flag indicating whether or not
- the outstar is currently stimulating the grid.
-
- Note that differential equation is calculated as an
- incremental difference equation.
-
- ***************************************************************************************/
- change_weight(row,col,out_on)
- int row,col, out_on;
- {
- double change; /* the incremental change to this weight */
- unsigned int status; /* local copy of global outstar output history */
- int outstim; /* effective stimulus from outstar at this time */
-
- change = -F * activity[row][col];
- if (out_on == STIM)
- {
- status = outstar_save; /* Be sure not to change the global version */
- status = status >> t0; /* right shift by t0 time units to allow for
- transmission time from outstar */
- outstim = status & STIMMASK;
- change += G * activity[row][col] * (outstim - T);
- }
- gridwts[row][col] += change;
- return;
- }
-
- /*******************************************************************
- read_pattern()
- Read in the input data file and store the patterns in
- in_pats and out_pats.
-
- The format for the data file is as follows:
- line# data expected
- ----- -----------------------------
- 1 In-X-size,in-y-size
- 2 1st X row of 1st pattern
- 3.. following rows of 1st pattern
- etc.
-
- Each row of data is separated by commas or spaces.
- The data is expected to be ascii text corresponding to
- either a +1 or a 0.
-
- Sample input for a pattern file (The comments to the
- right may NOT be in the file unless more sophisticated
- parsing of the input is done.):
-
- 5,7 input is 5x7 grid
- 0,1,1,1,0 beginning of pattern for "O"
- 1,0,0,0,1
- 1,0,0,0,1
- 1,0,0,0,1
- 1,0,0,0,1
- 1,0,0,0,0
- 0,1,1,1,0
-
- Clearly, this simple scheme can be expanded or enhanced
- any way you like.
-
- Returns -1 if any file error occurred, otherwise 0.
- ********************************************************************/
- read_pattern()
- {
- FILE *infile;
-
- int xinsize,yinsize;
- int rownum, numcols,x;
- int value, vals_read, status;
- char instring[STRINGSIZE];
-
- printf("\n Opening and retrieving data from file.");
-
- infile = fopen(inpath, "r");
- if (infile == NULL)
- {
- printf("\n error in opening file!");
- return -1 ;
- }
- vals_read =fscanf(infile,"%d,%d",&xinsize,&yinsize);
- if (vals_read != 2)
- {
- printf("\n Should read 2 items in line one; did read %d",vals_read);
- return -1;
- }
- if ((xinsize != ROWSIZE) || (yinsize != COLSIZE))
- {
- printf("\n\n ERROR: Pattern file is invalid!");
- printf("\n Pattern is a %d by %d grid instead of %d by %d",
- xinsize, yinsize, ROWSIZE, COLSIZE);
- return -1;
- }
- numcols = ROWSIZE;
- for (rownum = 0; rownum<COLSIZE; rownum++)
- {
- status = fscanf(infile,"%s",&instring);
- if (status == -1)
- {
- printf("\n ERROR: Insufficient data in file!");
- return -1;
- }
- value = parseline(instring,numcols,rownum);
- if (value == -1)
- return -1;
- }
- printf("\n Closing the input file now. ");
- fclose(infile);
- return 0;
- }
-
- /*******************************************************************
- parseline(string,numele,row)
- parse line of text to derive elements from pattern string.
- Parameters
- "string" is a pointer to string to be parsed.
- "numele" specifies number of elements contained in "string"
- "row" is pointer to correct row of "pattern" for elements.
- Elements in the string must be either "0", "1", <space>, ","
- 0,1 puts appropriate values in pattern array
- "<space>", or "," is ignored
-
- Notice that this is an extremely primitive parsing routine.
- This can (and should) be improved or modified as desired.
-
- Return:
- -1 if error, 0 else.
- ********************************************************************/
- parseline(string,numele,ygrid)
- char string[];
- int numele,ygrid;
- {
- int value;
- int charnum, ele;
- char ch;
-
- charnum = 0;
- value = 0;
- ele = 0;
- while ((ele < numele) && (value == 0))
- {
- if (charnum == STRINGSIZE) /* made it to the end without filling all element entries */
- value = -1;
- else
- { /*This routine does not care if digits are separated or not.
- each instance of a 0 or 1 will be taken as an element entry in
- the pattern. */
-
- ch = string[charnum];
- switch (ch)
- {
- case '0' : /* each "0" will be treated as a grid entry */
- pattern[ele][ygrid] = 0;
- ele++;
- break;
- case '1' : /* each "1" will be treated as a grid entry */
- pattern[ele][ygrid] = 1;
- ele++;
- break;
- default : /* all other characters are ignored. */
- break;
- }
- charnum++;
- }
- }
- return value;
- }
-
- /*******************************************************************
- randomize_wts()
- Intialize the weights in the grid neurodes to
- random values between -0.25..+0.25
- ********************************************************************/
- randomize_wts()
- {
- int i,j;
- double value;
-
- printf("\n Please enter a random number seed (1..32767): ");
- scanf("%d", &i);
- srand(i);
-
- for(i=0; i<ROWSIZE; i++)
- {
- for (j = 0; j<COLSIZE; j++)
- {
- value = (rand() / 32767.0 ) - 0.5;
- gridwts[i][j] = value/2;
- }
- }
- return;
- }
- /*******************************************************************
- randomize_activity()
- Intialize the activity in the grid neurodes to
- random values between 0.0..0.5
- ********************************************************************/
- randomize_activity()
- {
- int i,j;
- double value;
-
- for(i=0; i<ROWSIZE; i++)
- {
- for (j = 0; j<COLSIZE; j++)
- {
- value = ( rand() / 32767.0 );
- activity[i][j] = value/2.0;
- }
- }
- return;
- }
-
- /*******************************************************************
- set_constants()
- displays current (default) values for learning/activation
- constants and requests user changes.
- ********************************************************************/
- set_constants()
- {
- int ans;
- float value;
-
- show_constants(ACTIVATION);
- scanf("%d",&ans);
- while (ans != 0)
- {
- printf("\n New value for A? ");
- scanf("%f",&value);
- A = (double) value;
- printf("\n New value for T? ");
- scanf("%f",&value);
- T = (double) value;
- printf("\n New value for t0? ");
- scanf("%d",&t0);
- show_constants(ACTIVATION);
- scanf("%d",&ans);
- }
- show_constants(LEARNING);
- scanf("%d",&ans);
- while (ans != 0)
- {
- printf("\n New value for F? ");
- scanf("%f",&value);
- F = (double) value;
- printf("\n New value for G? ");
- scanf("%f",&value);
- G = (double) value;
- show_constants(LEARNING);
- scanf("%d",&ans);
- }
- return;
- }
- /*********************************************************************
- show_constants (which)
- displays either activation or learning constants for
- user approval or modification
- Paramter "which" determines which set will be displayed
- **********************************************************************/
- show_constants(which)
- int which;
- {
- if (which == ACTIVATION)
- {
- printf("\n The current values for the Grossberg activation constants are:");
- printf("\n Activation Decay Time constant (A): %6.3f",A);
- printf("\n Activity Threshold (T): %6.3f",T);
- printf("\n Transmission time to grid (t0): %d",t0);
- }
- if (which == LEARNING)
- {
- printf("\n The current values for the Grossberg learning constants are:");
- printf("\n Learning Decay 'Forgetting' constant (F): %6.3f",F);
- printf("\n Learning Gain constant (G): %6.3f",G);
- }
- printf("\n\n Do you wish to change any of these constants? (0 = no) ");
- return;
- }
-
- /*******************************************************************
- show_wts()
- print out the weights for the grid neurodes on the screen
- ********************************************************************/
- show_wts()
- {
- int row,col;
-
- printf("\n The current weights for the grid neurodes are:\n");
- for (col = 0; col < COLSIZE; col++)
- {
- printf ("\n");
- for (row = 0; row < ROWSIZE; row++)
- {
- printf(" %6.3f ",gridwts[row][col]);
- }
- }
- printf("\n\n");
- return;
- }
-
- /**************************************************************
- displaygrid()
- prints (text-only for portability) the current activity
- of the grid neurodes. Also displays the current time,
- and the desired pattern.
- ***************************************************************/
- displaygrid()
- {
- int i,j;
- double value;
-
- printf("\n Current pattern and activity at time %d:",curtime);
- printf("\n Scale (0.0 to 1.0): ' . _ o O Ñ' \n");
- printf("\n Grid activity is:");
- printf(" ");
- printf(" Desired pattern is:");
- for (j=0; j<COLSIZE; j++)
- {
- printf("\n ");
- for (i=0; i<ROWSIZE; i++)
- {
- value = activity[i][j];
- if (value < 0.17)
- printf(" ");
- if ((value >= 0.17) && (value < 0.35))
- printf(" . ");
- if ((value >= 0.35) && (value < 0.50))
- printf(" _ ");
- if ((value >= 0.50) && (value < 0.67))
- printf(" o ");
- if ((value >= 0.67) && (value < 0.83))
- printf(" O ");
- if (value >= 0.83)
- printf(" Ñ ");
- }
- printf(" ");
- for (i=0; i<ROWSIZE; i++)
- {
- switch(pattern[i][j])
- {
- case 0: printf(" ");
- break;
- case 1: printf(" Ñ ");
- break;
- default: break;
- }
- }
- }
- printf("\n");
- return;
- }
-
- /***************************************************************
- print_menu()
- prints out menu of operations for user choice
- ****************************************************************/
- print_menu()
- {
- printf("\n\n\n Please select an operation:");
- printf("\n\n 1. Train the network for a period of time.");
- printf("\n (Both external and outstar stimulate grid)");
- printf("\n\n 2. Allow the network to decay for a period of time.");
- printf("\n (Neither external or outstar stimulates grid)");
- printf("\n\n 3. Test the network for a period of time.");
- printf("\n (Only outstar stimulates grid)");
- printf("\n\n -1. Train the network for a period of time.");
- return;
- }
-
- /***************************************************************
- main()
- main program
- ****************************************************************/
- main()
- {
- int yesno;
- int done, donetraining;
- int traintime;
- int choice;
-
- done = 0; donetraining = 0;
- while (done == 0)
- {
- initialize();
- choice = 0;
- /* display the desired grid pattern on the screen */
- print_menu();
- printf("\n\n Please enter your choice: ");
- scanf("%d",&choice);
- printf("\n Your selection was %d",choice);
- while (choice != QUIT)
- {
- switch (choice)
- {
- case 1:
- train();
- break;
- case 2:
- decay();
- break;
- case 3:
- test();
- break;
- default:
- {
- choice = QUIT;
- break;
- }
- }
- print_menu();
- printf("\n\n Please enter your choice: ");
- scanf("%d",&choice);
- printf("\n Your selection was %d",choice);
- }
- printf("\n\n Training session terminated at user request...");
-
- /* want to start over (allows modification of constants)? */
- printf("\n\n Would you like to reset the network and begin again (no = 0)? ");
- scanf("%d",&yesno);
- if (yesno < 1)
- done = 2;
- }
- printf("\n\n Program complete.");
- /* stop */
- return;
- }