/* DISCRETE HOPFIELD NETWORK */ #include #include #define READFILE 0 #define WRITEFILE 1 #define TASK_NONE 0 #define TASK_LEARNING 1 #define TASK_OUTPUT 2 #define MAXRAND (1<<16) struct input_s { int *input; /* input vector */ int *output; /* output vector */ }; struct network { /* The structure */ int num_nodes; /* Number of nodes in the network */ double **w; /* Array of weights. w[i][j] is the weight between nodes i and j */ int *state; /* The state of the network. state[i] is the state of the i-th node */ /* The training parameters */ int learning; /* 1 - learning. 0 - output generation */ /* The inputs */ int num_inputs; /* #inputs */ struct input_s *inputs; /* Storage for input vectors */ }; void initrandom(); void deallocate_network(); int allocate_weights(), allocate_storage(); struct network *readinput(), *readfromfile(), *readfromkeyboard(), *getnetwork(); void openfile(), hebbian_learning(), get_outputs(); void initstate(), move_to_output(), iterate_network(); double net_input(), get_rand(); int select_node(), set_state(); void writeoutputweights(), writeoutputdata(); void hopd(); /* For learning: argv[1] - "-l" argv[2] - input structure file argv[3] - training data file argv[4] - output weights file For output generation argv[1] - "-o" argv[2] - weights filr argv[3] - test data file argv[4] - output data file */ void main(argc, argv) int argc; char *argv[]; { if (argc == 1) hopd(TASK_NONE, NULL, NULL, NULL); else if( argv[1][0] == '-' && argv[1][1] == 'l') hopd(TASK_LEARNING, argv[2], argv[3], argv[4]); else if( argv[1][0] == '-' && argv[1][1] == 'o') hopd(TASK_OUTPUT, argv[2], argv[3], argv[4]); else { printf("Usage\n"); printf("For interactive mode: \n"); printf("\thopd\n"); printf("For learning\n"); printf("\thopd -l input_file data_file weights_file\n"); printf("For output generation\n"); printf("\thopd -o weights_file data_file output_file\n"); } } /* Initialize random generator with number of microseconds since January 1, 1970, which is almost a random value in itself. */ void initrandom() { struct timeval tp; struct timezone tzp; if( gettimeofday(&tp,&tzp) == -1 ) { printf("Could not initialize random number generator\n"); _exit(0); } srandom((int)tp.tv_usec); } /* Returns a double precision random number between low and high */ double get_rand(low, high) double low, high; { double x = random() % MAXRAND / (double)MAXRAND; return( x*(high-low) + low ); } /* For every input, if the j-th and k-th bits are the same, strenghten the weight between nodes j and k. Else, weaken it */ void hebbian_learning(p) struct network *p; { int i,j,k; /* Initialize all weights to 0 */ for(i=0; inum_nodes; i++) for(j=0; jnum_nodes; j++) p->w[i][j] = 0.0; /* For all inputs, and for all pairs of bits */ for(i=0; inum_inputs; i++) for(j=0; jnum_nodes; j++) for(k=j+1; knum_nodes; k++) if(p->inputs[i].input[j] == p->inputs[i].input[k]) { p->w[j][k] = p->w[j][k] + 1.0; p->w[k][j] = p->w[k][j] + 1.0; } else { p->w[j][k] = p->w[j][k] - 1.0; p->w[k][j] = p->w[k][j] - 1.0; } /* Normalize weights by dividing with number of inputs */ for(i=0; inum_nodes; i++) for(j=0; jnum_nodes; j++) p->w[i][j] /= p->num_inputs; } /* Moves the input into the network. Initializes each node with the input */ void initstate(p, input, n) struct network *p; int *input; int n; { int i; for(i=0; istate[i] = input[i]; } /* Randomly select one of the nodes of the network 'net' */ int select_node(p) struct network *p; { int i = p->num_nodes; while(i>=p->num_nodes) i = (int) get_rand(0.0, (double)p->num_nodes); return(i); } /* Set the state of node 'node', according to the net input 'net_input' */ int set_state(net_input, value) double net_input; int value; { int node = value; if( net_input < 0) node = -1; else if(net_input > 0) node = 1; return(node); } /* Determine the net input in node 'node' of the network 'p' */ double net_input(node, p) int node; struct network *p; { int i; double sum = 0; for(i=0; inum_nodes; i++) if(i != node) sum += p->state[i] * p->w[node][i]; return(sum); } /* Repeatedly randomly select a node, and attempt to change it's state, depending on the node's net input. The procedure is repeated until no further changes are made in a large number of steps (possibly == 2*num_nodes) */ void iterate_network(p) struct network *p; { int nochanges = 0; int node, old_state; while(nochanges < 20) { node = select_node(p); old_state = p->state[node]; p->state[node] = set_state(net_input(node, p), p->state[node]); if(p->state[node] == old_state) nochanges++; else nochanges = 0; } } /* Moves the current state of the network to the output storage for a test pattern */ void move_to_output(p, output, n) struct network *p; int *output; int n; { int i; for(i=0; istate[i]; } /* Supplies each input sample to the network, iterates until convergence, then copies the network state to the output for the sample */ void get_outputs(p) struct network *p; { int i; for(i=0; inum_inputs; i++) { initstate(p, p->inputs[i].input, p->num_nodes); iterate_network(p); move_to_output(p, p->inputs[i].output, p->num_nodes); } } /* Opens the file filename for reading or writing (depending on type). Terminates the program on error. */ void openfile( filename, fp, type) char *filename; FILE **fp; int type; { if( filename ) { if( type == READFILE ) *fp = fopen(filename, "rt"); else *fp = fopen(filename, "wt"); if( *fp == NULL ) { printf("Unable to open file %s for %s\n",filename, (type == READFILE) ? "reading" : "writing"); _exit(0); } } } /* If in == NULL, the user will be interactively queried for all the values. One of the values: the weights file, will give pout its value. */ struct network *readinput(task, in, data, pout) int task; FILE *in, *data; FILE **pout; { struct network *p; if (task == TASK_NONE) p = readfromkeyboard(pout); else p = readfromfile(task, in, data); return(p); } /* Allocates the structure network, initializes all pointers to NULL, and all numbers to 0 */ struct network *getnetwork() { struct network *p; p = (struct network *)malloc(sizeof(struct network)); if (p == NULL) return(NULL); p->num_nodes = p->num_inputs = p->learning = 0; p->w = NULL; p->state = NULL; p->inputs = NULL; } /* Allocate a n*n weight matrix, and the array of states (of size n). If successfull, return 1, else return 0 */ int allocate_weights(p) struct network *p; { int i; /* Allocate the matrix */ p->w = (double **)malloc(p->num_nodes*sizeof(double *)); if(!p->w) return(0); for(i=0; inum_nodes; i++) p->w[i] = NULL; for(i=0; inum_nodes; i++) { p->w[i] = (double *)malloc(p->num_nodes*sizeof(double)); if(!p->w[i]) return(0); } /* Allocate space for the network state */ p->state = (int *)malloc(p->num_nodes*sizeof(int)); if(!p->state) return(0); return(1); } /* Allocate storage for 'number' inputs, 'n' bits each */ int allocate_storage(p) struct network *p; { int i; p->inputs = (struct input_s *)malloc(p->num_inputs*sizeof(struct input_s)); if( p->inputs == NULL ) return(0); for( i=0; inum_inputs; i++) { p->inputs[i].input = NULL; p->inputs[i].output = NULL; } for( i=0; inum_inputs; i++) { p->inputs[i].input = (int *)malloc(p->num_nodes * sizeof(int)); if( p->inputs[i].input == NULL ) return(0); p->inputs[i].output = (int *)malloc(p->num_nodes * sizeof(int)); if( p->inputs[i].output == NULL ) return(0); } return(1); } /* Deallocates all already allocated arrays and structures in the network */ void deallocate_network(p) struct network *p; { int i; if(p) { if(p->w) { for(i=0; inum_nodes; i++) if(p->w[i]) free(p->w[i]); free(p->w); } if(p->state) free(p->state); if(p->inputs) { for(i=0; inum_inputs; i++) { if(p->inputs[i].input) free(p->inputs[i].input); if(p->inputs[i].output) free(p->inputs[i].output); } free(p->inputs); } free(p); } } /* Prompts user for parameters of the machine, as well as names of data and output files. Allocates the structure network and places all values of interest in it. Returns the pointer towards the structure, and opens the weights file for output and places it's handler in pout. */ struct network *readfromkeyboard(pout) FILE **pout; { struct network *p = NULL; char buf[80]; char ch; int ok, i, j; FILE *in, *data; /* Allocate the structure network */ if( (p = getnetwork()) == NULL) return(NULL); ok = 0; while (!ok) { printf("** Select L(earning) or O(utput generation) **\n"); scanf("%s", buf); ch = buf[0]; if( ch == 'l' || ch == 'L' ) { p->learning = 1; ok = 1; } else if( ch == 'o' || ch == 'O' ) { p->learning = 0; ok = 1; } else printf("Incorrect option entered. Please re-enter\n"); } if( p->learning ) { printf("How many nodes are there in the network?: "); scanf("%d", &p->num_nodes); printf("Total number of input samples?: "); scanf("%d", &p->num_inputs); printf("Enter the name of the training data file: "); scanf("%s", buf); openfile(buf, &data, READFILE); printf("Enter the name of the output weight file: "); scanf("%s", buf); openfile(buf, pout, WRITEFILE); } else { printf("Enter the name of the weights file: "); scanf("%s", buf); openfile(buf, &in, READFILE); printf("Enter the name of the test data file: "); scanf("%s", buf); openfile(buf, &data, READFILE); printf("Enter the name of the output file: "); scanf("%s", buf); openfile(buf, pout, WRITEFILE); /* Get the number of nodes */ fscanf(in, "%d\n", &p->num_nodes); /* Get the number of test input samples */ fscanf(data, "%d\n", &p->num_inputs); } /* Allocate the weight matrix */ if(!allocate_weights(p)) { deallocate_network(p); return(NULL); } /* For output generation, read the weights from the weights file */ if(!p->learning) for(i=0; inum_nodes; i++) for(j=0; jnum_nodes; j++) fscanf(in, "%lf\n", p->w[i] + j); /* Allocate the input storage */ if(!allocate_storage(p)) { deallocate_network(p); return(NULL); } /* Read the inputs from the data file. */ for( i=0; inum_inputs; i++) { for( j=0; jnum_nodes; j++) fscanf(data, "%d ", p->inputs[i].input + j); fscanf(data, "\n"); } return(p); } /* Reads the input files and initializes the structure networs. Places all values of interest in the structure. If one of the arrays in the structure cannot be allocated, it deallocates the whole structure and returns NULL */ struct network *readfromfile(task, in, data) int task; FILE *in, *data; { struct network *p = NULL; int i,j; /* Allocate the structure network */ if( (p = getnetwork()) == NULL) return(NULL); if( task == TASK_LEARNING ) p->learning = 1; else p->learning = 0; /* Get the # of nodes (same as # of input dimensions) */ fscanf(in, "%d\n", &p->num_nodes); if(p->learning) { /* Get the number of training samples */ fscanf(in, "%d\n", &p->num_inputs); } else { /* Get the number of test samples */ fscanf(data, "%d\n", &p->num_inputs); } /* Allocate the weight matrix */ if(!allocate_weights(p)) { deallocate_network(p); return(NULL); } /* For output generation, read the weights from the weights file */ if(!p->learning) for(i=0; inum_nodes; i++) for(j=0; jnum_nodes; j++) fscanf(in, "%lf\n", p->w[i] + j); /* Allocate the input storage */ if(!allocate_storage(p)) { deallocate_network(p); return(NULL); } /* Read the inputs from the data file. */ for( i=0; inum_inputs; i++) { for( j=0; jnum_nodes; j++) fscanf(data, "%d ", p->inputs[i].input + j); fscanf(data, "\n"); } return(p); } /* Writes out the values of the weights derived for the supplied training inputs */ void writeoutputweights(p, out) struct network *p; FILE *out; { int i,j; /* Write the number of nodes */ fprintf(out, "%d\n", p->num_nodes); /* Write the weights themselves */ for(i=0; inum_nodes; i++) for(j=0; jnum_nodes; j++) fprintf(out, "%lf\n", p->w[i][j]); } /* Writes out the derived outputs for the supplied test inputs. */ void writeoutputdata(p, out) struct network *p; FILE *out; { int i,j; /* Write the inputs then the derived outputs */ for(i=0; inum_inputs; i++) { for(j=0; jnum_nodes; j++) fprintf(out, "%d ", p->inputs[i].input[j]); fprintf(out, "\n"); for(j=0; jnum_nodes; j++) fprintf(out, "%d ", p->inputs[i].output[j]); fprintf(out, "\n\n"); } } /* The discrete hopfield program. Opens all necessary files, reads inputs, trains (or tests) the network, and writes the derived weights (or outputs). */ void hopd(task, inputfile, datafile, outfile) int task; char *inputfile, *datafile, *outfile; { FILE *in, *data, *out; struct network *p; long t1, t2; t1 = clock(); in = out = data = NULL; openfile( inputfile, &in, READFILE); openfile( datafile, &data, READFILE); openfile( outfile, &out, WRITEFILE); if( (p = readinput(task, in, data, &out)) == NULL) { printf("Not enough memory\n"); _exit(0); } initrandom(); if(p->learning) { hebbian_learning(p); writeoutputweights(p, out); } else { get_outputs(p); writeoutputdata(p, out); } t2 = clock(); printf("Elapsed time %ld milliseconds\n",(t2-t1)/1000); } /*------------------------------------------------------------END*/