1 /******************************************************************************
2   FILE           : $Source: /projects/higgs1/SNNS/CVS/SNNS/tools/sources/linknets.c,v $
3   SHORTNAME      : snnslinknets.c
4   SNNS VERSION   : 4.2
5 
6   PURPOSE        : Combination of multiple SNNS feedforward networks to one
7                    combined net
8 
9   AUTHOR         : Michael Vogt
10   DATE           : 09.05.97
11   LAST UPDATE    : 09.05.97
12 
13   CHANGED BY     :
14   RCS VERSION    : $Revision: 1.11 $
15   LAST CHANGE    : $Date: 1998/05/25 16:01:45 $
16 
17     Copyright (c) 1990-1995  SNNS Group, IPVR, Univ. Stuttgart, FRG
18     Copyright (c) 1996-1998  SNNS Group, WSI, Univ. Tuebingen, FRG
19 
20   used files     : glob_typ.h, kr_ui.h      from kernel/sources
21                    libkernel.a              from kernel/bin/<architecture>
22                    functions.h, templates.h from actual directory
23 ******************************************************************************/
24 #include <config.h>
25 
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <ctype.h>
30 #ifdef HAVE_LIMITS_H
31 #include <limits.h>
32 #endif
33 #include <time.h>
34 #include <memory.h>
35 #include "glob_typ.h"
36 #include "kr_ui.h"
37 #include "functions.h"
38 #include "snns2clib.h"
39 
40 #include "kr_typ.h"
41 #include "kernel.h"
42 
43 #undef debug
44 
45 #define MAX_NO_OF_NETWORKS 20
46 #define KR_ERROR_RETURN(x) { fprintf(stderr, "%s\n", krui_error(x)); return ERR; }
47 
48 /* Macros for calculating the minimum or maximum of two values */
49 #define MAX(a, b) ( (a > b) ? (a) : (b) )
50 #define MIN(a, b) ( (a < b) ? (a) : (b) )
51 
52 /* Status (Error) Codes : OK = 0 (NO Error), ERR = 1, ...  */
53 typedef enum { OK=0, ERR, CANT_ADD, CANT_LOAD, MEM_ERR,
54 		   WRONG_PARAM, WRONG_ACT_FUNC, CANT_OPEN,
55 		   ILLEGAL_CYCLES, NO_CPN, NO_TDNN, NOT_SUPPORTED} Status;
56 
57 /* Recordtype for Layers */
58 typedef struct {
59   int   number;			/* of the Layer (not used yet) */
60   pList members;		/* Numbers of all member-units */
61   pList sources;		/* numbers of all sources of the member-units */
62   int   type;			/* INPUT , OUTPUT ...               */
63   int   ActFunc;		/* No in the ActivationFunctionList */
64   char  *name;			/* Name of the Layer */
65 } tLayer, *pLayer;
66 
67 /* Recordtype for Units */
68 typedef struct {
69     int    number;		/* of the Unit                         */
70     pList  sources;		/* numbers of the source-Units         */
71     float  *weights;	        /* Link-Weights to the Source-Units    */
72     int    ActFunc;		/* No in the ActivationFunctionList    */
73     int    type;		/* INPUT , OUTPUT ...                  */
74     char   *name;		/* Name of the unit, given by the user */
75     float  Bias;		/* Bias of the unit                    */
76     pLayer layer;		/* Pointer to the layer containing the unit */
77     float  act;                 /* Initial Activation of the Unit      */
78     char   *ActFuncName;	/* Name of ActivationFunction          */
79     char   *OutFuncName;	/* Name of OutputFunction              */
80     int    xpos;                /* unit position */
81     int    ypos;                /* unit position */
82     int    zpos;                /* unit position */
83 
84     int    kernel_nr;           /* kernel internal unit number */
85 } tUnit, *pUnit;
86 
87 #define DEFAULT_INPUT_UNIT_INITIALIZER \
88 { 0, NULL, NULL, 0, INPUT, "input", 0.0, NULL, 0.0, "Act_Logistic", "Out_Identity", 0, 0, 0, 0 }
89 
90 #define DEFAULT_OUTPUT_UNIT_INITIALIZER \
91 { 0, NULL, NULL, 0, OUTPUT, "output", 0.0, NULL, 0.0, "Act_Logistic", "Out_Identity", 0, 0, 0, 0 }
92 
93 #define IsInputUnit(x) ((x).type == INPUT || (x).type == SPECIAL_I)
94 #define IsOutputUnit(x) ((x).type == OUTPUT || (x).type == SPECIAL_O)
95 #define IsSpecialIHOUnit(x) ((x).type == SPECIAL_I || (x).type == SPECIAL_H || (x).type == SPECIAL_O)
96 
97 typedef struct {
98     int maxx;
99     int maxy;
100     int minx;
101     int miny;
102     int no_of_inputs;
103     int no_of_outputs;
104 } tNet, *pNet;
105 
106 static char *Act_Identity_String = "Act_IdentityPlusBias";
107 
108 /*
109  *  Output-Functions for debug-informations
110  */
111 
printLayer(pLayer layer)112 void printLayer(pLayer layer)
113 {
114   int i;
115   printf("\nLayer %d", layer->number);
116   printf("\nmembers: ");
117   for (i = 0; i < NoOf(layer->members); i++) {
118     printf("%d ", element(layer->members, i) );
119   }
120   printf("\nsources: ");
121   for (i = 0; i < NoOf(layer->sources); i++) {
122     printf("%d ", element(layer->sources, i) );
123   }
124   printf("\n");
125 }
126 
127 
printUnit(pUnit unit)128 void printUnit(pUnit unit)
129 {
130   int i;
131   printf("\nUnit %d", unit->number);
132   printf("\nsources: ");
133   for (i = 0; i < NoOf(unit->sources); i++) {
134     printf("%9d ", element(unit->sources, i) );
135   }
136   printf("\nweights: ");
137   for (i = 0; i < NoOf(unit->sources); i++) {
138     printf("%9f ", unit->weights[i] );
139   }
140   printf("\n");
141 }
142 
143 
144 /*****************************************************************************
145   FUNCTION : getpUnit
146 
147   PURPOSE  : Return the array element for the unit with a given number
148   RETURNS  :
149   NOTES    :
150 
151   UPDATE   :
152 ******************************************************************************/
getpUnit(pUnit Units,int NoOfUnits,int number)153 pUnit getpUnit(pUnit Units, int NoOfUnits, int number)
154 {
155     int i;
156 
157     for (i=0; i<NoOfUnits && Units[i].number != number; i++)
158 	;
159 
160     if (i>=NoOfUnits)
161 	return NULL;
162     else
163 	return &Units[i];
164 }
165 
166 
167 /*****************************************************************************
168   FUNCTION : checkErr
169 
170   PURPOSE  : writes an error message if needed and also stops program if
171              errCode stands for a fatal error
172   RETURNS  :
173   NOTES    :
174 
175   UPDATE   :
176 ******************************************************************************/
checkErr(int errCode)177 void checkErr(int errCode)
178 {
179   switch(errCode)
180       {
181       case OK          : ;
182 	break;
183       case ERR         : printf("unspecified Error\n");
184 	break;
185       case CANT_ADD    :
186       case MEM_ERR     : printf("not enough memory\n");
187 	break;
188       case CANT_LOAD   : printf("can't load file\n");
189 	break;
190       case WRONG_PARAM : printf("wrong parameters\n");
191 	break;
192       case CANT_OPEN   : printf("can't open file\n");
193 	break;
194       case NO_CPN      : printf("net is not a CounterPropagation network\n");
195 	break;
196       case NO_TDNN     : printf("net is not a Time Delay Neural Network\n");
197 	break;
198       case ILLEGAL_CYCLES : printf("net contains illegal cycles\n");
199 	break;
200       case WRONG_ACT_FUNC : ;
201 	break;
202       case NOT_SUPPORTED  : printf("not supported network type\n");
203 	break;
204       default             : printf("unknown error code : %d\n", errCode);
205       }
206 }
207 
208 
209 /*****************************************************************************
210   FUNCTION : is_BPTT_net
211 
212   PURPOSE  : checks, if the current Net is a BPTT, BBPTT or QPTT Network by
213              testing the learning functions.
214   RETURNS  : TRUE  if net is a kind of BPTT network, FALSE otherwise
215   NOTES    :
216 
217   UPDATE   :
218 ******************************************************************************/
is_BPTT_net(void)219 bool is_BPTT_net(void)
220 {
221   return (   (0 == strcmp("BPTT", krui_getLearnFunc() ))
222 	  || (0 == strcmp("BBPTT", krui_getLearnFunc() ))
223 	  || (0 == strcmp("QPTT", krui_getLearnFunc() )) );
224 }
225 
226 
227 /*****************************************************************************
228   FUNCTION : checkLearnFunc
229 
230   PURPOSE  : checks, if the Learning Function is supported by linknets
231   RETURNS  : Error Code : OK / NOT_SUPPORTED
232   NOTES    :
233 
234   UPDATE   :
235 ******************************************************************************/
checkLearnFunc(void)236 int checkLearnFunc(void)
237 {
238   static char *NotSupportedLearnFuncs[] = {
239     "ART1", "ART2", "ARTMAP", "BackPercolation", "Hebbian", "RM_delta",
240     "Kohonen", NULL
241   };
242 
243   char *LearnFunc = krui_getLearnFunc();     /* learning function of the network */
244   char **string   = NotSupportedLearnFuncs;  /* current function name to test */
245 
246   while(*string) {
247     if (!strcmp(*string, LearnFunc)) {	     /* e.g. the same function-name */
248       return(NOT_SUPPORTED);
249     }
250     string++;
251   }
252 
253   return(OK);
254 }
255 
256 
257 /*****************************************************************************
258   FUNCTION : checkActFunc
259 
260   PURPOSE  : checks, if an activation Function with the name actName is present
261   RETURNS  : number in the function table or -1 if not present
262   NOTES    :
263 
264   UPDATE   :
265 ******************************************************************************/
checkActFunc(char * actName)266 int checkActFunc(char *actName)
267 {
268   int i=0;
269 
270   while (**(ACT_FUNC_NAMES + i) ) {
271     if (!strcmp(ACT_FUNC_NAMES[i], actName) ) return (i);
272     i++;
273   }
274   fprintf(stderr, "Can't find the function <%s>\n", actName);
275   return(-1);
276 }
277 
278 
279 /*****************************************************************************
280   FUNCTION : initLayer
281 
282   PURPOSE  :  initialize the given layer e.g :
283               - initializes the two lists members and sources
284               - insert the type and the Number of the activation-function of the
285                 given Unit in the predefined places.
286               - inserts the sources of the given Unit into the list sources
287 
288   RETURNS  : error code
289   NOTES    :
290 
291   UPDATE   :
292 ******************************************************************************/
initLayer(pLayer layer,pUnit unit)293 int initLayer(pLayer layer, pUnit unit)
294 {
295   layer->members = newList();		   /* a list for member unit */
296   if (!layer->members) return(MEM_ERR);
297 
298   layer->sources = newList();		   /* a list for all predecessor */
299   if (!layer->sources) return(MEM_ERR);	   /* units of all members       */
300 
301   addList(layer->members, unit->number);   /* prototype unit is first member */
302   if (copyList(layer->sources, unit->sources) ) return(MEM_ERR);
303 
304   layer->ActFunc = unit->ActFunc;
305   layer->type    = unit->type;
306 
307   unit->layer = layer;
308 
309   return(OK);
310 }
311 
312 
313 /*****************************************************************************
314   FUNCTION : matchLayer
315 
316   PURPOSE  : checks if the unit could be in the same layer as the other units
317              which are in the layer yet
318   RETURNS  : TRUE the unit matches with the other units, FALSE otherwise
319   NOTES    :
320 
321   UPDATE   :
322 ******************************************************************************/
matchLayer(pLayer layer,pUnit unit)323 int matchLayer(pLayer layer, pUnit unit)
324 {
325   static int is_BPTT = 0, first_time = 1;
326 
327   /* a special flag is set to avoid unneeded function calls */
328   if (first_time) {
329     is_BPTT = is_BPTT_net();
330     first_time = 0;
331   }
332 
333   /* input neurons are all treated the same way */
334   if ( (unit->type == INPUT) && (layer->type == INPUT) ) return (TRUE);
335 
336   /* unit should match the attributes of the Layer */
337   if (unit->type != layer->type) return(FALSE);
338   if (unit->ActFunc != layer->ActFunc) return(FALSE);
339 
340   /* BPTT-nets have no topological order */
341   if (is_BPTT) return (TRUE);
342 
343   /* unit must not be a member of the source units */
344   if (isMember(layer->sources, unit->number) ) return (FALSE);
345 
346   /* Neue Version von Matthias Oderdorfer */
347   return ( CompareSources(unit->sources, layer->sources) );
348 
349   /* alte Version */
350   /* a member of the layer must not be a source element of the unit */
351   /* return( !haveIntersection(unit->sources, layer->members) ); */
352 }
353 
354 
355 /*****************************************************************************
356   FUNCTION : searchLayer
357 
358   PURPOSE  : searches a layer with matches the unit (or an empty Layer) and
359             inserts the unit in the layer.
360   RETURNS  : MEM_ERR : not enough Memory / OK: no problems
361   NOTES    :
362 
363   UPDATE   :
364 ******************************************************************************/
searchLayer(pUnit unit,pLayer globalLayers)365 int searchLayer(pUnit unit, pLayer globalLayers)
366 {
367   pLayer layer;
368 
369   layer = globalLayers;
370   while(TRUE) {
371     if (layer->members == NULL) {	   /* empty layer found */
372       return(initLayer(layer, unit));	   /* give possible Errors to caller */
373     }
374     else if (matchLayer(layer, unit) ) {   /* matching layer found */
375       if (addList(layer->members, unit->number)) {
376 	return(MEM_ERR);
377       }
378       unit->layer = layer;
379       return(mergeList(layer->sources, unit->sources)); /* returns Status */
380     }
381     layer++;
382   }
383 }
384 
385 
386 /*****************************************************************************
387   FUNCTION : divideNet
388 
389   PURPOSE  : parts a net into groups and prepares the Net for sorting the
390              layers.
391   RETURNS  : MEM_ERR : not enough Memory / OK: no problems
392   NOTES    :
393 
394   UPDATE   :
395 ******************************************************************************/
divideNet(pUnit globalUnits,pLayer globalLayers,int netnumber)396 int divideNet(pUnit globalUnits, pLayer globalLayers, int netnumber)
397 {
398   int       unitNo, sourceNo;	      /* number of the unit and source unit */
399   pUnit     unit;       	      /* unit and prototype unit */
400   FlintType dummy, weight;	      /* link weights */
401   int       error;		      /* error code    */
402   char      *string;		      /* free variable */
403   int       pos;		      /* free variable */
404   struct PosType unitpos;
405 
406 
407   /* loading all Units and group them into Layers  */
408   unitNo = krui_getFirstUnit();
409   unit   = globalUnits;
410 
411   while (unitNo) {
412     unit->number  = unitNo;
413 
414     /* copy the entries from SNNS to the own format */
415     unit->act     = krui_getUnitActivation(unitNo);
416     unit->type    = krui_getUnitTType(unitNo);
417     unit->Bias    = krui_getUnitBias(unitNo);
418 
419     unit->ActFuncName = strdup(krui_getUnitActFuncName(unitNo));
420     unit->OutFuncName = strdup(krui_getUnitOutFuncName(unitNo));
421     krui_getUnitPosition(unitNo, &unitpos);
422     unit->xpos = unitpos.x;
423     unit->ypos = unitpos.y;
424     unit->zpos = unitpos.z;
425 
426     /* units always have a name (at least its old number) */
427     string = krui_getUnitName(unitNo);
428     if (NULL == string) {
429       unit->name = malloc(12 * sizeof(char));
430       if(! unit->name) {
431 	return (MEM_ERR);
432       }
433       sprintf(unit->name, "N%dU%d", netnumber, unit->number);
434     }
435     else {
436       unit->name = malloc(MAX(1,strlen(string)+1));
437       if(! unit->name) {
438 	return (MEM_ERR);
439       }
440       strcpy(unit->name, string);
441     }
442 
443     unit->ActFunc = checkActFunc(krui_getUnitActFuncName(unitNo) );
444     if (unit->ActFunc < 0) return(WRONG_ACT_FUNC);
445 
446     /* insert all Source units in the list */
447     unit->sources = newList();
448     if (!unit->sources) return (MEM_ERR);
449     sourceNo = krui_getFirstPredUnit(&dummy);
450     while (sourceNo) {
451       /* only special-hidden-neurons may have links to itself */
452       if ( (unit->type != SPECIAL_H) && !is_BPTT_net() ) {
453         if (unit->number == sourceNo) return(ILLEGAL_CYCLES);
454       }
455       if(addList(unit->sources, sourceNo)) return (MEM_ERR);
456       sourceNo = krui_getNextPredUnit(&dummy);
457     }
458     /* now the weights can be written in the right order */
459     /* One more Element is allocated, because the array might have size 0 */
460     unit->weights = (float *)malloc(NoOf(unit->sources) *
461 				    sizeof(float) + sizeof(float));
462     if (!unit->weights) return(MEM_ERR);
463     sourceNo = krui_getFirstPredUnit(&weight);
464     while (sourceNo) {
465       pos = searchList(unit->sources, sourceNo);
466       unit->weights[pos] = weight;
467       sourceNo = krui_getNextPredUnit(&weight);
468     }
469 #ifdef debug
470     printUnit(unit);
471 #endif
472     error = searchLayer(unit, globalLayers);
473 		if (error) return(error);
474 
475     unit++;
476     unitNo = krui_getNextUnit();
477   }
478 
479   return(OK);
480 }
481 
482 
483 /*****************************************************************************
484   FUNCTION : checkOrder
485 
486   PURPOSE  : checks the order between layer x and layer y
487   RETURNS  : -1       : layer x before layer y
488               0       : indifferent
489               1       : layer x after layer y
490               ILLEGAL_CYCLES : error occured (no order given)
491 
492   NOTES    : SIDE-EFFECT : the entry SuccDelay is set up, if one layer
493              follows immediatly another. (needed by TDNN-networks)
494 
495 
496   UPDATE   :
497 ******************************************************************************/
checkOrder(pLayer globalLayers,int x,int y)498 signed char checkOrder(pLayer globalLayers, int x, int y)
499 {
500   /* preference of the unit type : low value means early update */
501   static char pref[12] = {0, 0, 2, 0, 1, 3, 3, 3, 3, 3, 3, 3};
502 
503   signed char order = 0;
504 
505   if (pref[globalLayers[x].type] < pref[globalLayers[y].type]) {
506     order = (signed char)-1;	/* e.g. layer x before layer y */
507   }
508   else if (pref[globalLayers[x].type] > pref[globalLayers[y].type]) {
509     order = 1;			/* e.g. layer x after layer y */
510   }
511 
512 	/* BPTT-Networks may contain any cycles so they must not be checked */
513 	if (is_BPTT_net() ) {
514 		return(order);
515 	}
516 
517   if (haveIntersection(globalLayers[x].sources, globalLayers[y].members) ) {
518     if (order == -1) {
519       if (SPECIAL_H != globalLayers[y].type) return (ILLEGAL_CYCLES);
520     }
521     else {
522       order = 1;
523     }
524   }
525   if (haveIntersection(globalLayers[x].members, globalLayers[y].sources) ) {
526     if (order == 1) {
527       if (SPECIAL_H != globalLayers[x].type) return (ILLEGAL_CYCLES);
528     }
529     else {
530       order = (signed char)-1;
531     }
532   }
533 
534   return(order);
535 }
536 
537 
538 /*****************************************************************************
539   FUNCTION : sortNet
540 
541   PURPOSE  : calculates the order between the global layers an returns it in
542              the array order. So the first number in the array is the first
543              layer to be updated and so on.
544   RETURNS  : MEM_ERR : not enough Memory / OK: no problems
545   NOTES    :
546 
547   UPDATE   :
548 ******************************************************************************/
sortNet(pLayer globalLayers,int NoOfLayers,int * order)549 int sortNet(pLayer globalLayers, int NoOfLayers, int *order)
550 {
551   char **matrix;		/* precedence matrix        */
552   char  *mask;			/* already chosen layers    */
553   int    i, j, x, y, ord, isSource = TRUE;
554   char   precedence;
555 
556   /* reserve memory for all the arrays and matrices */
557   matrix = (char **)malloc(NoOfLayers * sizeof(char *) );
558   if (!matrix) return (MEM_ERR);
559 
560   for (i = 0; i < NoOfLayers; i++) {
561     matrix[i] = (char *)calloc(NoOfLayers, sizeof(char) );
562     if (!matrix[i]) return (MEM_ERR);
563   }
564 
565   mask  = (char *)calloc(NoOfLayers, sizeof(char) );
566   if (!mask) {
567 		free(matrix);
568 		return (MEM_ERR);
569 	}
570 
571   /* build the precedence matrix of the Layer-Graph */
572   for (y = 0; y < NoOfLayers; y++) {
573     for (x = y + 1; x < NoOfLayers; x++) {
574       precedence = checkOrder(globalLayers, x, y);
575       if (precedence == ILLEGAL_CYCLES) return(ILLEGAL_CYCLES);
576 
577       matrix[x][y] = precedence;
578       matrix[y][x] = -precedence;
579 
580     } /* for x */
581 
582   } /* for y */
583 
584 #ifdef debug
585   printf("\nPrecedence Matrix is:\n");
586   for (y = 0; y < NoOfLayers; y++) {
587     for (x = 0; x < NoOfLayers; x++) {
588       printf("%3d ", matrix[y][x]);
589     }
590     printf("\n");
591   }
592 #endif
593 
594   /* put the Layers in the right order **/
595   for (ord = 0; ord < NoOfLayers; ord++) {
596 
597     for (i = 0; i < NoOfLayers; i++) {
598       if (mask[i]) continue;	/* Layer already chosen */
599 
600       isSource = TRUE;
601       for (j = 0; j < NoOfLayers; j++) {
602         /* exists a layer wich must be updated before ? */
603         if (matrix[i][j] == 1) {
604           isSource = FALSE;
605           break;
606         }
607       }
608 
609       if (isSource) {
610         order[ord] = i;		/* the number of the Layer becomes ord */
611         mask[i] = 1;		/* must not test this Layer again      */
612 
613         for (j = 0; j < NoOfLayers; j++) {
614           matrix[j][i] = 0;	/* clear depencies for other Layers */
615         }
616 
617         break;			/* find next Layer */
618       }
619 
620     } /* for i */
621 
622     if (!isSource) {
623       return(ILLEGAL_CYCLES);
624     }
625 
626   } /* for ord */
627 
628   for (i = 0; i < NoOfLayers; i++) {
629     free(matrix[i]);
630   }
631   free(matrix);
632   free(mask);
633 
634 #ifdef debug
635   printf("\nLayers sorted in following order :\n");
636   for (i = 0; i < NoOfLayers; i++) {
637     printf(" %d", order[i]);
638   }
639   printf("\n");
640 #endif
641 
642   return(OK);
643 }
644 
645 
646 /*****************************************************************************
647   FUNCTION : add_input_sources
648 
649   PURPOSE  :
650   RETURNS  :
651   NOTES    :
652 
653   UPDATE   :
654 ******************************************************************************/
add_input_sources(pUnit Units,int NoOfUnits,int sources)655 void add_input_sources(pUnit Units, int NoOfUnits, int sources)
656 {
657     int i;
658 
659     for (i=0; i<NoOfUnits; i++)
660     {
661 	if (IsInputUnit(Units[i]))
662 	{
663 	    Units[i].sources = newList();
664 	    Units[i].weights = (float *) malloc(sources * sizeof(float));
665 	}
666     }
667 }
668 
669 
670 /*****************************************************************************
671   FUNCTION : adjust_unit_numbers
672 
673   PURPOSE  :
674   RETURNS  :
675   NOTES    :
676 
677   UPDATE   :
678 ******************************************************************************/
adjust_unit_numbers(pUnit Units,int NoOfUnits,int offset)679 int adjust_unit_numbers(pUnit Units, int NoOfUnits, int offset)
680 {
681     int new_offset = offset;
682     int i, j;
683 
684     for (i=0; i<NoOfUnits; i++)
685     {
686 	Units[i].number += offset;
687 	new_offset = MAX(new_offset, Units[i].number);
688 	if (Units[i].sources)
689 	{
690 	    for (j=0; j<Units[i].sources->no; j++)
691 	    {
692 		Units[i].sources->values[j] += offset;
693 	    }
694 	}
695     }
696 
697     return new_offset;
698 }
699 
700 
701 /*****************************************************************************
702   FUNCTION : move_networks_down
703 
704   PURPOSE  :
705   RETURNS  :
706   NOTES    :
707 
708   UPDATE   :
709 ******************************************************************************/
move_networks_down(pUnit * UnitsP,int * NoOfUnitsP,int n,int offset)710 void move_networks_down(pUnit *UnitsP, int *NoOfUnitsP, int n, int offset)
711 {
712     int i, j;
713 
714     for (i=0; i<n; i++)
715     {
716 	for (j=0; j<NoOfUnitsP[i]; j++)
717 	    UnitsP[i][j].ypos += offset;
718     }
719 }
720 
721 
722 /*****************************************************************************
723   FUNCTION : adjust_network_positions
724 
725   PURPOSE  :
726   RETURNS  :
727   NOTES    :
728 
729   UPDATE   :
730 ******************************************************************************/
adjust_network_positions(pUnit * UnitsP,int * NoOfUnitsP,pNet NetinfoP,int n,int offset,int * height)731 int adjust_network_positions(pUnit *UnitsP, int *NoOfUnitsP, pNet NetinfoP,
732 			     int n, int offset, int *height)
733 {
734     int maxwidth = 0;
735     int i;
736     int j;
737     int yoffset=0;
738 
739     for (i=0; i<n; i++)
740     {
741 	for (j=0; j<NoOfUnitsP[i]; j++)
742 	{
743 	    UnitsP[i][j].xpos += offset - NetinfoP[i].minx;
744 	    UnitsP[i][j].ypos += yoffset - NetinfoP[i].miny;
745 	}
746 	yoffset += 2 + NetinfoP[i].maxy - NetinfoP[i].miny;
747     }
748 
749     for (i=0; i<n; i++)
750 	maxwidth = MAX(maxwidth, 1 + NetinfoP[i].maxx - NetinfoP[i].minx);
751 
752     *height = yoffset-1;
753     return maxwidth + offset;
754 }
755 
756 
757 /*****************************************************************************
758   FUNCTION : make_new_link
759 
760   PURPOSE  :
761   RETURNS  :
762   NOTES    :
763 
764   UPDATE   :
765 ******************************************************************************/
make_new_link(tUnit from,tUnit to,float weight)766 void make_new_link(tUnit from, tUnit to, float weight)
767 {
768     addList(to.sources, from.number);
769     to.weights[searchList(to.sources, from.number)] = weight;
770 }
771 
772 
773 /*****************************************************************************
774   FUNCTION : connect_input_units
775 
776   PURPOSE  :
777   RETURNS  :
778   NOTES    :
779 
780   UPDATE   :
781 ******************************************************************************/
connect_input_units(pUnit NewUnits,int nnew,pUnit OldUnits,int nold,int full)782 void connect_input_units(pUnit NewUnits, int nnew,
783 			 pUnit OldUnits, int nold, int full)
784 {
785     int opos;
786     int npos=0;
787 
788     for (opos=0; opos<nold; opos++)
789 	if (IsInputUnit(OldUnits[opos]))
790 	{
791 	    if (full)
792 	    {
793 		for (npos=0; npos<nnew; npos++)
794 		    make_new_link(NewUnits[npos], OldUnits[opos], 0.0);
795 		OldUnits[opos].type = HIDDEN;
796 	    }
797 	    else
798 	    {
799 		if (npos >= nnew)
800 		{
801 		    fprintf(stderr, "%s: internal error\n",
802 			    "connect_input_units");
803 		    exit(2);
804 		}
805 		make_new_link(NewUnits[npos], OldUnits[opos], 1.0);
806 		OldUnits[opos].type = SPECIAL_H;
807 		OldUnits[opos].Bias = 0.0;
808 		OldUnits[opos].ActFuncName = Act_Identity_String;
809 		npos++;
810 	    }
811 	}
812 }
813 
814 
815 /*****************************************************************************
816   FUNCTION : connect_output_units
817 
818   PURPOSE  :
819   RETURNS  :
820   NOTES    :
821 
822   UPDATE   :
823 ******************************************************************************/
connect_output_units(pUnit NewUnits,int nnew,pUnit OldUnits,int nold)824 void connect_output_units(pUnit NewUnits, int nnew, pUnit OldUnits, int nold)
825 {
826     int opos;
827     int npos;
828 
829     for (opos=0; opos<nold; opos++)
830 	if (IsOutputUnit(OldUnits[opos]))
831 	{
832 	    for (npos=0; npos<nnew; npos++)
833 		make_new_link(OldUnits[opos], NewUnits[npos], 0.0);
834 	    if (IsSpecialIHOUnit(OldUnits[opos]))
835 		OldUnits[opos].type = SPECIAL_H;
836 	    else
837 		OldUnits[opos].type = HIDDEN;
838 	}
839 }
840 
841 
842 /*****************************************************************************
843   FUNCTION : connect_inter_units
844 
845   PURPOSE  :
846   RETURNS  :
847   NOTES    :
848 
849   UPDATE   :
850 ******************************************************************************/
connect_inter_units(pUnit OldOutputUnits,int nout,pUnit OldInputUnits,int nin)851 void connect_inter_units(pUnit OldOutputUnits, int nout,
852 			 pUnit OldInputUnits, int nin)
853 {
854     int opos;
855     int npos;
856 
857     for (opos=0; opos<nout; opos++)
858 	if (IsOutputUnit(OldOutputUnits[opos]))
859 	    for (npos=0; npos<nin; npos++)
860 		if (IsInputUnit(OldInputUnits[npos]))
861 		    make_new_link(OldOutputUnits[opos],
862 				  OldInputUnits[npos], 0.0);
863 }
864 
865 
866 /*****************************************************************************
867   FUNCTION : multi_connect_inter_units
868 
869   PURPOSE  :
870   RETURNS  :
871   NOTES    :
872 
873   UPDATE   :
874 ******************************************************************************/
multi_connect_inter_units(pUnit * InputUnits,int * InputNoOfUnits,int ninputs,pUnit * OutputUnits,int * OutputNoOfUnits,int noutputs,int nconnects)875 void multi_connect_inter_units(pUnit *InputUnits, int *InputNoOfUnits,
876 			       int ninputs,
877 			       pUnit *OutputUnits, int *OutputNoOfUnits,
878 			       int noutputs, int nconnects)
879 {
880     int inet=0;
881     int ipos=0;
882     int onet=0;
883     int opos=0;
884 
885     while (nconnects>0)
886     {
887 	while (!IsOutputUnit(InputUnits[inet][ipos]))
888 	{
889 	    ipos++;
890 	    if (ipos >= InputNoOfUnits[inet])
891 	    {
892 		ipos = 0;
893 		inet++;
894 		if (inet >= ninputs)
895 		{
896 		    fprintf(stderr, "%s: internal error\n",
897 			    "multi_connect_inter_unit");
898 		    exit(2);
899 		}
900 	    }
901 	}
902 	while (!IsInputUnit(OutputUnits[onet][opos]))
903 	{
904 	    opos++;
905 	    if (opos >= OutputNoOfUnits[onet])
906 	    {
907 		opos = 0;
908 		onet++;
909 		if (onet >= noutputs)
910 		{
911 		    fprintf(stderr, "%s: internal error\n",
912 			    "multi_connect_inter_units");
913 		    exit(2);
914 		}
915 	    }
916 	}
917 	make_new_link(InputUnits[inet][ipos],
918 		      OutputUnits[onet][opos],
919 		      1.0);
920 
921 	if (IsSpecialIHOUnit(InputUnits[inet][ipos]))
922 	    InputUnits[inet][ipos].type = SPECIAL_H;
923 	else
924 	    InputUnits[inet][ipos].type = HIDDEN;
925 
926 	OutputUnits[onet][opos].type = SPECIAL_H;
927 	OutputUnits[onet][opos].Bias = 0.0;
928 	OutputUnits[onet][opos].ActFuncName = Act_Identity_String;
929 	nconnects--;
930     }
931 }
932 
933 
934 /*****************************************************************************
935   FUNCTION : substitute_output_units
936 
937   PURPOSE  :
938   RETURNS  :
939   NOTES    :
940 
941   UPDATE   :
942 ******************************************************************************/
substitute_output_units(pUnit Units,int n,int newtype)943 void substitute_output_units(pUnit Units, int n, int newtype)
944 {
945     int newspecialtype;
946 
947     switch (newtype)
948     {
949       case INPUT:
950 	newspecialtype = SPECIAL_I;
951 	break;
952       case HIDDEN:
953 	newspecialtype = SPECIAL_H;
954 	break;
955       case OUTPUT:
956 	newspecialtype = SPECIAL_O;
957 	break;
958       default:
959 	newspecialtype = newtype;
960 	break;
961     }
962 
963     while (--n>=0)
964     {
965 	if (IsOutputUnit(Units[n]))
966 	{
967 	    if (IsSpecialIHOUnit(Units[n]))
968 		Units[n].type = newspecialtype;
969 	    else
970 		Units[n].type = newtype;
971 	}
972     }
973 }
974 
975 
976 /*****************************************************************************
977   FUNCTION : substitute_input_units
978 
979   PURPOSE  :
980   RETURNS  :
981   NOTES    :
982 
983   UPDATE   :
984 ******************************************************************************/
substitute_input_units(pUnit Units,int n,int newtype)985 void substitute_input_units(pUnit Units, int n, int newtype)
986 {
987     int newspecialtype;
988 
989     switch (newtype)
990     {
991       case INPUT:
992 	newspecialtype = SPECIAL_I;
993 	break;
994       case HIDDEN:
995 	newspecialtype = SPECIAL_H;
996 	break;
997       case OUTPUT:
998 	newspecialtype = SPECIAL_O;
999 	break;
1000       default:
1001 	newspecialtype = newtype;
1002 	break;
1003     }
1004 
1005     while (--n>=0)
1006     {
1007 	if (IsInputUnit(Units[n]))
1008 	{
1009 	    if (IsSpecialIHOUnit(Units[n]))
1010 		Units[n].type = newspecialtype;
1011 	    else
1012 		Units[n].type = newtype;
1013 	}
1014     }
1015 }
1016 
1017 
1018 /*****************************************************************************
1019   FUNCTION : usage
1020 
1021   PURPOSE  :
1022   RETURNS  :
1023   NOTES    :
1024 
1025   UPDATE   :
1026 ******************************************************************************/
usage(char * name)1027 void usage(char *name)
1028 {
1029     fprintf(stderr, "usage : %s -innets <netfile> ... [ -outnets <netfile> ... ] -o <output network file> [ options ]\n",
1030 	    name);
1031     fprintf(stderr, "options:    -inunits        use copies of input units\n");
1032     fprintf(stderr, "            -inconnect <n>  fully connect with <n> input units\n");
1033     fprintf(stderr, "            -direct         connect input with output one-to-one\n");
1034     fprintf(stderr, "            -outconnect <n> fully connect to <n> output units\n");
1035     fprintf(stderr, "\n            -inunits and -inconnect may not be used together\n");
1036     fprintf(stderr, "\n!!!! PLEASE REFER TO THE MANUAL FOR DETAILED DESCRIPTION!!!!\n");
1037     exit(1);
1038 }
1039 
1040 
1041 /*****************************************************************************
1042   FUNCTION : create_and_save_network
1043 
1044   PURPOSE  :
1045   RETURNS  :
1046   NOTES    :
1047 
1048   UPDATE   :
1049 ******************************************************************************/
create_and_save_network(char * NetFileName,pUnit Units,int NoOfUnits)1050 int create_and_save_network(char *NetFileName, pUnit Units, int NoOfUnits)
1051 {
1052     int i, j;
1053     krui_err krerr;
1054     pUnit suP;
1055 
1056     struct PosType unitpos;
1057 
1058     krui_deleteNet();
1059     for (i=0; i<NoOfUnits; i++)
1060     {
1061 	Units[i].kernel_nr = krui_createUnit(Units[i].name,
1062 					     Units[i].OutFuncName,
1063 					     Units[i].ActFuncName,
1064 					     Units[i].act,
1065 					     Units[i].Bias);
1066 	if (Units[i].kernel_nr <= 0)
1067 	    KR_ERROR_RETURN(Units[i].kernel_nr);
1068 	if ((krerr=krui_setUnitTType(Units[i].kernel_nr, Units[i].type)) != KRERR_NO_ERROR)
1069 	    KR_ERROR_RETURN(krerr);
1070 	unitpos.x = Units[i].xpos;
1071 	unitpos.y = Units[i].ypos;
1072 	unitpos.z = Units[i].zpos;
1073 	krui_setUnitPosition(Units[i].kernel_nr, &unitpos);
1074     }
1075 
1076     for (i=0; i<NoOfUnits; i++)
1077     {
1078 	if (Units[i].sources)
1079 	{
1080 	    if ((krerr=krui_setCurrentUnit(Units[i].kernel_nr)) != KRERR_NO_ERROR)
1081 		KR_ERROR_RETURN(krerr);
1082 	    for (j=0; j<Units[i].sources->no; j++)
1083 	    {
1084 		suP = getpUnit(Units, NoOfUnits, Units[i].sources->values[j]);
1085 		if (!suP)
1086 		    return ERR;
1087 		if ((krerr=krui_createLink(suP->kernel_nr,
1088 					   Units[i].weights[j])) != KRERR_NO_ERROR)
1089 		    KR_ERROR_RETURN(krerr);
1090 	    }
1091 	}
1092     }
1093 
1094     if ((krerr=krui_saveNet(NetFileName, "snnslinknet")) != KRERR_NO_ERROR)
1095 	KR_ERROR_RETURN(krerr);
1096 
1097     return OK;
1098 }
1099 
1100 
1101 /*****************************************************************************
1102   FUNCTION : load_and_analyze_network
1103 
1104   PURPOSE  :
1105   RETURNS  :
1106   NOTES    :
1107 
1108   UPDATE   :
1109 ******************************************************************************/
load_and_analyze_network(int netnumber,char * NetFileName,pUnit * UnitsP,pLayer * LayersP,int * NoOfUnitsP,int * NoOfLayersP,int ** orderP,pNet NetinfoP)1110 int load_and_analyze_network(int netnumber, char *NetFileName, pUnit *UnitsP,
1111 			     pLayer *LayersP, int *NoOfUnitsP, int *NoOfLayersP,
1112 			     int **orderP, pNet NetinfoP)
1113 {
1114     krui_err err;			     /* error code of SNNS - krui */
1115 
1116     char *netname;
1117     int error;
1118     int nr;
1119 
1120 #ifdef debug
1121     printf("loading net %s \n", NetFileName);
1122 #endif
1123 
1124     /* load Net */
1125     err = krui_loadNet(NetFileName, &netname);
1126     if (err) {
1127 	fprintf(stderr, "%s\n", krui_error(err) );
1128 	return(CANT_LOAD);
1129     }
1130 
1131     error = checkLearnFunc();
1132     if (error) {
1133 	checkErr(error);
1134 	return(error);
1135     }
1136 
1137     *NoOfUnitsP = krui_getNoOfUnits();
1138 
1139     *UnitsP  =  (pUnit)calloc((*NoOfUnitsP + 1), sizeof(tUnit) );  /* because of sentinels */
1140     if (! *UnitsP) {
1141 	checkErr(MEM_ERR);
1142 	return(MEM_ERR);
1143     }
1144 
1145     *LayersP = (pLayer)calloc((*NoOfUnitsP + 1), sizeof(tLayer) ); /* because of sentinels */
1146     if (! *LayersP) {
1147 	free(*UnitsP);
1148 	checkErr(MEM_ERR);
1149 	return(MEM_ERR);
1150     }
1151 
1152     for (nr = 0; nr <= *NoOfUnitsP; nr++) {
1153 	(*LayersP)[nr].number  = nr;
1154     }
1155 
1156 #ifdef debug
1157     printf("dividing net into layers ...\n");
1158 #endif
1159 
1160     /* part Net into groups */
1161     error =  divideNet(*UnitsP, *LayersP, netnumber);
1162     if (error) {
1163 	checkErr(error);
1164 	return(error);
1165     }
1166 
1167     /* count the Non-empty Layers */
1168     for (nr = 0; (*LayersP)[nr].members != NULL; nr++);
1169     *NoOfLayersP = nr;
1170     *orderP = (int *)malloc(*NoOfLayersP * sizeof(int) );
1171 
1172 
1173     /* count the real number of units  */
1174     /* unused units have the number 0 the total Number can't exeed the   */
1175     /* Number of Units given by the SNNS-Interface-Function              */
1176     for(nr = 0; ((*UnitsP)[nr].number != 0) && (nr < *NoOfUnitsP); nr++) {
1177     }
1178     *NoOfUnitsP = nr;
1179 
1180 #ifdef debug
1181     for(nr = 0; nr < *NoOfLayersP; printLayer(*LayersP + nr++) );
1182 #endif
1183 
1184     /* topological sort of the layers */
1185 #ifdef debug
1186     printf("sorting layers ...\n");
1187 #endif
1188 
1189     error = sortNet(*LayersP, *NoOfLayersP, *orderP);
1190     if (error) {
1191 	checkErr(error);
1192 	return(error);
1193     }
1194 
1195     /* determine overall network info */
1196     NetinfoP->maxx = (*UnitsP)[0].xpos;
1197     NetinfoP->maxy = (*UnitsP)[0].ypos;
1198     NetinfoP->minx = NetinfoP->maxx;
1199     NetinfoP->miny = NetinfoP->maxy;
1200     NetinfoP->no_of_inputs = 0;
1201     NetinfoP->no_of_outputs = 0;
1202     for (nr=0; nr< *NoOfUnitsP; nr++)
1203     {
1204 	NetinfoP->maxx = MAX((*UnitsP)[nr].xpos, NetinfoP->maxx);
1205 	NetinfoP->maxy = MAX((*UnitsP)[nr].ypos, NetinfoP->maxy);
1206 	NetinfoP->minx = MIN((*UnitsP)[nr].xpos, NetinfoP->minx);
1207 	NetinfoP->miny = MIN((*UnitsP)[nr].ypos, NetinfoP->miny);
1208 	if (IsInputUnit((*UnitsP)[nr]))
1209 	    NetinfoP->no_of_inputs++;
1210 	if (IsOutputUnit((*UnitsP)[nr]))
1211 	    NetinfoP->no_of_outputs++;
1212     }
1213     return OK;
1214 }
1215 
1216 
1217 /*****************************************************************************
1218   FUNCTION : main
1219 
1220   PURPOSE  :
1221   RETURNS  :
1222   NOTES    :
1223 
1224   UPDATE   :
1225 ******************************************************************************/
main(int argc,char ** argv)1226 int main(int argc, char **argv)
1227 {
1228     int      ninputs = 0;
1229     char     *InputNetFileNames[MAX_NO_OF_NETWORKS];	     /* input files */
1230     pUnit    InputUnits[MAX_NO_OF_NETWORKS];    /* all input unit variables */
1231     pLayer   InputLayers[MAX_NO_OF_NETWORKS];  /* all input layer variables */
1232     tNet     InputNetinfo[MAX_NO_OF_NETWORKS];
1233 
1234     pUnit    NewInputUnits;
1235 
1236     int      noutputs = 0;
1237     char     *OutputNetFileNames[MAX_NO_OF_NETWORKS];       /* output files */
1238     pUnit    OutputUnits[MAX_NO_OF_NETWORKS];  /* all output unit variables */
1239     pLayer   OutputLayers[MAX_NO_OF_NETWORKS];/* all output layer variables */
1240     tNet     OutputNetinfo[MAX_NO_OF_NETWORKS];
1241 
1242     pUnit    NewOutputUnits;
1243 
1244     pUnit    FinalUnits;
1245     int      FinalNoOfUnits;
1246 
1247     int      InputNoOfUnits[MAX_NO_OF_NETWORKS],
1248 	     InputNoOfLayers[MAX_NO_OF_NETWORKS];
1249     int      *Inputorder[MAX_NO_OF_NETWORKS];    /* array with the order of
1250 						    the sorted layers */
1251 
1252     int      OutputNoOfUnits[MAX_NO_OF_NETWORKS],
1253 	     OutputNoOfLayers[MAX_NO_OF_NETWORKS];
1254     int      *Outputorder[MAX_NO_OF_NETWORKS];	 /* array with the order of
1255 						    the sorted layers */
1256 
1257     char     *outputnetworkfile;
1258     int      inunits = 0;
1259     int      inconnect = 0;
1260     int      interconnect = 1;
1261     int      outconnect = 0;
1262 
1263     int      total_no_of_in_inputs = 0;
1264     int      total_no_of_in_outputs = 0;
1265     int      total_no_of_out_inputs = 0;
1266     int      total_no_of_out_outputs = 0;
1267 
1268     int      equal_in_input_sizes = 1;
1269     int      equal_in_output_sizes = 1;
1270     int      equal_out_input_sizes = 1;
1271     int      equal_out_output_sizes = 1;
1272 
1273     int      total_no_of_units = 0;
1274     int      no_of_new_input_units = 0;
1275     int      no_of_new_output_units = 0;
1276 
1277     int i, j;
1278     int offset;
1279     int posoffset;
1280     int yoffseti = 0;
1281     int yoffseto = 0;
1282     int height = 0;
1283 
1284     static tUnit default_input_unit = DEFAULT_INPUT_UNIT_INITIALIZER;
1285     static tUnit default_output_unit = DEFAULT_OUTPUT_UNIT_INITIALIZER;
1286 
1287     char unitname[20];
1288 
1289     /* process options */
1290 
1291     i = 0;
1292     if (++i>=argc) usage(argv[0]);
1293 
1294     if (strcmp(argv[i], "-innets"))
1295 	usage(argv[0]);
1296 
1297     if (++i>=argc) usage(argv[0]);
1298     while (i<argc && argv[i][0] != '-')
1299     {
1300 	InputNetFileNames[ninputs] = strdup(argv[i]);
1301 	ninputs++;
1302 	if (ninputs >= MAX_NO_OF_NETWORKS)
1303 	{
1304 	    fprintf(stderr, "to many input networks\n");
1305 	    exit(1);
1306 	}
1307 	i++;
1308     }
1309     if (ninputs <= 0)
1310 	usage(argv[0]);
1311 
1312     if (i>=argc)usage(argv[0]);
1313 
1314     if (strcmp(argv[i], "-outnets") == 0)
1315     {
1316 	if (++i>=argc) usage(argv[0]);
1317 	while (i<argc && argv[i][0] != '-')
1318 	{
1319 	    OutputNetFileNames[noutputs] = strdup(argv[i]);
1320 	    noutputs++;
1321 	    if (noutputs >= MAX_NO_OF_NETWORKS)
1322 	    {
1323 		fprintf(stderr, "to many output networks\n");
1324 		exit(1);
1325 	    }
1326 	    i++;
1327 	}
1328     }
1329 
1330     if (strcmp(argv[i], "-o"))
1331 	usage(argv[0]);
1332 
1333     if (++i>=argc) usage(argv[0]);
1334 
1335 
1336     outputnetworkfile = strdup(argv[i]);
1337     i++;
1338 
1339 
1340     while (i<argc)
1341     {
1342 	if (strcmp(argv[i], "-inunits") == 0)
1343 	    inunits = 1;
1344 	else if (strcmp(argv[i], "-inconnect") == 0)
1345 	{
1346 	    if (++i>=argc) usage(argv[0]);
1347 	    inconnect = atoi(argv[i]);
1348 	}
1349 	else if (strcmp(argv[i], "-direct") == 0)
1350 	    interconnect = 0;
1351 	else if (strcmp(argv[i], "-outconnect") == 0)
1352 	{
1353 	    if (++i>=argc) usage(argv[0]);
1354 	    outconnect = atoi(argv[i]);
1355 	}
1356 	else
1357 	    usage(argv[0]);
1358 	i++;
1359     }
1360     if (inunits && inconnect)
1361 	usage(argv[0]);
1362 
1363     if (!interconnect && noutputs==0)
1364     {
1365 	fprintf(stderr, "no output networks defined, option -direct ignored\n");
1366 	interconnect = 1;
1367     }
1368 
1369     for (i=0; i<ninputs; i++)
1370     {
1371 	if (load_and_analyze_network(i+1, InputNetFileNames[i],
1372 				     &InputUnits[i], &InputLayers[i],
1373 				     &InputNoOfUnits[i], &InputNoOfLayers[i],
1374 				     &Inputorder[i], &InputNetinfo[i]) != OK)
1375 	    exit(1);
1376 
1377 	total_no_of_in_inputs += InputNetinfo[i].no_of_inputs;
1378 	total_no_of_in_outputs += InputNetinfo[i].no_of_outputs;
1379 	total_no_of_units += InputNoOfUnits[i];
1380 
1381 	if (i>0)
1382 	{
1383 	    if (InputNetinfo[i].no_of_inputs !=
1384 		InputNetinfo[i-1].no_of_inputs)
1385 		equal_in_input_sizes = 0;
1386 	    if (InputNetinfo[i].no_of_outputs !=
1387 		InputNetinfo[i-1].no_of_outputs)
1388 		equal_in_output_sizes = 0;
1389 	}
1390     }
1391 
1392     for (i=0; i<noutputs; i++)
1393     {
1394 	if (load_and_analyze_network(i+1+ninputs, OutputNetFileNames[i],
1395 				     &OutputUnits[i], &OutputLayers[i],
1396 				     &OutputNoOfUnits[i], &OutputNoOfLayers[i],
1397 				     &Outputorder[i], &OutputNetinfo[i]) != OK)
1398 	    exit(1);
1399 
1400 	total_no_of_out_inputs += OutputNetinfo[i].no_of_inputs;
1401 	total_no_of_out_outputs += OutputNetinfo[i].no_of_outputs;
1402 	total_no_of_units += OutputNoOfUnits[i];
1403 
1404 	if (i>0)
1405 	{
1406 	    if (OutputNetinfo[i].no_of_inputs !=
1407 		OutputNetinfo[i-1].no_of_inputs)
1408 		equal_out_input_sizes = 0;
1409 	    if (OutputNetinfo[i].no_of_outputs !=
1410 		OutputNetinfo[i-1].no_of_outputs)
1411 		equal_out_output_sizes = 0;
1412 	}
1413 
1414     }
1415 
1416     /* consitency check */
1417     if (inunits && !equal_in_input_sizes)
1418     {
1419 	fprintf(stderr, "illegal use of option -inunits\n"
1420 		"input networks are of different input sizes\n");
1421 	usage(argv[0]);
1422     }
1423     if (noutputs>0 && !interconnect &&
1424 	total_no_of_in_outputs != total_no_of_out_inputs)
1425     {
1426 	fprintf(stderr, "illegal use of option -direct\n"
1427 		"overall sum of input network output sizes differs from overall sum of output network input sizes\n");
1428 	usage(argv[0]);
1429     }
1430 
1431     /* determine numbers of additional units */
1432     if (inunits)
1433 	no_of_new_input_units = InputNetinfo[0].no_of_inputs;
1434     else if (inconnect)
1435 	no_of_new_input_units = inconnect;
1436 
1437     if (outconnect)
1438 	no_of_new_output_units = outconnect;
1439 
1440     /* reassign unit numbers */
1441     offset = no_of_new_input_units + 1;
1442     for (i=0; i<ninputs; i++)
1443 	offset = adjust_unit_numbers(InputUnits[i],InputNoOfUnits[i],offset)+1;
1444     for (i=0; i<noutputs; i++)
1445        offset = adjust_unit_numbers(OutputUnits[i],OutputNoOfUnits[i],offset)+1;
1446 
1447     /* create new units */
1448     NewInputUnits  = (pUnit) calloc(no_of_new_input_units + 1, sizeof(tUnit) );
1449     NewOutputUnits  = (pUnit) calloc(no_of_new_output_units + 1, sizeof(tUnit));
1450     FinalUnits = (pUnit) calloc(offset + no_of_new_output_units + 1,
1451 				sizeof(tUnit) );
1452 
1453     if (! NewOutputUnits || ! NewInputUnits || ! FinalUnits) {
1454 	checkErr(MEM_ERR);
1455 	exit(1);
1456     }
1457 
1458     /* move unit positions for graphical output */
1459     posoffset = 1 + adjust_network_positions(InputUnits, InputNoOfUnits,
1460 					 InputNetinfo, ninputs,
1461 					 no_of_new_input_units > 0 ?
1462 					 2 : 0, &yoffseti);
1463     if (noutputs > 0)
1464 	posoffset = 1 + adjust_network_positions(OutputUnits, OutputNoOfUnits,
1465 						 OutputNetinfo, noutputs,
1466 						 posoffset, &yoffseto);
1467 
1468     height = MAX(yoffseti, yoffseto);
1469     if (noutputs > 0)
1470     {
1471 	if (yoffseti < yoffseto)
1472 	    move_networks_down(InputUnits, InputNoOfUnits, ninputs,
1473 			       (yoffseto - yoffseti)/2);
1474 	else
1475 	    move_networks_down(OutputUnits, OutputNoOfUnits, noutputs,
1476 			       (yoffseti - yoffseto)/2);
1477     }
1478 
1479     /* create new input and output units */
1480     for (i=0; i<no_of_new_input_units; i++)
1481     {
1482 	NewInputUnits[i] = default_input_unit;
1483 	NewInputUnits[i].sources = newList();
1484 	NewInputUnits[i].number = i;
1485 	NewInputUnits[i].xpos = 0;
1486 	NewInputUnits[i].ypos = i + (no_of_new_input_units < height ?
1487 				     (height-no_of_new_input_units)/2 : 0);
1488 	sprintf(unitname, "in%d", i+1);
1489 	NewInputUnits[i].name = strdup(unitname);
1490     }
1491     for (i=0; i<no_of_new_output_units; i++)
1492     {
1493 	NewOutputUnits[i] = default_output_unit;
1494 	NewOutputUnits[i].sources = newList();
1495 	NewOutputUnits[i].weights =
1496 	    (float *) malloc(MAX(total_no_of_out_outputs,
1497 				 total_no_of_in_outputs) * sizeof(float));
1498 	NewOutputUnits[i].number = i+offset;
1499 	NewOutputUnits[i].xpos = posoffset;
1500 	NewOutputUnits[i].ypos = i + (no_of_new_output_units < height ?
1501 				      (height-no_of_new_output_units)/2 : 0);
1502 	sprintf(unitname, "out%d", i+1);
1503 	NewOutputUnits[i].name = strdup(unitname);
1504     }
1505 
1506     /* modify former input units to receive input from other source units */
1507     for (i=0; i<ninputs; i++)
1508 	add_input_sources(InputUnits[i], InputNoOfUnits[i],
1509 			  no_of_new_input_units);
1510     for (i=0; i<noutputs; i++)
1511 	add_input_sources(OutputUnits[i], OutputNoOfUnits[i],
1512 			  total_no_of_in_outputs);
1513 
1514     /* connect new input unit structure if necessary */
1515     for (i=0; i<ninputs; i++)
1516     {
1517 	if (inunits)
1518 	{
1519 	    connect_input_units(NewInputUnits, no_of_new_input_units,
1520 				InputUnits[i], InputNoOfUnits[i], 0);
1521 	}
1522 	else if (inconnect)
1523 	{
1524 	    connect_input_units(NewInputUnits, no_of_new_input_units,
1525 				InputUnits[i], InputNoOfUnits[i], 1);
1526 	}
1527     }
1528 
1529     /* connect new output unit structure if necessay */
1530     if (outconnect)
1531     {
1532 	if (noutputs > 0)
1533 	    for (i=0; i<noutputs; i++)
1534 	    {
1535 		connect_output_units(NewOutputUnits, no_of_new_output_units,
1536 				     OutputUnits[i], OutputNoOfUnits[i]);
1537 	    }
1538 	else
1539 	    for (i=0; i<ninputs; i++)
1540 	    {
1541 		connect_output_units(NewOutputUnits, no_of_new_output_units,
1542 				     InputUnits[i], InputNoOfUnits[i]);
1543 	    }
1544     }
1545 
1546     /* interconnect networks */
1547     if (interconnect && noutputs>0)
1548     {
1549 	for (i=0; i<ninputs; i++)
1550 	    for (j=0; j<noutputs; j++)
1551 		connect_inter_units(InputUnits[i], InputNoOfUnits[i],
1552 				    OutputUnits[j], OutputNoOfUnits[j]);
1553 	for (i=0; i<ninputs; i++)
1554 	    substitute_output_units(InputUnits[i], InputNoOfUnits[i],
1555 				    HIDDEN);
1556 	for (i=0; i<noutputs; i++)
1557 	    substitute_input_units(OutputUnits[i], OutputNoOfUnits[i],
1558 				   HIDDEN);
1559     }
1560     else if (noutputs>0)
1561 	multi_connect_inter_units(InputUnits, InputNoOfUnits, ninputs,
1562 				  OutputUnits, OutputNoOfUnits, noutputs,
1563 				  total_no_of_in_outputs);
1564 
1565     /* combine all networks to one single unit array */
1566     FinalNoOfUnits = 0;
1567     for (i=0; i<no_of_new_input_units; i++)
1568 	FinalUnits[FinalNoOfUnits++] = NewInputUnits[i];
1569     for (i=0; i<ninputs; i++)
1570 	for (j=0; j<InputNoOfUnits[i]; j++)
1571 	    FinalUnits[FinalNoOfUnits++] = InputUnits[i][j];
1572     for (i=0; i<noutputs; i++)
1573 	for (j=0; j<OutputNoOfUnits[i]; j++)
1574 	    FinalUnits[FinalNoOfUnits++] = OutputUnits[i][j];
1575     for (i=0; i<no_of_new_output_units; i++)
1576 	FinalUnits[FinalNoOfUnits++] = NewOutputUnits[i];
1577 
1578 #ifdef debug
1579     for (i=0; i<FinalNoOfUnits; i++)
1580 	printUnit(&FinalUnits[i]);
1581 #endif
1582 
1583     /* save created network */
1584     if(create_and_save_network(outputnetworkfile, FinalUnits,
1585 			       FinalNoOfUnits) != OK)
1586 	exit(1);
1587 
1588     exit(0);
1589 }
1590 
1591