/*
*
* ART1_1.CN - version 1 of ART1 on the CNAPS.
* July 16, 1996
* Clark S. Lindsey
*
*/
/*
*
* neuronF1 - non-poly structure that implements the F1 style
* input layer that uses the 2/3 rule to calculate
* the F1 output value
* neuronF2 - poly variable to implement the F2 style neurons.
* They hold both the bottom-up and top-down weight
* arrays.
*
*/
#include <cnio.h>
/***************************** Type Definitions *************************/
typedef unsigned scaled 5 11 jammed mval;
typedef unsigned scaled 8 8 jammed sval;
/***************************** Files ***********************************/
FILE (OUT_LOG, out, "*.log", NULL, NULL);
FILE (OUT_RUN, out, "*.log", NULL, NULL);
FILE (IN_TRAIN, in, "*.trn", NULL, NULL);
FILE (IN_TEST, in, "*.tst", NULL, NULL);
FILE (IN_WTS, inword, "*.wts", NULL, NULL);
FILE (OUT_WTS, outword, "*.wts", NULL, NULL);
/***************************** Constants *********************************/
#define MAX_NUM_INPUTS 64
#define ABS_MAX_CATEGORIES 128
#define TRAIN_MODE 1
#define TEST_MODE 0
#define CR '\n'
const sval INPUT_THRES=1.5; /* Used for 2/3 rule in F1 output */
/***************************** Entry Constants ***************************/
/* These can be changed from control program */
entry const int NINP=25;
entry const int NPATT=20;
entry const int MAX_CATEGORIES=20;
entry const sval BETA=0.500;
entry const sval rho=0.5; /* vigilance threshold = RHO/10 */
/* Program control flags */
entry const int RUN_MODE=TRAIN_MODE; /* = 0 for testing, = 1 for training */
entry const int LOG_MODE=1; /* Write out step by step results */
entry const int OUT_MODE=0; /* Write out pattern processing results only */
entry const int SAVE_WTS=0; /* Save weights to a file */
entry const int GET_WTS=0; /* Get weights from a file */
/***************************** Variables *********************************/
int numCategories=0; /* Number classes learned so far */
int classWinner=-1; /* Current class with best match to input */
sval gainF1=1; /* Gain control for F1 neurons to do 2/3 rule */
sval vig; /* Vigilance value */
int modified=0; /* Flag changes in number of 1 bits in F2 prototypes */
int outOfOutputs=0; /* Flag to indicate that not enough outputs available*/
int error=0; /* Flag to indicate errors */
sval INV_1_N; /* =1.0/(1+NINP) Initialization for bottom up weights */
/***************************** Structures ********************************/
/*
* F1 input neurons use the 2/3 rule to determine output
* output = ( (input + gain + top_down) > 1.5) ? 1 : 0;
*
* Use non-poly structure array.
*/
mono struct neuronF1 {
sval input;
sval output;
} F1[MAX_NUM_INPUTS];
/*
* F2 neurons are poly types.
*/
domain neuronF2 {
int category;
sval sum;
sval score;
sval topDown[MAX_NUM_INPUTS];
sval botUp[MAX_NUM_INPUTS];
} F2[ABS_MAX_CATEGORIES];
/****************************** Error Messages ***************************/
char * errorMsg[2] = {
"Wts read error: Bad categories number#",
"Wts read error: Bad inputs number#"
};
/****************************** Functions ********************************/
/* Only need to save the weights for the outputs that have been used. The*
* rest are set to default values. */
entry void saveWts(void)
{ int i,j;
open(OUT_WTS);
connect(OUT_WTS);
putword(numCategories);
putword(NINP);
for( i=0; i<numCategories; i++){
for( j=0; j<NINP; j++){
putword((%int)F2[i].topDown[j]);
}
}
for( i=0; i<numCategories; i++){
for( j=0; j<NINP; j++){
putword((%int)F2[i].botUp[j]);
}
}
close(OUT_WTS);
}
/****************************************************************************/
void openOutFiles(void){
if( LOG_MODE==1){
open(OUT_LOG);
}
if( OUT_MODE==1){
open(OUT_RUN);
connect(OUT_RUN); /* RUN is by default connected if opened */
}
}
/****************************************************************************/
void openInFiles(void){
/* Open input files */
if( RUN_MODE == TRAIN_MODE){
open(IN_TRAIN);
connect(IN_TRAIN);
} else {
open(IN_TEST);
connect(IN_TEST);
}
}
/****************************************************************************/
sval divide(sval numerator, sval denominator){
unsigned int aa, bb, c, abit, arg;
int flagReady,nShifts;
sval ans;
flagReady=0;
abit = 0x100;
arg = 0;
aa = (%unsigned int)numerator;
bb = (%unsigned int)denominator;
nShifts=0;
/* First section shifts denominator up or down until it
* is between a >= b > a/2
*/
if (aa >= bb){
do { /* Loop 1 */
c = bb << 1; /* while a>b, shift b up */
if (aa >= c) {
if (nShifts==7){ /* Don't shift more than 7 places to left */
arg=abit;
flagReady=1;
break;
}
abit <<= 1;
nShifts++;
bb = c; /* Only reached if bb still at least 1/2 < a */
}
} while (aa >= c);
}
else{ /* Loop 2 */
/* while b>a, shift b right */
/* Can't move abit more than 6 bits to right.
If move 6 bits then arg>>=3 will result in 0 as answer. */
do {
bb >>= 1; abit >>= 1;
if (abit < 1){ /* Moved to 3rd bit */
flagReady=1;
break;
}
} while (aa < bb);
}
/* Now a >= b > a/2 */
if(!flagReady){ /* here a >= b > a/2 */
do{ /* Loop 3 */
aa -= bb; /* subtract b from a */
arg += abit;
if ((bb < 2) || (abit < 2)){
flagReady=1;
break;
}
bb >>= 1; abit >>= 1; /* b <- b/2 */
while (aa < bb) { /* Loop 4 */
if (abit < 2){
flagReady=1;
break;
}
aa <<= 1; abit >>= 1;
}
if(flagReady) break;
} while (abit > 0);
}
ans= (%sval)arg;
return ( ans);
}
/************************************************************************/
void initialize(void){
mono int i;
mono sval one=1, norm;
norm = (sval)(1+NINP);
INV_1_N = divide(one,norm);
[domain neuronF2].{
category=-1;
score=0;
/* Initialize categories with all topDown components to 1 and
* all botUp components to 1/(1+NINP)
*/
for (i=0; i<NINP; i++){
topDown[i]=1;
botUp[i]=INV_1_N;
}
}
for(i=0; i<ABS_MAX_CATEGORIES; i++){
F2[i].category= i;
F2[i].sum = (sval)NINP;
}
}
/****************************************************************************/
void closeInFiles(void){
if( RUN_MODE == TRAIN_MODE)
close(IN_TRAIN);
else
close(IN_TEST);
}
/****************************************************************************/
void closeOutFiles(void){
if( LOG_MODE) close(OUT_LOG);
if( OUT_MODE) close(OUT_RUN);
}
/******************************************************************/
entry void connectLog(void)
{
if( LOG_MODE) connect(OUT_LOG);
}
/******************************************************************/
entry void connectRun(void)
{
if( OUT_MODE) connect(OUT_RUN);
}
/*******************************************************************/
entry void closeup(void){
closeOutFiles();
closeInFiles();
if( SAVE_WTS) saveWts();
}
/************************************************************************/
void logMessage(char * msg)
{ mono int i;
/* Now open the log file */
if( LOG_MODE)
connect(OUT_LOG);
else
return;
/* Write message */
for(i=0;i<100;i++){
if(msg[i] != '#')
putchar(msg[i]);
else{
putchar(CR);
break;
}
}
/* Now put things back as before */
if( OUT_MODE) connect(OUT_RUN);
}
/************************************************************************/
void processError(int errNum)
{
logMessage(errorMsg[errNum]);
closeup();
}
/************************************************************************/
entry void getWts(void)
{ int i,j;
int numInputs;
error=0;
/* char * msg[3]= {
"Init in getWts#",
"Read in wts#",
"getWts finished#"
}; */
initialize();
/* logMessage(msg[0]); */
open(IN_WTS);
connect(IN_WTS);
numCategories = getword();
if(numCategories < 1 || numCategories > ABS_MAX_CATEGORIES){
error=1;
return ;
}
numInputs=getword();
if(numInputs != NINP){
error=2;
return ;
}
for( i=0; i<numCategories; i++){
F2[i].sum = 0;
for( j=0; j<NINP; j++){
F2[i].topDown[j]=(%sval)getword();
F2[i].sum += F2[i].topDown[j];
}
}
for( i=0; i<numCategories; i++){
for( j=0; j<NINP; j++){
F2[i].botUp[j]=(%sval)getword();
}
}
/* logMessage(msg[1]); */
close(IN_WTS);
}
/****************************************************************************/
int setup(void){
int status=0;
openInFiles();
openOutFiles();
return status;
}
/****************************************************************************/
void loadInputs(void)
{
int i,j;
/* Get inputs to the F1 neurons */
for(i=0; i<NINP; i++) {
F1[i].input = (sval)getchar();
F1[i].output=0;
}
}
/****************************************************************************/
/*
* Implement 2/3 rule for determining F1 outputs
*/
void setF1Outputs(){
mono int i=0;
sval itot;
for(i=0;i<NINP;i++){
F1[i].output=0;
itot=F1[i].input + gainF1;
if(classWinner >= 0)itot += F2[classWinner].topDown[i];
if(itot > INPUT_THRES) F1[i].output=1;
}
}
/****************************************************************************/
/*
* Get number of matching 1's between the input and each F2 class
*/
void getScores(){
mono i;
[domain neuronF2].{
score=0;
for(i=0;i<NINP;i++){
score+=botUp[i]*F1[i].input;
}
}
}
/****************************************************************************/
/*
* Get F2 neuron with best match to input
*/
void getWinner(){
int i=0;
sval bestScore=0;
classWinner = -1;
for(i=0;i<MAX_CATEGORIES;i++){
if(F2[i].score > bestScore){
bestScore=F2[i].score;
classWinner=F2[i].category;
}
}
if(bestScore == 0) classWinner=-1;
}
/****************************************************************************/
sval vigilance(){
int i;
sval topDotIn,sum;
sum = 0;
topDotIn=0;
if( classWinner < 0) return (0);
for(i=0;i<NINP;i++){
/* Vigilance compares to the complete input vector so use F1.input
* rather than F1.output
*/
topDotIn += F2[classWinner].topDown[i] * F1[i].input;
sum += F1[i].output;
}
return (divide(topDotIn,sum));
}
/****************************************************************************/
void adaptLTM(){
int i;
sval sum=BETA;
for(i=0; i<NINP; i++){
sum += F1[i].output; /* After 2/3 rule used */
}
/* Check if the winner will be altered */
if( F2[classWinner].sum != sum){
/* Must be a new category if sum still equal to sum of inputs
* (assumes no input pattern will have all inputs on)
*/
if( ((int)(F2[classWinner].sum)) == NINP )++numCategories;
F2[classWinner].sum=sum;
modified=1;
}
for(i=0; i<NINP; i++){
F2[classWinner].topDown[i]=F1[i].output; /* After 2/3 rule used */
F2[classWinner].botUp[i]=divide(F2[classWinner].topDown[i],sum);
}
}
/****************************************************************************/
void runART1(void)
{ int tested;
/* Find output */
gainF1=1;
setF1Outputs(); /* Use 2/3 rule to get F1 outputs */
getScores();
tested=0;
do{
tested++;
getWinner();
if(classWinner < 0) break;
/* Count categories by monitoring when a new classWinner appears*/
vig=vigilance();
/* See if best match passes vigilance requirement */
if( vig > rho){
if(RUN_MODE==TEST_MODE)break;
gainF1=0;
setF1Outputs(); /* Use 2/3 rule to get F1 outputs */
adaptLTM();
break;
} else{
/* If not, then set this winner off and go to next best */
F2[classWinner].score=0;
classWinner=-1;
/* Turn back on F1 gain to get inputs */
gainF1=1;
setF1Outputs(); /* Use 2/3 rule to get F1 outputs */
}
} while(tested<MAX_CATEGORIES);
/* Check if out of F2 outputs */
if(tested==MAX_CATEGORIES && vig < rho
&& RUN_MODE==TRAIN_MODE) outOfOutputs=1;
}
/**************************** Entry Points ******************************/
entry void ART_Master(void)
{ int status;
int i,j,pat,epoch;
sval x,y;
char ch='B';
char * msg[4]= {
" Setup successful#",
" Init successful#",
" Training finished#",
" Number categories in wt file <= 0#"
};
status=setup();
if( status){
processError(status);
return;
}
logMessage(msg[0]);
/* Initialize the neurons unless they were loaded by getWts */
if(GET_WTS == 0) initialize();
logMessage(msg[1]);
outOfOutputs=0;
epoch=0;
if( OUT_MODE) {
i= rho*10;
putchar(i);
if( RUN_MODE==TRAIN_MODE)
putchar(MAX_CATEGORIES);
else{
putchar(numCategories);
if( numCategories <= 0){
logMessage(msg[3]);
return;
}
}
putchar(NPATT);
}
// Outer loop runs till no more changes in net
// or net runs out of prototypes.
do {
modified = 0;
epoch++;
/* run through all train. patterns */
for (pat=0; pat<NPATT; pat++)
{
if( OUT_MODE) putchar(pat);
classWinner=-1;
loadInputs();
runART1();
if( OUT_MODE) putchar(classWinner);
}
if( OUT_MODE) {
putchar(epoch);
putchar(modified);
/* log number of classes created */
putchar(numCategories);
}
rewind(IN_TRAIN);
} while ((modified) && !outOfOutputs);
if( OUT_MODE) putchar(outOfOutputs);
logMessage(msg[2]);
}
Lecture C:
CNAPS cn code
28-2-98