
/*
 * Copyright (1987) Jeff Elman.  University of California, San Diego
 * This software may be redistributed without charge; this notice
 * should be preserved.
 */



#include <stdio.h>
#include <sys/types.h>
#include "defs.h"

int	nlayers;		/* number of layers in network */
int	*layer_descp;		/* no. of nodes in each layer */
float	**layerp;		/* the layers themselves */
float	**weightp;		/* weights between layers */
float	**dweightp;		/* delta weights between layers */
float	**iweightp;		/* incremental weights between layers */
float	**deltap;		/* deltas for each node, each layer */
float	**biasp;		/* biases for each node, each layer */
float	**dbiasp;		/* delta biases for each node, each layer */
float	**ibiasp;		/* incremental biases for each node, layer */
float	*cunits;		/* context units */
int	**conmat;		/* connectivity matrix */
int	**conup;		/* connections up */
int	**condown;		/* connections down */
float	*nodep;			/* node activations */
int	*onlys;			/* only nodes with onlys[i]=1 learn */
int	numtotal;		/* total number of units */
int	numin;			/* total number of input units */
int	num_hidden;
int	numout;			/* total number of output units */
struct	constraint constraints[256];
float	decays[MAXNODES];

config(fileroot) 
	char	*fileroot;
{
	extern	char *malloc();
	extern	float rans();
	extern	float weight_limit;
	extern	float *delta_x;
	extern	float *delta_z;
	extern	int nwts;
	extern	int array;
	extern	int debug;
	extern	int decayflag;
	extern	int context;
	extern	int rbp;
	extern	FILE	*cfp;
	FILE	*fopen();
	register int i;
	register int j;
	struct constraint *csp;
	float	**lp;
	float	**wpp;
	float	**dwpp;
	float	**iwpp;
	float	*wp;
	float	*dwp;
	float	*iwp;
	float	*np;
	float	*dp;
	float	*bp;
	float	*dbp;
	float	*ibp;
	int	*ldp;
	int	**cpp;
	int	**cupp;
	int	**cdpp;
	int	*cp;
	int	*cup;
	int	*cdp;
	char	configfile[128];
	int	nread;

	sprintf(configfile, "%s.cf", fileroot);
	cfp = fopen(configfile, "r");
	if (cfp == NULL) {
		perror(configfile);
		return -1;
	}
	/*
	 * if not in "array" mode, then the network is described
	 * generally in the config file
	 */
	if (array == 0) {
		/* 
		 * first line is # of layers, need this to proceed
		 */
		if (fscanf(cfp, "%d\n", &nlayers) != 1)
			inputerr(configfile, "# layers");
		/*
		 * next come descriptions of # of elements per layer;
		 */
		layer_descp = (int *)malloc((u_int)nlayers * sizeof(int *));
		nread = 0;
		for (i=0, ldp=layer_descp; i<nlayers; i++, ldp++) {
			nread += fscanf(cfp, "%d\n", ldp);
		}
		if (nread != nlayers)
			inputerr(configfile, "layer sizes");
		/*
		 * calculate and save the number of hidden units
		 */
		for (i=1, ldp=layer_descp+1; i<nlayers-1; i++, ldp++) {
			num_hidden += *ldp;
		}
		/* 
		 * save number of output units
		 */
		numout = *(layer_descp+nlayers-1);
		/*
		 * if we have context units, these get added in to
		 * first layer; c=1 means saving hidden
		 */
		if (context == 1)
			*layer_descp += num_hidden;
		/*
		 * next the bounds on random weights
		 */
		if (fscanf(cfp, "%f\n", &weight_limit) != 1)
			inputerr(configfile, "weight limit value");
		/*
		 * if decays, they will follow the random weight bounds
		 */
		if (decayflag && context) {
			nread = 0;
			for (i=0; i<num_hidden; i++) {
				    nread += fscanf(cfp, "%f", &decays[i]);
			}
			if (nread != num_hidden)
				inputerr(configfile, "decay values");
		}
		/*
		 * now we build the network
		 */
		build_network(weight_limit);
		fclose(cfp);
		return 0;
	} else if (array == 1) {
		/*
		 * we are in "array" mode and the network
		 * has been described with a matrix of
		 * connections; first line has numbers of units
		 */
		if (fscanf(cfp, "%d %d %d\n", &numtotal, &numin, &numout) != 3)
			inputerr(configfile, "#total, #in, #out values");
		/*
		 * we use layer_descp elsewhere, so set it up so that
		 * it has 2 layers (input and output)
		 */
		nlayers = 2;
		layer_descp = (int *)malloc((u_int)nlayers * sizeof(int *));
		*layer_descp = numin;
		*(layer_descp+1) = numout;
		/* 
		 * allocate space for layer pointers
		 */
		layerp = (float **) malloc((u_int)nlayers * sizeof(float **));
		if (layerp == NULL) {
			perror("layerp malloc");
			exit(1);
		}
		/*
		 * allocate space for layers
		 */
		for (i=0, lp=layerp, ldp=layer_descp; i<nlayers; i++, lp++, ldp++) {
			*lp = (float *)malloc((u_int)*ldp * sizeof(float *));
			if (*lp == NULL) {
				perror("lp malloc");
				exit(1);
			}
		}
		/*
		 * if recurrent back prop. we'll need some other arrays
		 */
		if (rbp > 0) {
			delta_x = (float *) malloc((u_int)numtotal * sizeof(float));
			delta_z = (float *) malloc((u_int)numtotal * sizeof(float));
		}
		/* 
		 * connectivity matrices
		 *	conmat: initial read in of file
 		 *	conup:  connections up from bottom layer to top
		 *	condown: connections down from top layer to bottom
		 */
		conmat = (int **) malloc((u_int)numtotal * sizeof(int *));
		for (i=0, cpp=conmat; i<numtotal; i++, cpp++)  
			*cpp = (int *)malloc((u_int)numtotal * sizeof(int));
		conup = (int **) malloc((u_int)numtotal * sizeof(int *));
		for (i=0, cpp=conup; i<numtotal; i++, cpp++)  
			*cpp = (int *)malloc((u_int)numtotal * sizeof(int));
		condown = (int **) malloc((u_int)numtotal * sizeof(int *));
		for (i=0, cpp=condown; i<numtotal; i++, cpp++)  
			*cpp = (int *)malloc((u_int)numtotal * sizeof(int));
		/*
		 * weight matrix
		 */
		weightp = (float **) malloc((u_int)numtotal * sizeof(float *));
		for (i=0, wpp=weightp; i<numtotal; i++, wpp++)  
			*wpp = (float *)malloc((u_int)numtotal * sizeof(float));
		/*
		 * dweight matrix
		 */
		dweightp = (float **) malloc((u_int)numtotal * sizeof(float *));
		for (i=0, dwpp=dweightp; i<numtotal; i++, dwpp++)  
			*dwpp = (float *)malloc((u_int)numtotal * sizeof(float));
		/*
		 * iweight matrix
		 */
		iweightp = (float **) malloc((u_int)numtotal * sizeof(float *));
		for (i=0, iwpp=iweightp; i<numtotal; i++, iwpp++)  
			*iwpp = (float *)malloc((u_int)numtotal * sizeof(float));
		/*
		 * node activations
		 */
		nodep = (float *) malloc((u_int)numtotal * sizeof(float));
		onlys = (int *) malloc((u_int)numtotal * sizeof(int));
		/*
		 * XXX we use biasp and deltap as one-dimensional arrays
		 * here because its more convenient, however we don't
		 * re-declare them.  Its gross.
		 */
		/*
		 * biases
		 */
		biasp = (float **) malloc((u_int)numtotal * sizeof(float));
		dbiasp = (float **) malloc((u_int)numtotal * sizeof(float));
		ibiasp = (float **) malloc((u_int)numtotal * sizeof(float));
		/*
		 * deltas
		 */
		deltap = (float **) malloc((u_int)numtotal * sizeof(float));
		 /*
		  * get connectivity from file
		  */
		for (i=0, cpp=conmat; i<numtotal; i++,cpp++) {
			char con[4];
			nread = 0;
			for (j=0,cp= *cpp;j<numtotal;j++,cp++){
				nread += fscanf(cfp, "%1s", &con[0]);
				if (con[0] == '.') {
					*cp = 0;
				} else if (con[0] == '?') {
					*cp = 1;
				} else {
					*cp = 2;
					csp = &(constraints[con[0]]);
					csp->up[csp->num].from = i;
					csp->up[csp->num].to = j;
					csp->down[csp->num].from = j;
					csp->down[csp->num].to = i;
					csp->num++;
				}
			}
			if (nread != numtotal)
				inputerr(configfile, "a connectivity layer");
		}
		/* 
		 * if this is set, then the decays for each node follow
		 */
		if (decayflag) {
			nread = 0;
			for (i=0; i<numtotal; i++) {
				nread += fscanf(cfp, "%f", &decays[i]);
			}
			if (nread != numtotal)
				inputerr(configfile, "decays after connectivity");
		}
		/*
		 * last thing in the file is the weight_limit
		 */
		if (fscanf(cfp, "%f\n", &weight_limit) == 0)
			inputerr(configfile, "missing weight limit");
		fclose(cfp);
		/*
		 * print connectivity
		 */
		if (debug > 0) {
			fprintf(stdout, "Connectivity matrix:\n");
			for (i=0, cpp=conmat; i<numtotal; i++, cpp++) {
				for (j=0, cp = *cpp; j<numtotal; j++, cp++) {
					if (*cp == 1) {
						fprintf(stdout, "%c", '?');
					} else if (*cp == 2) {
						fprintf(stdout, "%c", 'C');
					} else if (*cp == 0) {
						fprintf(stdout, "%c", '.');
					}
				}
				fprintf(stdout, "\n");
			}
			if (decayflag) {
				fprintf(stdout, "Decays:\n");
				for (i=0; i<numtotal; i++) {
					fprintf(stdout, "%f ", decays[i]);
					if ((i%10) == 0)
						fprintf(stdout, "\n");
				}
			}
		}
		/*
		 * print constraints
		 */
		if (debug > 1) {
		    for (i=0, csp=constraints; i<256; i++,csp++) {
			    if (csp->num)
				    fprintf(stdout, "Constraint %c (%d)\n", i, csp->num);
			    for (j=0; j<csp->num; j++) {
				    fprintf(stdout, "  up.from: %d\t",csp->up[j].from);
				    fprintf(stdout, "  up.to: %d\t",csp->up[j].to);
				    fprintf(stdout, "  down.from: %d\t",csp->down[j].from);
				    fprintf(stdout, "  down.to: %d\n",csp->down[j].to);
			    }
		    }
		}
		/*
		 * construct "conup" and "condown" matrixes
		 */
		for (i=0,cpp=conmat,cupp=conup; i<numtotal; i++,cpp++,cupp++) {
			for (j=0,cp= *cpp,cup= *cupp; j<numtotal; j++, cp++) {
				if (*cp > 0)  {
					*cup++ = j;
				}
			}
			/* mark end */
			*cup = -1;
		}
		for (i=0,cdpp=condown; i<numtotal; i++,cdpp++) {
			for (j=0,cdp= *cdpp; j<numtotal; j++) {
				if (*(*(conmat+j)+i) > 0)
					*cdp++ = j;
			}
			/* mark end */
			*cdp = -1;
		}
		/*
		 * print reconstructed matrix
		 */
		if (debug > 2) {
		    fprintf(stdout, "Connections up\n");
		    for (i=0, cupp=conup; i<numtotal; i++, cupp++) {
			    for (j=0, cup = *cupp; j<numtotal; j++, cup++) {
				    fprintf(stdout, "%d ", *cup);
			    }
			    fprintf(stdout, "\n");
		    }
		    fprintf(stdout, "Connections down\n");
		    for (i=0, cdpp=condown; i<numtotal; i++, cdpp++) {
			    for (j=0, cdp = *cdpp; j<numtotal; j++, cdp++) {
				    fprintf(stdout, "%d ", *cdp);
			    }
			    fprintf(stdout, "\n");
		    }
		}
		/*
		 * initialize weights, biases, deltas, and nodes
		 * weights are organized bottom->up; also save # of wts.
		 */
		np = (float *) nodep;
		dp = (float *) deltap;
		bp = (float *) biasp;
		dbp = (float *) dbiasp;
		ibp = (float *) ibiasp;
		cpp = conmat;
		wpp = weightp;
		dwpp = dweightp;
		iwpp = iweightp;
		nwts = 0;
		for (i=0; i<numtotal; i++,cpp++,wpp++,dwpp++,iwpp++,np++,dp++,bp++,dbp++,ibp++) {
			*np = *dp = *bp = *dbp = *ibp = 0.0;
			for (j=0,cp = *cpp, wp= *wpp, dwp = *dwpp, iwp = *iwpp;
				j<numtotal;j++,cp++,wp++,dwp++,iwp++) {
				if (*cp == 0)  {
					*wp = 0.0;
					*dwp = 0.0;
					*iwp = 0.0;
					if (debug > 4)
						fprintf(stdout, "%3.3f ", *wp);
				} else {
					*wp = rans(weight_limit);
					nwts++;
					*dwp = 0.0;
					*iwp = 0.0;
					if (debug > 4)
						fprintf(stdout, "%3.3f ", *wp);
				}
			}
			if (debug > 4)
				fprintf(stdout, "\n");
		}
	}
	return 0;
}

unconfig() 
{
	extern	float weight_limit;
	extern	int array;
	extern	FILE	*cfp;

	if (array == 0) { 
		/*
		 * next come descriptions of # of elements per layer;
		 */
		if (layer_descp)
			free((char *)layer_descp);
		nlayers = 0;
		num_hidden = 0;
		weight_limit = 0;
		fclose(cfp);
	} 
}
