/*****************************************************************************
  FILE           : learn_f.c
  SHORTNAME      : learn_f
  SNNS VERSION   : 3.2

  PURPOSE        : SNNS-Kernel Learning Functions
  NOTES          : with following learning functions:
	           - Backpropagation
	           - Backpropagation with momentum term
                   - Quickprop
	           - Counterpropagation
	           - BackPercolation
                   - Backpropagation through time
                   - Batch backpropagation through time
                   - Quickprop through time
		   - Kohonen (by Univ. of Tuebingen)

  AUTHOR         : Niels Mache
  DATE           : 01.10.90

  CHANGED BY     : Sven Doering, Michael Vogt, Martin Reczko ,Guenter Mamier
  IDENTIFICATION : @(#)learn_f.c	1.41 4/25/94
  SCCS VERSION   : 1.41
  LAST CHANGE    : 4/25/94

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG

******************************************************************************/
#include <stdio.h>
#include <math.h>
#include <values.h>

#include "kr_typ.h"		/* Kernel Types and Constants  */
#include "kr_const.h"		/* Constant Declarators for SNNS-Kernel  */
#include "kr_def.h"		/* Default Values  */
#include "kernel.h"		/* kernel function prototypes  */
#include "kr_mac.h"		/* Kernel Macros   */
#include "glob_typ.h"
#include "kr_art.h"
#include "kr_art1.h"
#include "kr_art2.h"
#include "kr_amap.h"
#include "krart_df.h"
#include "kr_newpattern.h"
#include "kr_JordElm.h"

#include "learn_f.ph"






/*****************************************************************************
 *****************************************************************************

  GROUP        : backpropagation learning algorithm

  AUTHOR       : Niels Mache
  DATE         : 01.01.1990
  LAST CHANGE  : 18.03.1994

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG
             
******************************************************************************
******************************************************************************/


/*****************************************************************************
  FUNCTION : propagateNetForward

  PURPOSE  : forward pass for most of the learning algorithms
  RETURNS  : 
  NOTES    : topological forward propagation

  UPDATE   : 05.11.1993
******************************************************************************/
static void propagateNetForward(int pattern_no, int sub_pat_no)
{
    register struct Unit *unit_ptr;
    register Patterns in_pat;
    register TopoPtrArray topo_ptr;


    /* calculate startaddress for input pattern array  */
    in_pat = kr_getSubPatData(pattern_no,sub_pat_no,INPUT,NULL);
    if(in_pat == NULL){
	KernelErrorCode = KRERR_NP_NO_SUCH_PATTERN;
	return;
    }

    topo_ptr = topo_ptr_array;

    /* copy pattern into input unit's activation and calculate output of the
       input units */
    while ((unit_ptr = *++topo_ptr) != NULL) {	
	
	/* topo_ptr points to a (topological sorted) unit
	   stucture (input unit first)  */
	if (unit_ptr->out_func == OUT_IDENTITY)
	    /* identity output function: there is no need to call the output
	       function  */
	    unit_ptr->Out.output = unit_ptr->act = *in_pat++;
	else
	    /* no identity output function: calculate unit's output also  */
	    unit_ptr->Out.output 
		= (*unit_ptr->out_func) (unit_ptr->act = *in_pat++);
    }

    /* popagate hidden units  */
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* topo_ptr points to a
						   (topological sorted) unit
						   stucture */
	/* clear error values  */
	unit_ptr->Aux.flint_no = 0.0;

	/* calculate the activation value of the unit: call the activation
	   function if needed  */
	unit_ptr->act = (*unit_ptr->act_func) (unit_ptr);

	if (unit_ptr->out_func == OUT_IDENTITY)
	    /* identity output function: there is no need to call the output
	       function  */
	    unit_ptr->Out.output = unit_ptr->act;
	else
	    /* no identity output function: calculate unit's output also  */
	    unit_ptr->Out.output = (*unit_ptr->out_func) (unit_ptr->act);
    }

    /* popagate output units  */
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* topo_ptr points to a
						   (topological sorted) unit
						   stucture */
	/* clear error values  */
	unit_ptr->Aux.flint_no = 0.0;

	/* calculate the activation value of the unit: call the activation
	   function if needed  */
	unit_ptr->act = (*unit_ptr->act_func) (unit_ptr);

	if (unit_ptr->out_func == OUT_IDENTITY)
	    /* identity output function: there is no need to call the output
	       function  */
	    unit_ptr->Out.output = unit_ptr->act;
	else
	    /* no identity output function: calculate unit's output also  */
	    unit_ptr->Out.output = (*unit_ptr->out_func) (unit_ptr->act);
    }
}



/*****************************************************************************
  FUNCTION : propagateNetBackward2

  PURPOSE  : backward pass of the backprop learning algorithm
  RETURNS  : network error
  NOTES    : network must be topologically sorted

  UPDATE   : 07.02.1994 by Sven Doering 
******************************************************************************/
static float propagateNetBackward2(int pattern_no, int sub_pat_no, 
				   float learn_parameter, float delta_max)
{
    register struct Link *link_ptr;
    register struct Site *site_ptr;
    register struct Unit *unit_ptr;
    register Patterns out_pat;
    register float  error, sum_error, eta, devit, learn_error;
    register TopoPtrArray topo_ptr;
    int size;

    sum_error = 0.0;		/* reset network error  */
    eta = learn_parameter;	/* store learn_parameter in CPU register  */

    /* calculate address of the output pattern (with number pattern_no + 1)  */
    out_pat = kr_getSubPatData(pattern_no,sub_pat_no,OUTPUT,&size);
    if(out_pat == NULL){
	KernelErrorCode = KRERR_NP_NO_SUCH_PATTERN;
	return(-1);
    }
    out_pat += size;


    /* add 3 to no_of_topo_units because the topologic array contains 4 NULL
       pointers  */
    topo_ptr = topo_ptr_array + (no_of_topo_units + 3);

    /* calculate output units only  */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	devit = *(--out_pat) - unit_ptr->Out.output; /* calc. devitation */
	if ((float) fabs(devit) <= delta_max)
	    continue;

	sum_error += devit * devit; /* sum up the error of the network  */

	/* calc. error for output units	 */
	error = devit * (unit_ptr->act_deriv_func) (unit_ptr);
	/* calc. the error for adjusting weights and bias of the pred.
	   units  */
	if (IS_SPECIAL_UNIT(unit_ptr))
	    learn_error = 0.0;
	else
	    learn_error = eta * error;
	/* adjust bias value  */
	unit_ptr->bias += learn_error;

	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {
	    /* the unit has direkt links  */
	    FOR_ALL_LINKS(unit_ptr, link_ptr) { /* adjust links and
						   calc. errors of the
						   predecessor units  */
		    link_ptr->to->Aux.flint_no += link_ptr->weight * error;
		    link_ptr->weight += learn_error * link_ptr->to->Out.output;
		}
	    } else {		/* the unit has sites  */
		FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {	
		    /* adjust links and calc. errors of the predecessor units */
		    link_ptr->to->Aux.flint_no += link_ptr->weight * error;
		    link_ptr->weight += learn_error * link_ptr->to->Out.output;
		}
	    }
    }

    /* calculate hidden units only  */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	/* calc. the error of the (hidden) unit  */
	error = (unit_ptr->act_deriv_func) (unit_ptr) * 
	    unit_ptr->Aux.flint_no;
	/* calc. the error for adjusting weights and bias of the pred.
	   units  */
	if (IS_SPECIAL_UNIT(unit_ptr)) 
	    learn_error =0.0;
	else
	    learn_error = eta * error;
	/* adjust bias value  */
	unit_ptr->bias += learn_error;

	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {
	    /* the unit has direkt links	 */
	    FOR_ALL_LINKS(unit_ptr, link_ptr) {	/* adjust links and
						   calc. sum of errors
						   of the pred. units */
		    if IS_HIDDEN_UNIT
			(link_ptr->to)
			/* this link points to a hidden unit: sum up the
			   error's from previos units  */
			    link_ptr->to->Aux.flint_no += 
				link_ptr->weight * error;
		    link_ptr->weight
			+= learn_error * link_ptr->to->Out.output;
		}
	    } else {		/* the unit has sites  */
		FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {	
		    /* adjust links and calc sum of errors of the pred. units */
		    if IS_HIDDEN_UNIT
			(link_ptr->to)
			/* this link points to a hidden unit: sum up the
			   error's from previos units  */
			    link_ptr->to->Aux.flint_no += 
				link_ptr->weight * error;
		    link_ptr->weight
			+= learn_error * link_ptr->to->Out.output;
		}
	    }
    }

    return (sum_error);		/* return the error of the network */
}



/*****************************************************************************
  FUNCTION : LEARN_backprop

  PURPOSE  : main routine for the backpropagation algorithm
  RETURNS  : kernel error code
  NOTES    : Input Parameters:   1 : learning parameter
                                 2 : delta max

             Output Parameters:  1 : error of the network (sum of all cycles)


  UPDATE   : 05.11.1993
******************************************************************************/
krui_err LEARN_backprop(int start_pattern, int end_pattern,
			float *parameterInArray, int NoOfInParams,
			float **parameterOutArray, int *NoOfOutParams)
{
    static float    OutParameter[1];	/* OutParameter[0] stores the
					   learning error */
    int             i, pattern_no, sub_pat_no, no_of_layers;


    KernelErrorCode = KRERR_NO_ERROR;	/* reset return code  */

    /* ####  have to be changed (must be 2)  #### */
    if (NoOfInParams < 1) {	/* Not enough input parameters	 */
	KernelErrorCode = KRERR_PARAMETERS;
	return (KernelErrorCode);
    }
    *NoOfOutParams = 1;		/* One return value is available (the
				   learning error)  */
    *parameterOutArray = OutParameter;	/* set the output parameter reference */

    if (NetModified || (TopoSortID != TOPOLOGICAL_FF)) {
	/* Net has been modified or topologic array isn't initialized */
	/* check the topology of the network  */
	no_of_layers = kr_topoCheck();
	if (KernelErrorCode != KRERR_NO_ERROR)
	    /* an error has occured	 */
	    return (KernelErrorCode);

	if (no_of_layers < 2) {	/* the network has less then 2 layers  */
	    KernelErrorCode = KRERR_FEW_LAYERS;
	    return (KernelErrorCode);
	}
	/* count the no. of I/O units and check the patterns  */
	if (kr_IOCheck() != KRERR_NO_ERROR)
	    return (KernelErrorCode);

	/* sort units by topology and by topologic type  */
	(void) kr_topoSort(TOPOLOGICAL_FF);
	if ((KernelErrorCode != KRERR_NO_ERROR) &&
	    (KernelErrorCode != KRERR_DEAD_UNITS))
	    return (KernelErrorCode);

	NetModified = FALSE;
    }


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    NET_ERROR(OutParameter) = 0.0;	/* reset network error value  */

    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	propagateNetForward(pattern_no,sub_pat_no);   /* Forward propagation */

	/* Backward propagation  */
	/* 1st parameter is the learning parameter 2nd parameter is the max.
	   devitation between output pattern and the output of the output
	   unit (delta max) */
	NET_ERROR(OutParameter) +=
	    propagateNetBackward2(pattern_no,sub_pat_no,
				  LEARN_PARAM1(parameterInArray),
				  LEARN_PARAM2(parameterInArray));
    }

    return (KernelErrorCode);
}



/*****************************************************************************
  FUNCTION : propagateNetBackwardBatch

  PURPOSE  : backward pass in batch mode for the backprop learning algorithm
  RETURNS  : network error
  NOTES    :

  UPDATE   : 05.11.1993 by Guenter Mamier
******************************************************************************/
static float propagateNetBackwardBatch(int pattern_no, int sub_pat_no,  
				       float delta_max)
{
    register struct Link *link_ptr;
    register struct Site *site_ptr;
    register struct Unit *unit_ptr;
    register Patterns out_pat;
    register float  error, sum_error, devit;
    register TopoPtrArray topo_ptr;
    int size;

    sum_error = 0.0;		/* reset network error  */
    /* calculate address of the output pattern (with number pattern_no + 1)  */
    out_pat = kr_getSubPatData(pattern_no,sub_pat_no,OUTPUT,&size);
    if(out_pat == NULL){
	KernelErrorCode = KRERR_NP_NO_SUCH_PATTERN;
	return(-1);
    }
    out_pat += size;

    /* add 3 to no_of_topo_units because the topologic array contains 4 NULL
       pointers  */
    topo_ptr = topo_ptr_array + (no_of_topo_units + 3);

    /* calculate output units only  */
    /* no test for special units takes place because the final weight */
    /* change is performed  by updateWeights */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	devit = *(--out_pat) - unit_ptr->Out.output; /* calc. devitation */
	if ((float) fabs(devit) <= delta_max)
	    continue;

	sum_error += devit * devit; /* sum up the error of the network  */

	/* calc. error for output units	 */
	error = devit * (unit_ptr->act_deriv_func) (unit_ptr);
	/* calc. the error for adjusting weights and bias of the pred.
	   units  */
	/* adjust bias value  */
	unit_ptr->value_a += error;

	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {
	    /* the unit has direkt links  */
	    FOR_ALL_LINKS(unit_ptr, link_ptr) {	/* adjust links and
						   calc. sum of errors
						   of pred. units  */
		link_ptr->to->Aux.flint_no += link_ptr->weight * error;
		link_ptr->value_a += error * link_ptr->to->Out.output;
	    }
	} else {
	    /* the unit has sites  */
	    FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {	
		/* adjust links and calc. sum of errors of pred. units */
		link_ptr->to->Aux.flint_no += link_ptr->weight * error;
		link_ptr->value_a += error * link_ptr->to->Out.output;
	    }
	}
    }

    /* calculate hidden units only  */
    /* no test for special units takes place because the final weight */
    /* change is performed  by updateWeights */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	/* calc. the error of the (hidden) unit  */
	error = (unit_ptr->act_deriv_func) (unit_ptr) * 
	    unit_ptr->Aux.flint_no;
	/* calc. the error for adjusting weights and bias of the pred.
	   units  */
	/* adjust bias value  */
	unit_ptr->value_a += error;

	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {
	    /* the unit has direkt links	 */
	    FOR_ALL_LINKS(unit_ptr, link_ptr) {	
		/* adjust links and calc sum of errors of the pred. units */
		if IS_HIDDEN_UNIT
		    (link_ptr->to)
			/* this link points to a hidden unit: sum up the
			   error's from previos units  */
			link_ptr->to->Aux.flint_no += 
			    link_ptr->weight * error;
		link_ptr->value_a += error * link_ptr->to->Out.output;
	    }
	} else {
	    /* the unit has sites  */
	    FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {	
		/* adjust links and calc sum of errors of the pred. units */
		if IS_HIDDEN_UNIT
		    (link_ptr->to)
			/* this link points to a hidden unit: sum up the
			   error's from previos units  */
			link_ptr->to->Aux.flint_no
			    += link_ptr->weight * error;
		link_ptr->value_a += error * link_ptr->to->Out.output;
	    }
	}
    }

    return (sum_error);		/* return the error of the network */
}


/*****************************************************************************
  FUNCTION : clearDeltas

  PURPOSE  : clears delta values for a new run of backprop batch
  RETURNS  : kernel error code
  NOTES    :

  UPDATE   : 05.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err clearDeltas(void)
{
    register FlagWord flags;
    register struct Link *link_ptr;
    register struct Unit *unit_ptr;
    register struct Site *site_ptr;


    FOR_ALL_UNITS(unit_ptr) {
	flags = unit_ptr->flags;

	if ((flags & UFLAG_IN_USE) == UFLAG_IN_USE) {	/* unit is in use  */
	    unit_ptr->value_a = (FlintType) 0;

	    if (flags & UFLAG_SITES) {	/* unit has sites  */
		FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr)
		    link_ptr->value_a = (FlintType) 0;
	    } else {		/* unit has no sites   */
		if (flags & UFLAG_DLINKS) {	/* unit has direct links   */
		    FOR_ALL_LINKS(unit_ptr, link_ptr)
			link_ptr->value_a = (FlintType) 0;
		}
	    }
	}
    }

    return (KRERR_NO_ERROR);
}



/*****************************************************************************
  FUNCTION : updateWeights

  PURPOSE  : Update the weights after all patterns have been presented by 
             backpropBatch 
  RETURNS  : kernel error code
  NOTES    :

  UPDATE   : 05.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err updateWeights(float eta)
{
    register FlagWord flags;
    register struct Link *link_ptr;
    register struct Unit *unit_ptr;
    register struct Site *site_ptr;


    FOR_ALL_UNITS(unit_ptr) {
	if (!IS_SPECIAL_UNIT(unit_ptr)) {
	    flags = unit_ptr->flags;

	    if ((flags & UFLAG_IN_USE) == UFLAG_IN_USE) {
		/* unit is in use  */
		unit_ptr->bias += unit_ptr->value_a * eta;

		if (flags & UFLAG_SITES) {
		    /* unit has sites  */
		    FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr)
			link_ptr->weight += link_ptr->value_a * eta;
		} else {
		    /* unit has no sites   */
		    if (flags & UFLAG_DLINKS) {	/* unit has direct links    */
			FOR_ALL_LINKS(unit_ptr, link_ptr)
			    link_ptr->weight += link_ptr->value_a * eta;
		    }
		}
	    }
	}
    }
    return (KRERR_NO_ERROR);
}




/*****************************************************************************
  FUNCTION : LEARN_backpropBatch

  PURPOSE  : main routine for the batch version of the backpropagation 
             algorithm
  RETURNS  : kernel error code
  NOTES    : Input Parameters:   1 : learning parameter
                                 2 : delta max

             Output Parameters:  1 : error of the network (sum of all cycles)

  UPDATE   : 05.11.1993 by Guenter Mamier
******************************************************************************/
krui_err  LEARN_backpropBatch(int start_pattern, int end_pattern, 
			      float *parameterInArray, int NoOfInParams, 
			      float **parameterOutArray, int *NoOfOutParams)
{
    static float    OutParameter[1];	/* OutParameter[0] stores the
					   learning error  */
    int             i, pattern_no, sub_pat_no, no_of_layers;


    KernelErrorCode = KRERR_NO_ERROR;	/* reset return code  */

    /* ####  have to be changed (must be 2)  #### */
    if (NoOfInParams < 1) {	/* Not enough input parameters	 */
	KernelErrorCode = KRERR_PARAMETERS;
	return (KernelErrorCode);
    }
    *NoOfOutParams = 1;	  /* One return value is available ( learning error)  */
    *parameterOutArray = OutParameter;	/* set the output parameter reference */

    if (NetModified || (TopoSortID != TOPOLOGICAL_FF)) {	
	/* Net has been modified or topologic array isn't initialized */
	/* check the topology of the network  */
	no_of_layers = kr_topoCheck();
	if (KernelErrorCode != KRERR_NO_ERROR)
	    /* an error has occured	 */
	    return (KernelErrorCode);

	if (no_of_layers < 2) {	/* the network has less then 2 layers  */
	    KernelErrorCode = KRERR_FEW_LAYERS;
	    return (KernelErrorCode);
	}
	/* count the no. of I/O units and check the patterns  */
	if (kr_IOCheck() != KRERR_NO_ERROR)
	    return (KernelErrorCode);

	/* sort units by topology and by topologic type  */
	(void) kr_topoSort(TOPOLOGICAL_FF);
	if ((KernelErrorCode != KRERR_NO_ERROR) && 
	    (KernelErrorCode != KRERR_DEAD_UNITS))
	    return (KernelErrorCode);

	NetModified = FALSE;
    }
    clearDeltas();

    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    NET_ERROR(OutParameter) = 0.0;	/* reset network error value  */

    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	propagateNetForward(pattern_no,sub_pat_no);  /* Forward propagation */

	/* Backward propagation  */
	/* 1st parameter is the learning parameter 2nd parameter is the max.
	   devitation between output pattern and the output of the output
	   unit (delta max) */
	NET_ERROR(OutParameter) +=
	    propagateNetBackwardBatch(pattern_no,sub_pat_no,
				      LEARN_PARAM2(parameterInArray));
    }

    updateWeights(LEARN_PARAM1(parameterInArray));


    return (KernelErrorCode);
}


/*****************************************************************************
 *****************************************************************************

  GROUP        : backpropagation learning algorithm with momentum term

  AUTHOR       : Niels Mache
  DATE         : 01.01.1990
  LAST CHANGE  : 05.11.1993

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG
             
******************************************************************************
******************************************************************************/


/*****************************************************************************
  FUNCTION : initializeBackpropMomentum

  PURPOSE  : backprop-momentum initialisation
  RETURNS  : kernel error code
  NOTES    :

  UPDATE   : 05.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err initializeBackpropMomentum(void)
{
    register FlagWord flags;
    register struct Link *link_ptr;
    register struct Unit *unit_ptr;
    register struct Site *site_ptr;


    FOR_ALL_UNITS(unit_ptr) {
	flags = unit_ptr->flags;

	if ((flags & UFLAG_IN_USE) == UFLAG_IN_USE) {	/* unit is in use  */
	    unit_ptr->value_a = (FlintType) 0;

	    if (flags & UFLAG_SITES) {	/* unit has sites  */
		FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr)
		    link_ptr->value_b = (FlintType) 0;
	    } else {		/* unit has no sites   */
		if (flags & UFLAG_DLINKS) {	/* unit has direct links   */
		    FOR_ALL_LINKS(unit_ptr, link_ptr)
			link_ptr->value_b = (FlintType) 0;
		}
	    }
	}
    }

    return (KRERR_NO_ERROR);
}




/*****************************************************************************
  FUNCTION : Backprop_momentum_FSE
  PURPOSE  : Backward error propagation (topological) of backpropagation 
             learnig function with momentum term and flat spot elimination

  RETURNS  : network error
  NOTES    :

  UPDATE   : 07.02.1994 by Sven Doering
******************************************************************************/
static float Backprop_momentum_FSE(int pattern_no, int sub_pat_no, 
				   float learn_parameter, 
				   float mu, float FSE_term, float delta_max)
{
    register struct Link *link_ptr;
    register struct Site *site_ptr;
    register struct Unit *unit_ptr;
    register Patterns out_pat;
    register float  error, sum_error, eta, devit, learn_error, mu_help;
    register TopoPtrArray topo_ptr;
    int size;


    sum_error = 0.0;		/* reset network error  */
    eta = learn_parameter;	/* store learn_parameter in CPU register  */

    /* calculate address of the output pattern (with number pattern_no + 1)  */
    out_pat = kr_getSubPatData(pattern_no,sub_pat_no,OUTPUT,&size);
    if(out_pat == NULL){
	KernelErrorCode = KRERR_NP_NO_SUCH_PATTERN;
	return(-1);
    }
    out_pat += size;


    /* add 3 to no_of_topo_units because the topologic array contains 4 NULL
       pointers  */
    topo_ptr = topo_ptr_array + (no_of_topo_units + 3);

    /* calculate output units only  */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	devit = *(--out_pat) - unit_ptr->Out.output;	/* calc. devitation */
	if ((float) fabs(devit) <= delta_max)
	    continue;

	sum_error += devit * devit;	/* sum up the error of the network  */
	/* calc. error for output units	 */
	error = devit * ((unit_ptr->act_deriv_func) (unit_ptr) + FSE_term);

	/* calc. the error for adjusting weights and bias of the predecessor
	   units  */
	mu_help = mu;
	learn_error = eta * error;
	if(IS_SPECIAL_UNIT( unit_ptr )){
	    learn_error = 0.0;
	    mu = 0.0;
	}
	unit_ptr->value_a = learn_error + mu * unit_ptr->value_a;
	/* adjust bias value  */
	unit_ptr->bias += unit_ptr->value_a;

	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {	/* the unit has direkt links  */
	    FOR_ALL_LINKS(unit_ptr, link_ptr) {	/* adjust link weights and
						   calc. sum of errors of the
						   predecessor units  */
		link_ptr->to->Aux.flint_no += link_ptr->weight * error;
		link_ptr->value_b = learn_error * link_ptr->to->Out.output + 
		                    mu * link_ptr->value_b;
		link_ptr->weight += link_ptr->value_b;
	    }
	} else {		/* the unit has sites  */
	    FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {	
		/* adjust links and calc. sum of errors of the pred. units */
		link_ptr->to->Aux.flint_no += link_ptr->weight * error;
		link_ptr->value_b = learn_error * link_ptr->to->Out.output + 
		                    mu * link_ptr->value_b;
		link_ptr->weight += link_ptr->value_b;
	    }
	}
	mu = mu_help;
    }


    /* calculate hidden units only  */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	/* calc. the error of the (hidden) unit  */
	error = unit_ptr->Aux.flint_no * 
	        ((unit_ptr->act_deriv_func) (unit_ptr) + FSE_term);

	/* calc. the error for adjusting weights and bias of the pred. units */
	mu_help = mu;
	learn_error = eta * error;
	if(IS_SPECIAL_UNIT( unit_ptr )){
	    learn_error = 0.0;
	    mu = 0.0;
	}
	unit_ptr->value_a = learn_error + mu * unit_ptr->value_a;
	/* adjust bias value  */
	unit_ptr->bias += unit_ptr->value_a;

	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {	/* the unit has direkt links */
	    FOR_ALL_LINKS(unit_ptr, link_ptr) {	/* adjust link weights and
						   calc. sum of errors of the
						   predecessor units  */
		if IS_HIDDEN_UNIT
		    (link_ptr->to)
		    /* this link points to a hidden unit: sum up the error's
		       from previos units  */
			link_ptr->to->Aux.flint_no += link_ptr->weight * error;

		link_ptr->value_b = learn_error * link_ptr->to->Out.output + 
		                    mu * link_ptr->value_b;
		link_ptr->weight += link_ptr->value_b;
	    }
	} else {		/* the unit has sites  */
	    FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {	
		/* adjust links and calc. sum of errors of the pred. units */
		if IS_HIDDEN_UNIT
		    (link_ptr->to)
		    /* this link points to a hidden unit: sum up the error's
		       from previos units  */
			link_ptr->to->Aux.flint_no += link_ptr->weight * error;

		link_ptr->value_b = learn_error * link_ptr->to->Out.output + 
		                    mu * link_ptr->value_b;
		link_ptr->weight += link_ptr->value_b;
	    }
	}
	mu = mu_help;
    }

    return (sum_error);		/* return the error of the network */
}


/*****************************************************************************
  FUNCTION : LEARN_backpropMomentum

  PURPOSE  : main routine for backpropagation with momentum
  RETURNS  : kernel error code
  NOTES    : Input Parameters:   1 : learning parameter
                                 2 : momentum term
				 3 : flat-spot-elimination value
				 4 : delta max

             Output Parameters:  1 : error of the network (sum of all cycles)

  UPDATE   : 05.11.1993 by Guenter Mamier
******************************************************************************/
krui_err LEARN_backpropMomentum(int start_pattern, int end_pattern, 
				float *parameterInArray, int NoOfInParams, 
				float **parameterOutArray, int *NoOfOutParams)
{
    static float OutParameter[1];  /*OutParameter[0] stores the learning error*/
    int          i, ret_code, pattern_no, sub_pat_no;


    if (NoOfInParams < 1)	/* ####  have to be changed (must be 2)  #### */
	return (KRERR_PARAMETERS);	/* Not enough input parameters  */

    *NoOfOutParams = 1;		/* One return value is available (the
				   learning error)  */
    *parameterOutArray = OutParameter;	/* set the output parameter reference */
    ret_code = KRERR_NO_ERROR;	/* reset return code  */

    if (NetModified || (TopoSortID != TOPOLOGICAL_FF)) {	
	/* Net has been modified or topologic array isn't initialized */
	/* check the topology of the network  */
	ret_code = kr_topoCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);	/* an error has occured  */
	if (ret_code < 2)
	    return (KRERR_FEW_LAYERS);	/* the network has less then 2 layers */

	/* count the no. of I/O units and check the patterns  */
	ret_code = kr_IOCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);

	/* sort units by topology and by topologic type  */
	ret_code = kr_topoSort(TOPOLOGICAL_FF);
	if ((ret_code != KRERR_NO_ERROR) && (ret_code != KRERR_DEAD_UNITS))
	    return (ret_code);

	NetModified = FALSE;
    }
    if (NetInitialize || LearnFuncHasChanged) {	/* Net has been modified or
						   initialized, initialize
						   backprop now  */
	ret_code = initializeBackpropMomentum();
	if (ret_code != KRERR_NO_ERROR)
	    return (ret_code);
    }


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    NET_ERROR(OutParameter) = 0.0;	/* reset network error value  */

    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	propagateNetForward(pattern_no,sub_pat_no);    /* Forward propagation */

	/* Backward propagation  */
	/* 1st parameter is the learning parameter 2nd parameter is the
	   momentum term 3rd parameter is the flat-spot-elimination value 4th
	   parameter is the max. devitation between output pattern and the
	   output of the output unit (delta max) */
	NET_ERROR(OutParameter) +=
	    Backprop_momentum_FSE(pattern_no,sub_pat_no,
				  LEARN_PARAM1(parameterInArray),
				  LEARN_PARAM2(parameterInArray),
				  LEARN_PARAM3(parameterInArray),
				  LEARN_PARAM4(parameterInArray));
    }

    return (ret_code);
}





/*****************************************************************************
 *****************************************************************************

  GROUP        : quickpropagation learning function

  AUTHOR       : Peter Zimmerer
  DATE         : 01.01.1990
  LAST CHANGE  : 05.11.1993

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG
             
******************************************************************************
******************************************************************************/

/*****************************************************************************
  FUNCTION : initializeQuickprop

  PURPOSE  : initializes the quickprop learning
  RETURNS  : kernel error code
  NOTES    :

  UPDATE   : 05.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err initializeQuickprop(void)
{
    register unsigned short flags;
    register struct Link *link_ptr;
    register struct Unit *unit_ptr;
    register struct Site *site_ptr;


    FOR_ALL_UNITS(unit_ptr) {
	flags = unit_ptr->flags;

	if ((flags & UFLAG_IN_USE) == UFLAG_IN_USE) {	/* unit is in use  */
	    unit_ptr->value_a = unit_ptr->value_b =
		unit_ptr->value_c = (FlintType) 0;

	    if (flags & UFLAG_SITES) {	/* unit has sites  */
		FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr)
		    link_ptr->value_a = link_ptr->value_b = 
			                link_ptr->value_c = (FlintType) 0;
	    } else {		/* unit has no sites   */
		if (flags & UFLAG_DLINKS) {	/* unit has direct links    */
		    FOR_ALL_LINKS(unit_ptr, link_ptr)
			link_ptr->value_a = link_ptr->value_b = 
			                    link_ptr->value_c = (FlintType) 0;
		}
	    }
	}
    }

    return (KRERR_NO_ERROR);
}



/*****************************************************************************
  FUNCTION : propagateNetBackwardQuickprop

  PURPOSE  : quickprop backward error propagation
  RETURNS  : network error
  NOTES    : quickprop backward error propagation
             (topological) for quickprop with SIGMOID_PRIME_OFFSET
	     batch-modus: without adaption of links and bias

  UPDATE   : 05.11.1993 by Guenter Mamier
******************************************************************************/
static float propagateNetBackwardQuickprop(int pattern_no, int sub_pat_no, 
					   float delta_max)
{
    register struct Link *link_ptr;
    register struct Site *site_ptr;
    register struct Unit *unit_ptr;
    register Patterns out_pat;
    register float  error,	/* error  */
    sum_error,			/* sum of the error  */
    devit;			/* deviation  */
    TopoPtrArray    topo_ptr;
    int size;


    sum_error = 0.0;		/* reset network error  */

    /* calculate address of the output pattern (with number pattern_no + 1)  */
    out_pat = kr_getSubPatData(pattern_no,sub_pat_no,OUTPUT,&size);
    out_pat += size;

    /* add 3 to no_of_topo_units because the topologic array contains 4 NULL
       pointers  */
    topo_ptr = topo_ptr_array + (no_of_topo_units + 3);

    /* calculate output units only  */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	devit = *(--out_pat) - unit_ptr->Out.output;
	/* = o * (1.0 - o) in [0.0,0.25], */
	/* for asymmetric logistic function */

	if ((float) fabs(devit) <= delta_max)
	    continue;

	sum_error += devit * devit; /* sum up the error of the network  */

	/* calc. error for output units	 */
	error = devit * ((unit_ptr->act_deriv_func) (unit_ptr) +
			 SIGMOID_PRIME_OFFSET);

	unit_ptr->value_c += -error; /* calculate the bias slopes  */
	/* learn bias like a weight  */
	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {
	    /* the unit has direct links  */
	    FOR_ALL_LINKS(unit_ptr, link_ptr) {	/* calculate the slopes */
		link_ptr->value_c += -error * link_ptr->to->Out.output;
		link_ptr->to->Aux.flint_no += link_ptr->weight * error;
	    }
	} else {
	    /* the unit has sites  */
	    FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {	
		/* calculate the value_cs  */
		link_ptr->value_c += -error * link_ptr->to->Out.output;
		link_ptr->to->Aux.flint_no += link_ptr->weight * error;
	    }
	}
    }


    /* calculate hidden units only  */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	error = ((unit_ptr->act_deriv_func) (unit_ptr) +
		 SIGMOID_PRIME_OFFSET) * unit_ptr->Aux.flint_no;

	unit_ptr->value_c += -error; /* calculate the bias slopes  */
	/* learn bias like a weight  */
	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {
	    /* the unit has direct links  */
	    FOR_ALL_LINKS(unit_ptr, link_ptr) { /* calculate the slopes  */
		if IS_HIDDEN_UNIT
		    (link_ptr->to)
			/* this link points to a hidden unit: sum up the
			   error's from previos units  */
			link_ptr->to->Aux.flint_no += 
			    link_ptr->weight * error;

		link_ptr->value_c += -error * link_ptr->to->Out.output;
	    }
	} else {
	    /* the unit has sites  */
	    FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {	
		/* calculate the slopes  */
		if IS_HIDDEN_UNIT
		    (link_ptr->to)
			/* this link points to a hidden unit: sum up the
			   error's from previos units  */
			link_ptr->to->Aux.flint_no += 
			    link_ptr->weight * error;

		link_ptr->value_c += -error * link_ptr->to->Out.output;
	    }
	}
    }
    return (sum_error);		/* return the error of the network  */
}


/*****************************************************************************
  FUNCTION : MODI_quickprop

  PURPOSE  : modifies the network at the end of each epoch
  RETURNS  : 
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static void  MODI_quickprop(float learn_parameter, float max_factor, 
			    float decay)
 /* learning parameter */
 /* maximal grow factor of weights */
 /* decay factor */

{
    double          deltaw;	/* actual weight (bias) change */
    float           shfac;	/* shrink factor */
    register struct Link *link_ptr;
    register struct Site *site_ptr;
    register struct Unit *unit_ptr;
    TopoPtrArray    topo_ptr;
    bool            hidden_units;


    /* maximal grow factor of weights is max_factor  */
    shfac = max_factor / (1.0 + max_factor);

    topo_ptr = topo_ptr_array + (NoOfInputUnits + 1);
    hidden_units = TRUE;

    /* calculate hidden and output units only  */
    do {
	if ((unit_ptr = *++topo_ptr) == NULL) {
	    if (!hidden_units)
		break;		/* end of topologic pointer array reached  */
	    unit_ptr = *++topo_ptr;	/* skip NULL pointer  */
	    hidden_units = FALSE;
	}
	if (IS_SPECIAL_UNIT(unit_ptr)) {
	    unit_ptr->value_a = 
	    unit_ptr->value_b = 
	    unit_ptr->value_c = 0.0;
	    if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {/* unit has direct links */
		FOR_ALL_LINKS(unit_ptr, link_ptr) {
		    link_ptr->value_a = 
		    link_ptr->value_b = 
		    link_ptr->value_c = 0.0;
		}
	    } else {		/* the unit has sites  */
		FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {
		    link_ptr->value_a = 
		    link_ptr->value_b = 
		    link_ptr->value_c = 0.0;
		}
	    }
	} else {
	    deltaw = 0.0;	/* adjust bias like a weight  */
	    if (unit_ptr->value_a > 0.0) { /* previous step was positive  */
		if (unit_ptr->value_c < 0.0)
		    /* same direction,i.e. slope, value_b have same sign  */
		    deltaw += learn_parameter * (-unit_ptr->value_c);

		if (unit_ptr->value_c <= shfac * unit_ptr->value_b)
		    /* maximal positive step  */
		    deltaw += max_factor * unit_ptr->value_a;
		else
		    /* littler positive step squared approximation  */
		    deltaw += unit_ptr->value_c / 
			      (unit_ptr->value_b - unit_ptr->value_c)
			      * unit_ptr->value_a;
	    } else if (unit_ptr->value_a < 0.0) {	/* previous step was
							   negative  */
		if (unit_ptr->value_c > 0.0)
		    /* same direction,i.e. slope, prevslope have same sign  */
		    deltaw += learn_parameter * (-unit_ptr->value_c);

		if (unit_ptr->value_c >= shfac * unit_ptr->value_b)
		    /* maximal negative step  */
		    deltaw += max_factor * unit_ptr->value_a;
		else
		    /* littler negative step squared approximation */
		    deltaw += unit_ptr->value_c / 
			      (unit_ptr->value_b - unit_ptr->value_c)
			      * unit_ptr->value_a;
	    } else
		/* previous step was 0.0  */
		/* start of learning process with BP  */
		deltaw += learn_parameter * (-unit_ptr->value_c);

	    unit_ptr->bias += deltaw;	/* new bias */
	    unit_ptr->value_a = deltaw;	/* bias change */
	    unit_ptr->value_b = unit_ptr->value_c;	/* previous slope */
	    unit_ptr->value_c = decay * unit_ptr->bias;	/* set new slope  */

	    /* adjust links */
	    if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {/* unit has direct links */
		FOR_ALL_LINKS(unit_ptr, link_ptr) {
		    deltaw = 0.0;

		    if (link_ptr->value_a > 0.0) { /* prev step was positive */
			if (link_ptr->value_c < 0.0)
			    /* same direction,i.e. slope, prevslope have same
			       sign  */
			    deltaw += learn_parameter * (-link_ptr->value_c);

			if (link_ptr->value_c <= shfac * link_ptr->value_b)
			    /* maximal positive step  */
			    deltaw += max_factor * link_ptr->value_a;
			else
			    deltaw += link_ptr->value_c / 
				      (link_ptr->value_b - link_ptr->value_c)
				      * link_ptr->value_a;
		    } else if (link_ptr->value_a < 0.0) {	
			/* previous step was negative */
			if (link_ptr->value_c > 0.0)
			    /* same direction,i.e. slope, prevslope have same
			       sign */
			    deltaw += learn_parameter * (-link_ptr->value_c);

			if (link_ptr->value_c >= shfac * link_ptr->value_b)
			    /* maximal negative step  */
			    deltaw += max_factor * link_ptr->value_a;
			else
			    deltaw += link_ptr->value_c / 
				      (link_ptr->value_b - link_ptr->value_c)
				      * link_ptr->value_a;
		    } else	/* previous step was 0.0  */
			/* start of learning process with BP  */
			deltaw += learn_parameter * (-link_ptr->value_c);

		    link_ptr->weight += deltaw;	/* new weight */
		    link_ptr->value_a = deltaw;	/* weight change */
		    link_ptr->value_b = link_ptr->value_c;  /* previous slope */
		    /* set new slope  */
		    link_ptr->value_c = decay * link_ptr->weight;
		}		/* for links  */
	    } else {		/* the unit has sites  */
		FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {
		    deltaw = 0.0;
		    if (link_ptr->value_a > 0.0) {	/* previous step was
							   positive  */
			if (link_ptr->value_c < 0.0)
			    /* same direction,i.e. slope, prevslope have same
			       sign  */
			    deltaw += learn_parameter * (-link_ptr->value_c);

			if (link_ptr->value_c <= shfac * link_ptr->value_b)
			    /* maximal positive step  */
			    deltaw += max_factor * link_ptr->value_a;
			else
			    /* littler positive step squared approximation  */
			    deltaw += link_ptr->value_c / 
				      (link_ptr->value_b - link_ptr->value_c)
				       * link_ptr->value_a;
		    } else if (link_ptr->value_a < 0.0) {	
			/* previous step was negative  */
			if (link_ptr->value_c > 0.0)
			    /* same direction,i.e. slope, prevslope have same
			       sign  */
			    deltaw += learn_parameter * (-link_ptr->value_c);

			if (link_ptr->value_c >= shfac * link_ptr->value_b)
			    /* maximal negative step  */
			    deltaw += max_factor * link_ptr->value_a;
			else
			    deltaw += link_ptr->value_c / 
				      (link_ptr->value_b - link_ptr->value_c)
				      * link_ptr->value_a;
		    } else	/* previous step was 0.0  */
			/* start of learning process with BP  */
			deltaw += learn_parameter * (-link_ptr->value_c);

		    link_ptr->weight += deltaw;	/* new weight */
		    link_ptr->value_a = deltaw;	/* weight change */
		    link_ptr->value_b = link_ptr->value_c; /* previous slope */
		    /* set new slope */
		    link_ptr->value_c = decay * link_ptr->weight;
		}
	    }
	}
    }				/* for units  */
    while (TRUE);

}


/*****************************************************************************
  FUNCTION : LEARN_quickprop

  PURPOSE  : Quickprop learning function
  RETURNS  : kernel error code
  NOTES    : Input Parameters:   1 : learning parameter
                                 2 : max factor (of the net after every epoch)
                                 3 : decay
                                 4 : delta max

             Output Parameters:  1 : error of the network (sum of all cycles)


  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err LEARN_quickprop(int start_pattern, int end_pattern, 
			 float *parameterInArray, int NoOfInParams, 
			 float **parameterOutArray, int *NoOfOutParams)
{
    static float OutParameter[1]; /* OutParameter[0] stores the learning error*/
    int          i, pattern_no, sub_pat_no, ret_code;


    if (NoOfInParams < 1)	/* ###  have to be changed  (must be 3)  #### */
	return (KRERR_PARAMETERS);	/* not enough input parameters  */

    *NoOfOutParams = 1;		/* one return value is available (the
				   learning error) */

    *parameterOutArray = OutParameter;	/* set output parameter reference  */
    ret_code = KRERR_NO_ERROR;	/* reset return code  */

    if (NetModified || (TopoSortID != TOPOLOGICAL_FF)) {	
	/* Net has been modified or topologic array isn't initialized */
	/* check the topology of the network  */
	ret_code = kr_topoCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);	/* an error has occured  */
	if (ret_code < 2)
	    return (KRERR_FEW_LAYERS);	/* the network has less then 2 layers */

	/* count the no. of I/O units and check the patterns  */
	ret_code = kr_IOCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);

	/* sort units by topology and by topologic type  */
	ret_code = kr_topoSort(TOPOLOGICAL_FF);
	if ((ret_code != KRERR_NO_ERROR) && (ret_code != KRERR_DEAD_UNITS))
	    return (ret_code);

	NetModified = FALSE;
    }
    if (NetInitialize || LearnFuncHasChanged) {	/* Net has been modified or
						   initialized, initialize
						   backprop now  */
	ret_code = initializeQuickprop();
	if (ret_code != KRERR_NO_ERROR)
	    return (ret_code);
    }


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    NET_ERROR(OutParameter) = 0.0;	/* reset network error value  */

    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	propagateNetForward(pattern_no,sub_pat_no);   /* Forward propagation */

	/* backward propagation and summation of gradient  */
	NET_ERROR(OutParameter) +=
	    propagateNetBackwardQuickprop(pattern_no,sub_pat_no,
					  LEARN_PARAM4(parameterInArray));
    }

    /* modificate links and bias  */
    MODI_quickprop(LEARN_PARAM1(parameterInArray),
		   LEARN_PARAM2(parameterInArray),
		   LEARN_PARAM3(parameterInArray));

    return (ret_code);
}



/*****************************************************************************
 *****************************************************************************

  GROUP        : Counterpropagation learning function

  AUTHOR       :
  DATE         : 01.01.1990
  LAST CHANGE  : 05.11.1993

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG
             
******************************************************************************
******************************************************************************/

/*****************************************************************************
  FUNCTION : initializeCPN

  PURPOSE  : Counterpropagation initialisation
  RETURNS  : kernel error code
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err initializeCPN(void)
{
    register struct Unit *unit_ptr;


    NoOfLearnedPatterns = 0;

    /* set unit's bias to zero  */
    FOR_ALL_UNITS(unit_ptr)
	if ((unit_ptr->flags & UFLAG_IN_USE) == UFLAG_IN_USE)
	/* unit is in use  */
	unit_ptr->bias = (FlintType) 0;

    return (KRERR_NO_ERROR);
}


/*****************************************************************************
  FUNCTION : normalize_weight

  PURPOSE  : Counterpropagation initialisation
  RETURNS  : 
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static void normalize_weight(struct Unit * winner_ptr, float sum)
{
    register struct Site *site_ptr;
    register struct Link *link_ptr;
    register float  amount;


    amount = 1.0 / sqrt(sum);

    /* not necessary to see whether this is a special unit */

    if (winner_ptr->flags & UFLAG_SITES)
	/* the unit has sites */
	FOR_ALL_SITES_AND_LINKS(winner_ptr, site_ptr, link_ptr)
	    link_ptr->weight = link_ptr->weight * amount;
    else
	/* the unit has direct links */
	FOR_ALL_LINKS(winner_ptr, link_ptr)
	    link_ptr->weight = link_ptr->weight * amount;
}


/*****************************************************************************
  FUNCTION : normalize_inputvector 

  PURPOSE  :
  RETURNS  : 
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static void normalize_inputvector(float sum)
{
    register struct Unit *unit_ptr;
    register float  amount;


    amount = 1.0 / sqrt(sum);

    FOR_ALL_UNITS(unit_ptr)
	if (IS_INPUT_UNIT(unit_ptr) && UNIT_IN_USE(unit_ptr))
	/* this is a input unit */
	unit_ptr->Out.output = unit_ptr->Out.output * amount;
}



/*****************************************************************************
  FUNCTION : propagateNet_CPN

  PURPOSE  : forward pass of counterprop
  RETURNS  : 
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static float propagateNet_CPN(int pattern_no, int sub_pat_no, float alpha,
			      float beta, float threshold)
{
    register struct Link *link_ptr;
    register struct Site *site_ptr;
    register struct Unit *unit_ptr;
    register struct Unit *winner_ptr;
    register Patterns in_pat, out_pat;
    float           maximum, sum_error, devit, learn_error, sum;
    float           unit_ptr_net;
    float           noOfPatterns_mul_NoHiddenUnits;
    register TopoPtrArray topo_ptr;

    /* calculate the activation and the output values         */
    /* of the input units (Input Layer)                       */

    noOfPatterns_mul_NoHiddenUnits = (float) NoOfLearnedPatterns *
	                             (float) NoOfHiddenUnits;

    sum = 0.0;

    /* calculate startaddress for input pattern array  */
    in_pat = kr_getSubPatData(pattern_no,sub_pat_no,INPUT,NULL);


    topo_ptr = topo_ptr_array;

    /* copy pattern into input unit's activation and calculate output of the
       input units */
    while ((unit_ptr = *++topo_ptr) != NULL) {	
	
	/* topo_ptr points to the unit stuctures (sorted by: input-, hidden- 
	   and output-units, separated with NULL pointers) */
	sum += *in_pat * *in_pat;

	if (unit_ptr->out_func == OUT_IDENTITY)
	    /* identity output function: there is no need to call the output
	       function  */
	    unit_ptr->Out.output = unit_ptr->act = *in_pat++;
	else
	    /* no identity output function: calculate unit's output also  */
	    unit_ptr->Out.output = 
		(*unit_ptr->out_func) (unit_ptr->act = *in_pat++);
    }

    if (sum != 0.0)
	/* normalize the inputvector */
	normalize_inputvector(sum);


    /* propagate Kohonen Layer   */

    /* calculate the activation and the output values         */
    /* of the hidden units (Kohonen Layer)                    */


    winner_ptr = NULL;
    maximum = -1.0e30;		/* contains the maximum of the activations */

    /* popagate hidden units  */
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* topo_ptr points to a
						   (topological sorted) unit
						   stucture */
	unit_ptr_net = 0.0;
	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {	/* the unit has direct links */
	    FOR_ALL_LINKS(unit_ptr, link_ptr)
		unit_ptr_net += (link_ptr->weight * link_ptr->to->Out.output);
	} else {		/* the unit has sites	 */
	    FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr)
		unit_ptr_net += (link_ptr->weight * link_ptr->to->Out.output);
	}

	if (unit_ptr->bias >= noOfPatterns_mul_NoHiddenUnits)
	    unit_ptr_net -= threshold;

	if (maximum < unit_ptr_net) {	/* determine winner unit  */
	    winner_ptr = unit_ptr;
	    maximum = unit_ptr_net;
	}
	/* reset output and activation of hidden units  */
	unit_ptr->Out.output = unit_ptr->act = (FlintType) 0;
    }


    /* the competitive winner is chosen                */

    winner_ptr->Out.output = winner_ptr->act = (FlintType) 1;
    winner_ptr->bias++;



    /* Training the Kohonen Layer                   
       
       Only the weights of links that go to the winning unit are adjusted, 
       the others remain the same. The incoming weights to the competitive 
       unit are adapted as follows:
         
       weight(new) = weight(old) + eta * (output - weight(old))  
         
       where eta is the learning constant (0<eta<=1.0)             
       and output is the output of the input unit                  
    */ 


    if (!IS_SPECIAL_UNIT(winner_ptr)) {
	sum = 0.0;
	if (winner_ptr->flags & UFLAG_DLINKS) {	/* the winner unit has direct
						   links  */
	    FOR_ALL_LINKS(winner_ptr, link_ptr) {
		devit = link_ptr->to->Out.output - link_ptr->weight;
		learn_error = alpha * devit;
		link_ptr->weight += learn_error;
		/* this is needed for the normalization of the weight_vector */
		sum += link_ptr->weight * link_ptr->weight;
	    }
	} else {		/* the winner unit has sites  */
	    FOR_ALL_SITES_AND_LINKS(winner_ptr, site_ptr, link_ptr) {
		devit = link_ptr->to->Out.output - link_ptr->weight;
		learn_error = alpha * devit;
		link_ptr->weight += learn_error;
		/* this is needed for the normalization of the weight_vector */
		sum += link_ptr->weight * link_ptr->weight;
	    }
	}
	if (sum != 0.0)
	    normalize_weight(winner_ptr, sum);
    }


    /* propagate Grossberg Layer                      */
    /* Training the Grossberg Layer                   */
    /* Adaptation of the Grossberg Layer weights is done by the    */
    /* Widrow-Hoff rule:                                           */

    /* weight(new) = weight(old) + beta * (target output - output) */

    /* for all weights connected with the winning unit of the      */
    /* Kohonen Layers                                              */


    /* calculate address of the output pattern */
    out_pat = kr_getSubPatData(pattern_no,sub_pat_no,OUTPUT,NULL);


    sum_error = 0.0;

    /* popagate output units  */
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* topo_ptr points to a
						   (topological sorted) unit
						   stucture */

	/* calculate the activation and the output values         */
	/* of the output units (Grossberg Layer)                  */

	/* the activation function is the identity function (weighted sum)
	   and identity output function */
	unit_ptr->Out.output = unit_ptr->act = (*unit_ptr->act_func) (unit_ptr);

	devit = *out_pat++ - unit_ptr->Out.output;    /* calculate devitation */
	sum_error += devit * devit;
	learn_error = beta * devit;

	if (!IS_SPECIAL_UNIT(unit_ptr)) {
	    if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)){ /* unit has direct links */
		FOR_ALL_LINKS(unit_ptr, link_ptr)
		    if (link_ptr->to == winner_ptr) {
			/* link to the winning unit of the Kohonen Layer */
			link_ptr->weight += learn_error;
			break;
		    }
	    } else {		/* the unit has sites */
		FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr)
		    if (link_ptr->to == winner_ptr) {
			/* link to the winning unit of the Kohonen Layer */
			link_ptr->weight += learn_error;
			break;
		    }
	    }
	}
    }

    return (sum_error);
}



/*****************************************************************************
  FUNCTION : LEARN_CPN

  PURPOSE  : main function for counterpropagtion
  RETURNS  : 
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err  LEARN_CPN(int start_pattern, int end_pattern, 
		    float *parameterInArray, int NoOfInParams, 
		    float **parameterOutArray, int *NoOfOutParams)
{
    static float    OutParameter[1];	/* OutParameter[0] stores the
					   learning error  */
    int             ret_code, i, pattern_no, sub_pat_no;


    if (NoOfInParams < 1)	/* have to be changed (must be 3) */
	return (KRERR_PARAMETERS);	/* Not enough input parameters  */

    *NoOfOutParams = 1;		/* one return value is available (the
				   learning error) */
    *parameterOutArray = OutParameter;	/* set output parameter reference  */
    ret_code = KRERR_NO_ERROR;	/* clear return code  */


    if (NetModified || (TopoSortID != TOPOLOGIC_TYPE)) {	
	/* Net has been modified  or topologic array isn't initialized */
	/* check the topology of the network  */
	ret_code = kr_topoCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);	/* an error has occured  */
	if (ret_code != 3)
	    return (KRERR_FEW_LAYERS);	/* the network has less then 2 layers */

	/* count the no. of I/O units and check the patterns  */
	ret_code = kr_IOCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);

	/* sort units by topology and by topologic type  */
	ret_code = kr_topoSort(TOPOLOGIC_TYPE);
	if ((ret_code != KRERR_NO_ERROR) && (ret_code != KRERR_DEAD_UNITS))
	    return (ret_code);

	NetModified = FALSE;
    }
    if (NetInitialize || LearnFuncHasChanged) {	/* Net has been modified or
						   initialized, initialize
						   backprop now  */
	ret_code = initializeCPN();
	if (ret_code != KRERR_NO_ERROR)
	    return (ret_code);
    }


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    NET_ERROR(OutParameter) = 0.0;	/* reset network error value  */

    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	NoOfLearnedPatterns++;
	NET_ERROR(OutParameter) 
	    += propagateNet_CPN(pattern_no,sub_pat_no,
				LEARN_PARAM1(parameterInArray),
				LEARN_PARAM2(parameterInArray),
				LEARN_PARAM3(parameterInArray));
    }

    return (ret_code);
}




/*****************************************************************************
 *****************************************************************************

  GROUP        : Back-Percolation Learning Function

  AUTHOR       : Artemis Hatzigeorgiou  Algorithm by Mark Jurik
  DATE         : 01.01.1992
  LAST CHANGE  : 06.11.1993

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG
             
******************************************************************************
******************************************************************************/

/*****************************************************************************
  FUNCTION : propagateNetForward_perc

  PURPOSE  : topological forward propagation
  RETURNS  : 
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static void propagateNetForward_perc(int pattern_no, int sub_pat_no)
{
    register struct Unit *unit_ptr;
    register Patterns in_pat;
    register TopoPtrArray topo_ptr;

    /* calculate startaddress for input pattern array  */
    in_pat = kr_getSubPatData(pattern_no,sub_pat_no,INPUT,NULL);

    topo_ptr = topo_ptr_array;

    /* copy pattern into input unit's activation and calculate output of the
       input units */
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* topo_ptr points to a
						   (topological sorted) unit
						   stucture (input units
						   first)  */
	if (unit_ptr->out_func == OUT_IDENTITY)
	    /* identity output function: there is no need to call the output
	       function  */
	    unit_ptr->Out.output = unit_ptr->act = *in_pat++;
	else
	    /* no identity output function: calculate unit's output also  */
	    unit_ptr->Out.output = 
		(*unit_ptr->out_func) (unit_ptr->act = *in_pat++);
    }

    /* popagate hidden units  */
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* topo_ptr points to a
						   (topological sorted) unit
						   stucture */
	/* clear values  */
	unit_ptr->Aux.flint_no = 0.0;
	unit_ptr->value_a = 0.0;
	unit_ptr->value_b = 0.000001;

	/* calculate the activation value of the unit: call the activation
	   function if needed  */
	unit_ptr->act = (*unit_ptr->act_func) (unit_ptr);

	if (unit_ptr->out_func == OUT_IDENTITY)
	    /* identity output function: there is no need to call the output
	       function  */
	    unit_ptr->Out.output = unit_ptr->act;
	else
	    /* no identity output function: calculate unit's output also  */
	    unit_ptr->Out.output = (*unit_ptr->out_func) (unit_ptr->act);
    }

    /* popagate output units  */
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* topo_ptr points to a
						   (topological sorted) unit
						   stucture */
	/* clear values  */
	unit_ptr->Aux.flint_no = 0.0;
	unit_ptr->value_a = 0.0;
	unit_ptr->value_b = 0.000001;

	/* calculate the activation value of the unit: call the activation
	   function if needed  */
	unit_ptr->act = (*unit_ptr->act_func) (unit_ptr);

	if (unit_ptr->out_func == OUT_IDENTITY)
	    /* identity output function: there is no need to call the output
	       function  */
	    unit_ptr->Out.output = unit_ptr->act;
	else
	    /* no identity output function: calculate unit's output also  */
	    unit_ptr->Out.output = (*unit_ptr->out_func) (unit_ptr->act);
    }
}


/*****************************************************************************
  FUNCTION : propagateNetBackward_perc

  PURPOSE  :topological backward propagation
  RETURNS  : network error
  NOTES    :

  UPDATE   : 07.02.1994 by Sven Doering
******************************************************************************/
static float propagateNetBackward_perc(int pattern_no, int sub_pat_no,
				       float learn_parameter,
				       float delta_max, float *perc_error)
{
    register struct Link *link_ptr;
    register struct Unit *unit_ptr;
    register Patterns out_pat;
    register float  error, sum_error, eta, devit;
    register TopoPtrArray topo_ptr;
    register float  norm, delta_sig_normaliser, message_weight;
    register float  act_err, normalised_error, scaled_error, 
                    delta_weight_normaliser;
    register float  der = 0.0;
    register float  tmp;	
    register int    is_special;
    int size;

    sum_error = 0.0;		/* reset network error  */
    eta = learn_parameter;	/* store learn_parameter in CPU register  */

    /* calculate address of the output pattern (with number pattern_no + 1)  */
    out_pat = kr_getSubPatData(pattern_no,sub_pat_no,OUTPUT,&size);
    out_pat += size;

    /* add 3 to no_of_topo_units because the topologic array contains 4 NULL
       pointers  */
    topo_ptr = topo_ptr_array + (no_of_topo_units + 3);

    /* calculate output units only  */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	devit = *(--out_pat) - unit_ptr->Out.output;	/* calc. devitation */

	if (fabs(devit) > delta_max) {	/* calc. error for output units     */
	    *perc_error += fabs(devit);
	    error = -2.0 * devit * (unit_ptr->act_deriv_func) (unit_ptr);
	    act_err = devit * eta;
	    sum_error += devit * devit;	/* sum up the error of the network  */
	} else {		/* set error of output units to zero	 */
	    error = 0.0;
	    act_err = 0.000001 * eta;
	    continue;
	}

	/* calc. the error for adjusting weights and bias of the predecessor
	   units  */

	norm = 0.0;
	delta_sig_normaliser = 0.000001;
	FOR_ALL_LINKS(unit_ptr, link_ptr) {	/* adjust link weights and
						   calc. sum of errors of the
						   predecessor units  */
	    if (IS_HIDDEN_UNIT(link_ptr->to))
		norm += fabs(link_ptr->weight);
	    delta_sig_normaliser += SQR(link_ptr->to->Out.output);
	}
	delta_weight_normaliser = delta_sig_normaliser + 1;
	norm += delta_sig_normaliser;
	is_special = IS_SPECIAL_UNIT(unit_ptr);
	normalised_error = act_err / norm;
	scaled_error = act_err / delta_weight_normaliser;
	FOR_ALL_LINKS(unit_ptr, link_ptr) {
	    tmp = link_ptr->weight * error;
	    link_ptr->to->Aux.flint_no += tmp;

	    message_weight = tmp * tmp;
	    if (!is_special) {
		link_ptr->to->value_a += link_ptr->weight * 
		    normalised_error * message_weight;
		link_ptr->to->value_b += message_weight;
		link_ptr->weight += link_ptr->to->Out.output * scaled_error;
	    }
	}


	/* adjust bias value  */
	if (!is_special)
	    unit_ptr->bias += scaled_error;
    }

    /* calculate hidden units only  */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	der = (unit_ptr->act_deriv_func) (unit_ptr);
	error = der * unit_ptr->Aux.flint_no;
	act_err = (unit_ptr->value_a / unit_ptr->value_b) * der;

	/* calc. the error for adjusting weights and bias of the predecessor
	   units  */

	norm = 0.0;
	delta_sig_normaliser = 0.000001;
	FOR_ALL_LINKS(unit_ptr, link_ptr) {
	    if (IS_HIDDEN_UNIT(link_ptr->to))
		norm += fabs(link_ptr->weight);

	    delta_sig_normaliser += SQR(link_ptr->to->Out.output);
	}
	delta_weight_normaliser = delta_sig_normaliser + 1;
	norm += delta_sig_normaliser;
	is_special = IS_SPECIAL_UNIT(unit_ptr);
	normalised_error = act_err / norm;
	scaled_error = act_err / delta_weight_normaliser;
	FOR_ALL_LINKS(unit_ptr, link_ptr) {
	    tmp = link_ptr->weight * error;
	    link_ptr->to->Aux.flint_no += tmp;

	    message_weight = tmp * tmp;

	    if (!is_special) {
		link_ptr->to->value_a += link_ptr->weight * 
		    normalised_error * message_weight;
		link_ptr->to->value_b += message_weight;
		link_ptr->weight += link_ptr->to->Out.output * scaled_error;
	    }
	}


	/* adjust bias value  */
	if (!is_special)
	    unit_ptr->bias += scaled_error;
    }

    return (sum_error);		/* return the error of the network */
}



/*****************************************************************************
  FUNCTION : LEARN_perc

  PURPOSE  : main function for backpercolation
  RETURNS  : kernel error code
  NOTES    : Input Parameters:   1 : learning parameter
                                 2 : delta max

             Output Parameters:  1 : error of the network (sum of all cycles)

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err LEARN_perc(int start_pattern, int end_pattern, 
	            float *parameterInArray, int NoOfInParams, 
	            float **parameterOutArray, int *NoOfOutParams)
{
    static float    OutParameter[1];	/* OutParameter[0] stores the
					   learning error  */
    int             i, ret_code, pattern_no, sub_pat_no;
    float           p_error, l_error;
    register struct Unit *unit_ptr;

    if (NoOfInParams < 1)	        /* have to be changed (must be 2)  */
	return (KRERR_PARAMETERS);	/* Not enough input parameters  */

    *NoOfOutParams = 1;		        /* One return value is available (the
				           learning error)  */
    *parameterOutArray = OutParameter;	/* set the output parameter reference */
    ret_code = KRERR_NO_ERROR;	/* reset return code  */

    if (NetModified || (TopoSortID != TOPOLOGICAL_FF)) {	
	/* Net has been modified or topologic array isn't initialized */
	/* check the topology of the network  */
	FOR_ALL_UNITS(unit_ptr)
	    if UNIT_HAS_SITES
	    (unit_ptr)
		return (KRERR_SITES_NO_SUPPORT);

	ret_code = kr_topoCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);	/* an error has occured  */
	if (ret_code < 2)
	    return (KRERR_FEW_LAYERS);	/* the network has less then 2 layers */

	/* count the no. of I/O units and check the patterns  */
	ret_code = kr_IOCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);

	/* sort units by topology and by topologic type  */
	ret_code = kr_topoSort(TOPOLOGICAL_FF);
	if ((ret_code != KRERR_NO_ERROR) && (ret_code != KRERR_DEAD_UNITS))
	    return (ret_code);

	NetModified = FALSE;
    }
    if (NetInitialize || LearnFuncHasChanged) {	/* Net has been modified or
						   initialized, initialize
						   backprop now  */
	if (ret_code != KRERR_NO_ERROR)
	    return (ret_code);
	parameterInArray[4] = 1.0;
    }


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    NET_ERROR(OutParameter) = 0.0;	/* reset network error value  */
    p_error = 0.0;

    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	propagateNetForward_perc(pattern_no,sub_pat_no);  /* Forward pass */

	/* Backward propagation  */
	/* 1st parameter is the learning parameter 2nd parameter is the max.
	   devitation between output pattern and the output of the output
	   unit (delta max) */

	NET_ERROR(OutParameter) +=
	    propagateNetBackward_perc(pattern_no,sub_pat_no,
				      LEARN_PARAM1(parameterInArray),
				      LEARN_PARAM3(parameterInArray), &p_error);
    }

    p_error = p_error / (kr_TotalNoOfSubPatPairs()* NoOfOutputUnits);

    if (p_error < LEARN_PARAM2(parameterInArray)) {
	p_error = (parameterInArray[4] + p_error) / 2;
	l_error = exp((parameterInArray[4] - p_error) / 
		      (parameterInArray[4] + p_error));
	if (l_error <= 0.5)
	    l_error = 0.5;
	else if (l_error >= 1.05)
	    l_error = 1.05;
	parameterInArray[0] = parameterInArray[0] * l_error;
    }
    parameterInArray[4] = p_error;

    return (ret_code);
}



/*****************************************************************************
 *****************************************************************************

  GROUP  : Radial Basis Functions Learning

  AUTHOR : Michael Vogt
  Notes  : Use of special entries in links and units with RBFs:

           for Units in hidden layer:
	   Unit value_a: |X - L|^2  == norm^2 == square of euclidean distance 
	                  between all links and all input units to this unit.
	   Unit value_b: delta_BIAS == sum of all deltas to BIAS during learning
	   Unit value_c: Backpropagated weighted sum of errors in output layer

	   for Units in output layer:
	   Unit value_a: error (y_learn - y_net) during learning current pattern
	   Unit value_b: delta_BIAS == sum of all deltas to BIAS during learning

	   for links between input and hidden layer:
	   Link value_b: delta for this link during learning (link treated as 
	                 vector)
	   Link value_a: Momentum term for this link (last change)

	   for links between hidden and output layer:
	   Link value_b: delta for weight of this link during learning.
	   Link value_a: Momentum term for this link (last change)
 
	   for links between input and output layer:
	   Link value_b: delta for weight of this link during learning.
	   Link value_a: Momentum term for this link (last change)


  DATE         : 01.04.1992
  LAST CHANGE  : 06.11.1993

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG
             
******************************************************************************
******************************************************************************/


/*****************************************************************************
  FUNCTION : RbfLearnClean

  PURPOSE  : Clean all deltas, so that learning can start.
  RETURNS  : kernel error code
  NOTES    : Called every time LEARN_RBF is called to be sure that there is 
             no stuff inside the value_b fields of links and units

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err RbfLearnClean(void)
{
    register struct Unit *unit_ptr;
    register struct Link *link_ptr;

    FOR_ALL_UNITS(unit_ptr) {
	unit_ptr->value_b = 0.0;
	FOR_ALL_LINKS(unit_ptr, link_ptr) {
	    link_ptr->value_b = 0.0;
	}
    }

    return KRERR_NO_ERROR;
}



/*****************************************************************************
  FUNCTION : RbfLearnForward

  PURPOSE  : Forward propagation of current pattern. Calculation of different
             value_a fields. value_c of hidden units is set to 0.0
  RETURNS  : kernel error code
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err  RbfLearnForward(int pattern_no, int sub_pat_no)
{
    register struct Unit *unit_ptr;
    register Patterns current_in_pattern;
    register Patterns current_out_pattern;
    register TopoPtrArray topo_ptr;

    /* calculate index of current input pattern in Pattern array:	 */
    current_in_pattern = kr_getSubPatData(pattern_no,sub_pat_no,INPUT,NULL);

    /* activate input units with current patterns and calculate	 */
    /* their output value:						 */

    topo_ptr = topo_ptr_array;
    while ((unit_ptr = *(++topo_ptr)) != NULL) {
	/* go through all input units, set activation and calculate */
	/* output:							 */

	unit_ptr->act = *current_in_pattern++;
	unit_ptr->Out.output = unit_ptr->out_func == OUT_IDENTITY
	    ? unit_ptr->act
	    : (*unit_ptr->out_func) (unit_ptr->act);
    }

    /* activate hidden units, by calling the activation function	 */
    /* (has to be a RBF activation function which places norm ^ 2	 */
    /* into value_a of the unit: see trans_f.c: RbfUnitGetNormsqr). */
    /* The output function is supposed to be OUT_IDENTITY !		 */
    /* (so the output function is never called !)			 */

    while ((unit_ptr = *(++topo_ptr)) != NULL) {
	unit_ptr->act = unit_ptr->Out.output =
	    (*unit_ptr->act_func) (unit_ptr);

	unit_ptr->value_c = 0.0;
    }

    /* activate output units. Again, the output function is supposed */
    /* to be OUT_IDENTITY. The calculated output is compared to the */
    /* current pattern, the error (difference) is calculated and    */
    /* stored in value_a of the current output unit.		 */

    current_out_pattern = kr_getSubPatData(pattern_no,sub_pat_no,OUTPUT,NULL);

    while ((unit_ptr = *(++topo_ptr)) != NULL) {
	unit_ptr->act = unit_ptr->Out.output =
	    (*unit_ptr->act_func) (unit_ptr);
	unit_ptr->value_a = *current_out_pattern++ - unit_ptr->act;
    }

    return KRERR_NO_ERROR;
}


#define	RBF_LEARN_CENTER	0x1
#define RBF_LEARN_BIAS		0x2
#define RBF_LEARN_WEIGHT	0x4
#define RBF_LEARN_PAIN		0x8

/*****************************************************************************
  FUNCTION : RbfLearnAdjustDelta

  PURPOSE  : Adjusting of all deltas (value_b fields) by using the current 
             input pattern (activation of input units) and the stored error 
	     of the output units (value_a). value_c of hidden units is used 
	     too!
  RETURNS  : 
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
float RbfLearnAdjustDelta(float para_center, float para_bias, 
			  float para_weight, float para_pain, 
			  float para_momentum, float para_delta_max, 
			  int learn_mask)
{
    register struct Unit *curr_unit;	/* current unit		 */
    register struct Link *curr_link;	/* current link		 */
    register struct Unit *source_unit;	/* input unit to link	 */
    register TopoPtrArray topo_ptr;
    register float  center_delta;	/* delta of centers	 */
    register float  w_error;	        /* weighted error of 	 */
                                        /* output unit		 */
    register float  w2_error;           /* w_error for special u.*/
    register float  learn_error;

    /* start with last unit in output layer:			 */
    topo_ptr = topo_ptr_array + no_of_topo_units + 3;

    learn_error = 0.0;

    /* work on the output layer and all links leading to it:	 */

    while ((curr_unit = *(--topo_ptr)) != NULL) {
	/* go on with next unit if |error| <= delta_max		 */
	if ((float) fabs(curr_unit->value_a) <= para_delta_max)
	    continue;

	/* error, weighted by the deviation of the activation:	 */
	w2_error = w_error = (curr_unit->value_a) *
	    (*curr_unit->act_deriv_func) (curr_unit);

	/* sum up the learning error:				 */
	learn_error += (curr_unit->value_a) * (curr_unit->value_a);

	if (learn_mask & RBF_LEARN_WEIGHT) {
	    /* sum up all deltas for change of bias:		 */

#ifdef RBF_INCR_LEARNING
	    if (IS_SPECIAL_UNIT(curr_unit)
		w_error = 0.0;
	    curr_unit->bias += para_weight * w_error;
#else
	    curr_unit->value_b += w_error;
#endif
	}
	if (learn_mask) {
	    FOR_ALL_LINKS(curr_unit, curr_link) {
		source_unit = curr_link->to;

		/* sum up deltas for change of link weight:	 */

#ifdef RBF_INCR_LEARNING
		curr_link->weight += para_weight * w_error *
		    source_unit->Out.output;
#else
		curr_link->value_b += w_error * source_unit->Out.output;
#endif

		/* if comming from hidden unit: sum up delta for change */
		/* of bias of hidden unit:			        */
		if (IS_HIDDEN_UNIT(source_unit))
		    source_unit->value_c += w2_error * curr_link->weight;
	    }
	}
    }

    /* work on the hidden layer and all links leading to it:	 */

    if (learn_mask & (RBF_LEARN_CENTER | RBF_LEARN_BIAS)) {
	while ((curr_unit = *(--topo_ptr)) != NULL) {
	    /* now calculate delta for weights of links (centers of the */
	    /* RBF function)						 */
	    curr_unit->Aux.int_no = 2;	/* derivated to norm ^2 */
	    center_delta = curr_unit->value_c *
		(*curr_unit->act_deriv_func) (curr_unit);

	    if (learn_mask & RBF_LEARN_CENTER) {
#ifdef RBF_INCR_LEARNING
		if (IS_SPECIAL_UNIT(curr_unit))
		    center_delta = 0.0;
#endif
		FOR_ALL_LINKS(curr_unit, curr_link) {

#ifdef RBF_INCR_LEARNING
		    curr_link->weight += para_center * center_delta *
			((curr_link->to->Out.output) - (curr_link->weight));
#else
		    curr_link->value_b += center_delta *
			((curr_link->to->Out.output) - (curr_link->weight));
#endif
		}
	    }
	    /* calculate delta for bias (parameter of RBF function):	 */
	    curr_unit->Aux.int_no = 3;	/* derivation to bias!  */

#ifdef RBF_INCR_LEARNING
	    if (!IS_SPECIAL_UNIT(curr_unit))
		curr_unit->bias += para_bias * curr_unit->value_c *
		    (*curr_unit->act_deriv_func) (curr_unit);
#else
	    curr_unit->value_b += curr_unit->value_c *
		(*curr_unit->act_deriv_func) (curr_unit);
#endif
	}
    }
    return learn_error;
}



/*****************************************************************************
  FUNCTION : RbfLearnAdjustWeights

  PURPOSE  : Adjusting of all learnable parameters, depending on collected 
             deltas and on actual learning parameters.
  RETURNS  : 
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
void RbfLearnAdjustWeights(float para_center, float para_bias, 
			   float para_weight, float para_momentum)
{
    register struct Unit *curr_unit;	/* current unit		 */
    register struct Link *curr_link;	/* current link		 */
    register TopoPtrArray topo_ptr;

#ifdef RBF_DELTA_PROT
    static int      step = 0;	/* current learning step */
    char            filename[20];	/* Name of prot file	 */
    FILE           *protfile;	/* filepointer		 */

#endif

#ifdef RBF_DELTA_PROT
    step++;
    sprintf(filename, "rbf_%04d.prot", step);
    protfile = fopen(filename, "w");
    if (protfile == NULL)
	fprintf(stderr, "RbfLearnAdjustWeights: Can't open protfile\n");
#endif

    /* start with last unit in output layer:			 */
    topo_ptr = topo_ptr_array + no_of_topo_units + 3;

#ifdef RBF_DELTA_PROT
    fprintf(protfile, "%s\t\t\n", "h -> o");
#endif

    while ((curr_unit = *(--topo_ptr)) != NULL) {
	if (!IS_SPECIAL_UNIT(curr_unit)) {
	    /* adjust bias of output unit:                       */
	    curr_unit->bias += para_weight * (curr_unit->value_b);

#ifdef RBF_DELTA_PROT
	    fprintf(protfile, "%13s:\t\n", curr_unit->unit_name);
#endif

	    /* adjust weights of links leading to this unit:	 */
	    FOR_ALL_LINKS(curr_unit, curr_link) {

#ifdef RBF_DELTA_PROT
		fprintf(protfile, "%-10.2e\t\n",
			para_weight * (curr_link->value_b));
#endif

		curr_link->weight +=
		    (curr_link->value_a = para_weight * (curr_link->value_b)
		     + para_momentum * curr_link->value_a);
	    }
	}
    }

    /* now adjust weights of hidden layer:			 */

#ifdef RBF_DELTA_PROT
    fprintf(protfile, "%s\t\t\n", "i -> h");
#endif

    while ((curr_unit = *(--topo_ptr)) != NULL) {
	if (!IS_SPECIAL_UNIT(curr_unit)) {
	    /* adjust bias of hidden unit (parameter of RBF function):	 */
	    curr_unit->bias += para_bias * (curr_unit->value_b);
	    if (curr_unit->bias <= 0.0)
		fprintf(stderr, "Hidden unit bias %f !\n", curr_unit->bias);

#ifdef RBF_DELTA_PROT
	    fprintf(protfile, "%13s:\t\n", curr_unit->unit_name);
#endif

	    /* adjust weights of links (centers of RBF functions):	 */
	    FOR_ALL_LINKS(curr_unit, curr_link) {

#ifdef RBF_DELTA_PROT
		fprintf(protfile, "%-10.2e\t\n",
			para_center * (curr_link->value_b));
#endif

		curr_link->weight +=
		    (curr_link->value_a = para_center * (curr_link->value_b)
		     + para_momentum * curr_link->value_a);
	    }
	}
    }

#ifdef RBF_DELTA_PROT
    fclose(protfile);
#endif
}



/*****************************************************************************
  FUNCTION : RbfTopoCheck

  PURPOSE  : Topological Check for Radial Basis Functions.
             Also the number of output units is compared to the patterns.
  RETURNS  : 
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err RbfTopoCheck(void)
{
    krui_err        ret_code;	/* error return code		 */

    /* Net has been modified or topologic array isn't		 */
    /* initialized. check the topology of the network.		 */
    ret_code = kr_topoCheck();
    if (ret_code < KRERR_NO_ERROR)
	return (ret_code);	/* an error has occured */
    if (ret_code < 2)
	return (KRERR_NET_DEPTH);	/* the network has less */
    /* then 2 layers	 */

    /* count the no. of I/O units and check the patterns	 */
    ret_code = kr_IOCheck();
    if (ret_code < KRERR_NO_ERROR)
	return (ret_code);

    /* sort units by topology and by topologic type		 */
    ret_code = kr_topoSort(TOPOLOGICAL_FF);

    return ret_code;
}


/*****************************************************************************
  FUNCTION : LEARN_RBF

  PURPOSE  : Learning function for RBF (GRBF) called from kernel.
  RETURNS  : kernel error code
  NOTES    : Use of Learning Parameters:
             LEARN_PARAM1: learning parameter for adjusting centers (links 
	                   between input and hidden layer, treated as vectors)
             LEARN_PARAM2: learning parameter for adjusting RBF-parameter 
	                   (BIAS of units in hidden layer)
             LEARN_PARAM3: learning parameter for adjusting weights (all links
	                   to output layer + bias of output units)
	     LEARN_PARAM4: maximum difference between output value and teaching
                           input which is treated as error 0.0 (delta_max)
             LEARN_PARAM5: factor for momentum term

  UPDATE   : 06.11.1993 by Guenter Mamier 
******************************************************************************/
krui_err LEARN_RBF(int start_pattern, int end_pattern, 
		   float *parameterInArray, int NoOfInParams, 
		   float **parameterOutArray, int *NoOfOutParams)
{
    static float    OutParameter[1];	/* OutParameter[0] stores	 */
                                        /* the learning error   	 */
    int             i, ret_code, pattern_no, sub_pat_no, learn_mask;
    float           para_bias, para_center, para_weight, para_pain, 
                    para_momentum,para_delta_max;

    register struct Unit *unit_ptr;
    register struct Link *link_ptr;

#ifdef RBF_LEARN_PROT
    static int      schritt = 1;
    int             fehler_zaehler = 0;
    float           temp_fehler;
    FILE           *protfile;

#endif

    if (NoOfUnits == 0)
	return (KRERR_NO_UNITS);/* No Units defined		 */
    if (NoOfInParams < 1)	/* has to be changed (must be 4) */
	return (KRERR_PARAMETERS);	/* Not enough input parameters  */

    *NoOfOutParams = 1;		/* One return value is available */
                                /* (the learning error)		 */
    *parameterOutArray = OutParameter;	/* set the reference to */
                                        /* the output parameter */

    ret_code = KRERR_NO_ERROR;	/* default return code		 */

    if (NetModified || (TopoSortID != TOPOLOGICAL_FF)) {
	ret_code = RbfTopoCheck();

	if ((ret_code != KRERR_NO_ERROR) && (ret_code != KRERR_DEAD_UNITS))
	    return (ret_code);

	NetModified = FALSE;
    }
    if (NetInitialize || LearnFuncHasChanged) {
	fprintf(stderr, "Initialization RBF_Weights should be called!\n");
	/* initialize fields for momentum term */
	FOR_ALL_UNITS(unit_ptr) {
	    FOR_ALL_LINKS(unit_ptr, link_ptr) {
		link_ptr->value_a = 0.0;
	    }
	}
    }
    NET_ERROR(OutParameter) = 0.0;
    para_center = -LEARN_PARAM1(parameterInArray);
    para_bias = LEARN_PARAM2(parameterInArray);
    para_weight = LEARN_PARAM3(parameterInArray);
    para_momentum = LEARN_PARAM5(parameterInArray);
    para_delta_max = LEARN_PARAM4(parameterInArray);
    para_pain = 0.0;		/* not used now	*/

    /* set learn mask in condition of the learning parameters:	 */
    learn_mask = 0;
    if (para_center != 0.0)
	learn_mask |= RBF_LEARN_CENTER;
    if (para_bias != 0.0)
	learn_mask |= RBF_LEARN_BIAS;
    if (para_weight != 0.0)
	learn_mask |= RBF_LEARN_WEIGHT;
    if (para_pain != 0.0)
	learn_mask |= RBF_LEARN_PAIN;

#ifndef RBF_INCR_LEARNING
    ret_code = RbfLearnClean();
    if (ret_code != KRERR_NO_ERROR)
	return ret_code;
#endif


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);

    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	RbfLearnForward(pattern_no,sub_pat_no);	/* forward propagation	 */

	/* backward propagation 					 */

#ifdef RBF_LEARN_PROT
	temp_fehler = RbfLearnAdjustDelta(para_center,
			   para_bias, para_weight, para_pain, para_momentum,
					  para_delta_max, learn_mask);
	NET_ERROR(OutParameter) += temp_fehler;
	if (temp_fehler > 0.0)
	    fehler_zaehler++;
#else
	NET_ERROR(OutParameter) += RbfLearnAdjustDelta(para_center,
			   para_bias, para_weight, para_pain, para_momentum,
						para_delta_max, learn_mask);
#endif
    }

#ifndef RBF_INCR_LEARNING
    RbfLearnAdjustWeights(para_center, para_bias, para_weight,
			  para_momentum);
#endif

#ifdef RBF_LEARN_PROT
    protfile = fopen("rbf_learn_prot_file", "a");
    if (schritt == 1) {
	fprintf(protfile, "# Neues Lernprotokoll: \n");
    }
    fprintf(protfile, "%d %f %d\n", schritt, NET_ERROR(OutParameter),
	    fehler_zaehler);
    fclose(protfile);
    schritt++;
#endif

    return (ret_code);
}


/*****************************************************************************
 *****************************************************************************

  GROUP        : RPROP learning function

  AUTHOR       : Martin Riedmiller, ILKD, University of Karlsruhe
  Notes        : RPROP parameters are the initial update value (default 0.1) 
                 and the maximal update value (default 50.0). The defaults 
		 are assumed if the parameters are set to 0.0. It may be 
		 helpfull to limit the second paream to 0.01.

  DATE         : 01.04.1992
  LAST CHANGE  : 06.11.1993

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG
             
******************************************************************************
******************************************************************************/

#define RPROP_ETAPLUS 1.2
#define RPROP_ETAMINUS 0.5
#define RPROP_MINEPS 1e-6
#define RPROP_MAXEPS 50.0
#define RPROP_DEFAULT_UPDATE_VALUE 0.1


/*****************************************************************************
  FUNCTION : initializeRprop

  PURPOSE  : Rprop initialisation:
  RETURNS  : kernel error code
  NOTES    : ->value_c : Sum (dEdw)
             ->value_b : dw(t-1)
             ->value_a : update_value

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err initializeRprop(float update_val)
 /* initial update_value */
{
    register unsigned short flags;
    register struct Link *link_ptr;
    register struct Unit *unit_ptr;
    register struct Site *site_ptr;


    FOR_ALL_UNITS(unit_ptr) {
	flags = unit_ptr->flags;

	if ((flags & UFLAG_IN_USE) == UFLAG_IN_USE) {	/* unit is in use  */
	    unit_ptr->value_b = unit_ptr->value_c = (FlintType) 0;
	    unit_ptr->value_a = (FlintType) update_val;

	    if (flags & UFLAG_SITES) {	/* unit has sites  */
		FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {
		    link_ptr->value_b = link_ptr->value_c = (FlintType) 0;
		    link_ptr->value_a = (FlintType) update_val;
		}

	    } else {		/* unit has no sites   */
		if (flags & UFLAG_DLINKS) {	/* unit has direct links   */
		    FOR_ALL_LINKS(unit_ptr, link_ptr) {
			link_ptr->value_b = link_ptr->value_c = (FlintType) 0;
			link_ptr->value_a = (FlintType) update_val;
		    }
		}
	    }
	}
    }

    return (KRERR_NO_ERROR);
}



/*****************************************************************************
  FUNCTION : propagateNetBackwardRprop

  PURPOSE  : Pure Backpropagation of gradient without weight-update
  RETURNS  : network error
  NOTES    : sum(dE/dw) -> value_c.

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static float propagateNetBackwardRprop(int pattern_no, int sub_pat_no)
 /* number of actual pattern  */
{
    register struct Link *link_ptr;
    register struct Site *site_ptr;
    register struct Unit *unit_ptr;
    register Patterns out_pat;
    register float  error,	/* error  */
                    sum_error,	/* sum of the error  */
                    devit;	/* deviation  */
    TopoPtrArray    topo_ptr;
    int size;

    sum_error = 0.0;		/* reset network error  */

    /* calculate address of the output pattern (with number pattern_no + 1)  */
    out_pat = kr_getSubPatData(pattern_no,sub_pat_no,OUTPUT,&size);
    out_pat += size;

    /* add 3 to no_of_topo_units because the topologic array contains 4 NULL
       pointers  */
    topo_ptr = topo_ptr_array + (no_of_topo_units + 3);

    /* calculate output units only  */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	devit = *(--out_pat) - unit_ptr->Out.output;
	/* = o * (1.0 - o) in [0.0,0.25], */
	/* for asymmetric logistic function */

	sum_error += devit * devit;	/* sum up the error of the network  */

	/* calc. error for output units	 */
	error = devit * ((unit_ptr->act_deriv_func) (unit_ptr));

	unit_ptr->value_c += -error /* * 1 */ ;	/* calculate the bias slopes  */
	/* learn bias like a weight  */
	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {	/* the unit has direct links  */
	    FOR_ALL_LINKS(unit_ptr, link_ptr) {	/* calculate the slopes  */
		link_ptr->value_c += -error * link_ptr->to->Out.output;
		link_ptr->to->Aux.flint_no += link_ptr->weight * error;
	    }
	} else {		/* the unit has sites  */
	    FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {	
		/* calculate the value_cs  */
		link_ptr->value_c += -error * link_ptr->to->Out.output;
		link_ptr->to->Aux.flint_no += link_ptr->weight * error;
	    }
	}
    }


    /* calculate hidden units only  */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	error = ((unit_ptr->act_deriv_func) (unit_ptr)) * 
	         unit_ptr->Aux.flint_no;

	unit_ptr->value_c += -error /* * 1 */ ;	/* calculate the bias slopes  */
	/* learn bias like a weight  */
	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {	/* the unit has direct links  */
	    FOR_ALL_LINKS(unit_ptr, link_ptr) {	/* calculate the slopes  */
		if (link_ptr->to->flags & UFLAG_TTYP_HIDD)
		    /* this link points to a hidden unit: sum up the error's
		       from previos units  */
		    link_ptr->to->Aux.flint_no += link_ptr->weight * error;

		link_ptr->value_c += -error * link_ptr->to->Out.output;
	    }
	} else {		/* the unit has sites  */
	    FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {	
		/* calculate the slopes  */
		if (link_ptr->to->flags & UFLAG_TTYP_HIDD)
		    /* this link points to a hidden unit: sum up the error's
		       from previos units  */
		    link_ptr->to->Aux.flint_no += link_ptr->weight * error;

		link_ptr->value_c += -error * link_ptr->to->Out.output;
	    }
	}
    }

/*    sum_error *= 0.5; */
    return (sum_error);		/* return the error of the network  */
}


/*****************************************************************************
  FUNCTION : MODI_rprop

  PURPOSE  : modifies network after each epoch
  RETURNS  : 
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static void MODI_rprop(float maxeps)
{
    register struct Link *link_ptr;
    register struct Site *site_ptr;
    register struct Unit *unit_ptr;
    TopoPtrArray    topo_ptr;
    bool            hidden_units;
    float           direction;


    topo_ptr = topo_ptr_array + (NoOfInputUnits + 1);
    hidden_units = TRUE;

    /* calculate hidden and output units only  */
    do {
	if ((unit_ptr = *++topo_ptr) == NULL) {
	    if (!hidden_units)
		break;		/* end of topologic pointer array reached  */
	    unit_ptr = *++topo_ptr;	/* skip NULL pointer  */
	    hidden_units = FALSE;
	}
	if (IS_SPECIAL_UNIT(unit_ptr)) {	/* Do not change weights */
	    unit_ptr->value_c = 0.0;	/* reset */
	    if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {/*the unit has direct links*/
		FOR_ALL_LINKS(unit_ptr, link_ptr) {
		    link_ptr->value_c = 0.0;	/* reset */
		}
	    } else {		/* the unit has sites  */
		FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {
		    link_ptr->value_c = 0.0;	/* reset */
		}
	    }
	    continue;		/* next unit */
	}
	direction = unit_ptr->value_b * unit_ptr->value_c;
	if (direction < 0.0) {	/* same direction : dw * dEdw < 0  */
	    unit_ptr->value_a *= RPROP_ETAPLUS;	/* adapt update_value */
	    if (unit_ptr->value_a > maxeps)
		unit_ptr->value_a = maxeps;
	    if (unit_ptr->value_c < 0.0)
		unit_ptr->value_b = unit_ptr->value_a;
	    else
		unit_ptr->value_b = -(unit_ptr->value_a);
	} else if (direction > 0.0) {	/* direction changed  */
	    unit_ptr->bias -= unit_ptr->value_b;      /* revert previous step */
	    unit_ptr->value_b = 0;	/* reset for restarting adaptation in
					   next step */
	    unit_ptr->value_a *= RPROP_ETAMINUS;	/* adapt update_value */
	    if (unit_ptr->value_a < RPROP_MINEPS)
		unit_ptr->value_a = RPROP_MINEPS;
	} else {
	    /* start of RPROP learning process */
	    if (unit_ptr->value_c < 0.0)
		unit_ptr->value_b = unit_ptr->value_a;
	    else if (unit_ptr->value_c > 0.0)
		unit_ptr->value_b = -(unit_ptr->value_a);

	    /* else no action if  derivative was zero */

	}

	unit_ptr->bias += unit_ptr->value_b;	/* compute new bias */
	unit_ptr->value_c = 0.0;/* reset */

	/* adjust links */
	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {	/* the unit has direct links  */
	    FOR_ALL_LINKS(unit_ptr, link_ptr) {
		direction = link_ptr->value_b * link_ptr->value_c;
		if (direction < 0.0) {	/* same direction : dw * dEdw < 0  */
		    link_ptr->value_a *= RPROP_ETAPLUS;	/* adapt update_value */
		    if (link_ptr->value_a > maxeps)
			link_ptr->value_a = maxeps;
		    if (link_ptr->value_c < 0.0)
			link_ptr->value_b = link_ptr->value_a;
		    else
			link_ptr->value_b = -(link_ptr->value_a);
		} else if (direction > 0.0) {	/* direction changed  */
		    link_ptr->weight -= link_ptr->value_b; /* revert previous 
							      step */
		    link_ptr->value_b = 0;	/* reset for restarting
						   adaptation in next step */
		    link_ptr->value_a *= RPROP_ETAMINUS;/* adapt update_value */
		    if (link_ptr->value_a < RPROP_MINEPS)
			link_ptr->value_a = RPROP_MINEPS;
		} else {
		    /* start of RPROP learning process  */
		    if (link_ptr->value_c < 0.0)
			link_ptr->value_b = link_ptr->value_a;
		    else if (link_ptr->value_c > 0.0)
			link_ptr->value_b = -(link_ptr->value_a);

		    /* else no action if  derivative was zero */

		}

		link_ptr->weight += link_ptr->value_b;	/* compute new bias */
		link_ptr->value_c = 0.0;	        /* reset */


	    }
	} else {		/* the unit has sites  */
	    FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr) {
		if (direction < 0.0) {	/* same direction : dw * dEdw <! 0  */
		    link_ptr->value_a *= RPROP_ETAPLUS;	/* adapt update_value */
		    if (link_ptr->value_a > maxeps)
			link_ptr->value_a = maxeps;
		    if (link_ptr->value_c < 0.0)
			link_ptr->value_b = link_ptr->value_a;
		    else
			link_ptr->value_b = -(link_ptr->value_a);
		} else if (direction > 0.0) {	/* direction changed  */
		    link_ptr->weight -= link_ptr->value_b; /* revert previous 
							      step */
		    link_ptr->value_b = 0;	/* reset for restarting
						   adaptation in next step */
		    link_ptr->value_a *= RPROP_ETAMINUS;/* adapt update_value */
		    if (link_ptr->value_a < RPROP_MINEPS)
			link_ptr->value_a = RPROP_MINEPS;
		} else {
		    /* start of RPROP learning process */
		    if (link_ptr->value_c < 0.0)
			link_ptr->value_b = link_ptr->value_a;
		    else if (link_ptr->value_c > 0.0)
			link_ptr->value_b = -(link_ptr->value_a);

		    /* else no action if  derivative was zero */

		}

		link_ptr->weight += link_ptr->value_b;	/* compute new bias */
		link_ptr->value_c = 0.0;	/* reset */


	    }
	}
    }				/* for units  */
    while (TRUE);

}


/*****************************************************************************
  FUNCTION : LEARN_rprop

  PURPOSE  : RPROP learning function
  RETURNS  : kernel error code
  NOTES    : Input Parameters:   1 : initial update value
                                 2 : maxeps;
			         3 : blocksize;   (learning by block)

             Output Parameters:  1 : error of the network (sum of all cycles)

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err LEARN_rprop(int start_pattern, int end_pattern, 
		     float *parameterInArray, int NoOfInParams, 
		     float **parameterOutArray, int *NoOfOutParams)
{
    static float    OutParameter[1];	/* OutParameter[0] stores the */

    /* learning error */
    int             i, pattern_no, sub_pat_no, ret_code, blocksize;
    float           maxeps, update_value;



    if (NoOfUnits == 0)
	return (KRERR_NO_UNITS);/* No Units defined  */

    if (NoOfInParams < 3)
	return (KRERR_PARAMETERS);	/* not enough input parameters  */

    /* DEFAULTS: */

    if ((update_value = LEARN_PARAM1(parameterInArray)) == 0.0)
	update_value = RPROP_DEFAULT_UPDATE_VALUE;
    if ((maxeps = LEARN_PARAM2(parameterInArray)) == 0.0)
	maxeps = RPROP_MAXEPS;
    if (update_value > maxeps)
	update_value = maxeps;


    *NoOfOutParams = 1;		/* one return value */

    *parameterOutArray = OutParameter;	/* set output parameter reference  */
    ret_code = KRERR_NO_ERROR;	/* reset return code  */

    if (NetModified || (TopoSortID != TOPOLOGICAL_FF)) {       
	/* Net has been modified  or topologic array isn't initialized */
	/* check the topology of the network  */
	ret_code = kr_topoCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);	/* an error has occured  */
	if (ret_code < 2)
	    return (KRERR_NET_DEPTH);	/* the network has less then 2 layers */

	/* count the no. of I/O units and check the patterns  */
	ret_code = kr_IOCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);

	/* sort units by topology and by topologic type  */
	ret_code = kr_topoSort(TOPOLOGICAL_FF);
	if ((ret_code != KRERR_NO_ERROR) && (ret_code != KRERR_DEAD_UNITS))
	    return (ret_code);

	NetModified = FALSE;
    }
    if (NetInitialize || LearnFuncHasChanged) {	/* Net has been modified or
						   initialized, initialize
						   RPROP */
	ret_code = initializeRprop(update_value);
	if (ret_code != KRERR_NO_ERROR)
	    return (ret_code);
    }
    /* DEFAULTS: */
    if ((blocksize = LEARN_PARAM3(parameterInArray)) == 0)
	blocksize = end_pattern;


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,blocksize);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    NET_ERROR(OutParameter) = 0.0;	/* reset network error value  */

    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	propagateNetForward(pattern_no,sub_pat_no);   /* forward propagation  */

	/* backward propagation and summation of gradient  */
	NET_ERROR(OutParameter) 
	    += propagateNetBackwardRprop(pattern_no,sub_pat_no);
    }

    /* modificate links and bias  */
    MODI_rprop(maxeps);
    return (ret_code);
}



/*****************************************************************************
 *****************************************************************************

  GROUP        : ART 1 learning function

  AUTHOR       : Kai-Uwe Herrmann

  DATE         : 01.08.1992
  LAST CHANGE  : 06.11.1993

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG
             
******************************************************************************
******************************************************************************/



/*****************************************************************************
  FUNCTION : LEARN_ART1

  PURPOSE  : ART 1 learning function.
  RETURNS  : kernel error code
  NOTES    : 1 input-parameter  :  1. vigilance parameter RHO

             output-parameters  :  numbers of classified patterns,
                                   separator -1,
                                   numbers of not classifiable patterns

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err  LEARN_ART1(int start_pattern, int end_pattern,
		     float parameterInArray[], int NoOfInParams,
		     float **parameterOutArray, int *NoOfOutParams)
{
    krui_err        ret_code = KRERR_NO_ERROR;
    int             pattern_no, sub_pat_no;	/* Contains actual */
						/* pattern number */
    int             start, end;
    int             i,n;
    struct Unit    *winner_ptr;	/* recognition unit which is the winner of
				   w.t.a  */
    TopoPtrArray    topo_layer[6];	/* topo_layer[0] : *first input unit
					   topo_layer[1] : *first comp. unit
					   topo_layer[2] : *first rec.  unit
					   topo_layer[3] : *first delay unit
					   topo_layer[4] : *first local reset
					   unit topo_layer[5] : *first
					   special unit (classified_unit) */
    TopoPtrArray    topo_ptr;
    FlintType       beta;
    float           rho;


    /* Check number of incoming parameters */

    if (NoOfInParams < 1) {
	ret_code = KRERR_PARAMETERS;
	return (ret_code);
    }				/* if */
    /* rho is the vigilance parameter   */
    rho = parameterInArray[0];


    /* Check interval for vigilance parameter and constant value L */

    if ((rho < 0.0) || (rho > 1.0)) {
	ret_code = KRERR_PARAMETERS;
	return (ret_code);
    }				/* if */
    /* Check if network has been modified or learning func has been changed */
    if (NetModified || LearnFuncHasChanged || (TopoSortID != ART1_TOPO_TYPE)) {
	(void) kr_topoSort(ART1_TOPO_TYPE);
	ret_code = KernelErrorCode;
	if (ret_code != KRERR_NO_ERROR) {
	    NetModified = TRUE;
	    return (ret_code);
	}			/* if */
	NetModified = FALSE;
	LearnFuncHasChanged = FALSE;
    }				/* if */
    /* set initial activation values */
    ret_code = kra1_init_i_act(rho);

    if (ret_code != KRERR_NO_ERROR) {
	return (ret_code);
    }				/* if */
    /* beta is another learning parameter of the network which is determined
       when initializing the network. It is there written to the bias field
       of the structure of each unit. Now we will read this value. */
    beta = (unit_array + 1)->bias;

    if (beta <= 0.0) {
	topo_msg.error_code = KRERR_PARAM_BETA;
	topo_msg.src_error_unit = 0;
	topo_msg.dest_error_unit = 1;
	return (topo_msg.error_code);
    }				/* if */
    /* # of output parameters is 0  */
    *NoOfOutParams = 0;
    *parameterOutArray = NULL;


    /* get pointers to first elements of each layer in topo_ptr_array */

    topo_ptr = topo_ptr_array + 1;

    for (i = 0; i <= 5; i++) {
	topo_layer[i] = topo_ptr;
	do {
	} while (*topo_ptr++ != NULL);

    }				/* for */


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    /* Search phase */
    start = kr_AbsPosOfFirstSubPat(start_pattern);
    end   = kr_AbsPosOfFirstSubPat(end_pattern);
    end  += kr_NoOfSubPatPairs(end_pattern) - 1;

    for(n=start; n<=end; n++){

	kr_getSubPatternByNo(&pattern_no,&sub_pat_no,n);
    
	/* initialize the unit activations of the whole net */

	ret_code = krart_reset_activations();
	if (ret_code != KRERR_NO_ERROR) {
	    return (ret_code);
	}			/* if */
	/* put pattern into input units */
	ret_code = put_ART1_in_pattern(pattern_no, sub_pat_no, topo_layer[0]);
	if (ret_code != KRERR_NO_ERROR) {
	    return (ret_code);
	}			/* if */
	/* repeat synchronous propagation and look for winner until pattern
	   is classified or network tells us, that pattern is not
	   classifiable */
	do {
	    /* 1 propagation step (all units push their information onto
	       their output and calculate their new activation. */

	    krart_prop_synch();

	    /* look for the recognition unit with the highest activation
	       returns a NULL pointer if all recognition units have
	       activation 0.0 */
	    winner_ptr = krart_get_winner(topo_layer[2], 1.0);

	} while (!(ART1_CLASSIFIED) && !(ART1_NOT_CLASSIFIABLE));


	/* training phase */

	if (ART1_CLASSIFIED) {

	    /* Train network i.e. adjust weights between comparison layer and
	       winner_unit and vice versa */

	    ret_code = adjust_ART1_weights(beta, topo_layer[1],
					   topo_layer[3], winner_ptr);

	    if (ret_code != KRERR_NO_ERROR) {
		return (ret_code);
	    }/* if */
	}/* if */
    }/* for */


    return (ret_code);

}/* LEARN_ART1 */




/*****************************************************************************
  FUNCTION : put_ART1_in_pattern

  PURPOSE  : pushes a new pattern into the input units of the network
  RETURNS  : kernel error code
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err put_ART1_in_pattern(int pattern_no, int sub_pat_no, 
				    TopoPtrArray topo_inp_ptr)
{
    int             ret_code = KRERR_NO_ERROR;
    register Patterns in_pat;
    struct Unit    *unit_ptr;
    TopoPtrArray    topo_ptr = topo_inp_ptr;

    /* calculate startadress of actual pattern   */
    in_pat = kr_getSubPatData(pattern_no,sub_pat_no,INPUT,NULL);

    while ((unit_ptr = *topo_ptr++) != NULL) {
	if (unit_ptr->out_func == OUT_IDENTITY) {
	    unit_ptr->act = unit_ptr->Out.output = *in_pat++;
	} else {
	    unit_ptr->act = *in_pat++;
	    unit_ptr->Out.output = (*unit_ptr->out_func) (unit_ptr->act);
	}/* if */
    }/* while */

    return (ret_code);

}/* put_ART1_in_pattern */



/*****************************************************************************
  FUNCTION : adjust_ART1_weights

  PURPOSE  : training function for ART1 networks
  RETURNS  : kernel error code
  NOTES    : Parameters:  
             beta         constant value beta > 0.0

             comp_ptr     points to pointer to first comparison unit

             delay_ptr    points to pointer to first unit in the delay layer. 
	                  The t(j,i) links are not between recognition layer 
			  and comparison layer but between the respective 
			  delay unit of the recogniton unit and the comparison 
			  layer. So first we have to look for the corresponding
			  delay unit of the winning unit before training these 
			  weights.

             winner_ptr   points to winning unit of the recognition layer.

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err adjust_ART1_weights(double beta, TopoPtrArray comp_ptr,
				    TopoPtrArray delay_ptr,
				    struct Unit * winner_ptr)
{
    krui_err        ret_code = KRERR_NO_ERROR;
    TopoPtrArray    topo_ptr = NULL;
    struct Unit    *unit_ptr_comp = NULL, *unit_ptr_delay = NULL;
    struct Link    *link_ptr = NULL;
    bool            found_delay_unit = FALSE;
    FlintType       sum_ck = 0.0;


    /* get corresponding unit of the winning unit in the delay layer */

    topo_ptr = delay_ptr;

    while ((!found_delay_unit) && (*topo_ptr != NULL)) {

	unit_ptr_delay = *topo_ptr++;

	if (((struct Link *) unit_ptr_delay->sites)->to == winner_ptr) {
	    found_delay_unit = TRUE;
	}/* if */
    }/* while */

    if (!found_delay_unit) {

	/* There was no delay unit found corresponding to the winning
	   recognition unit */

	ret_code = KRERR_TOPOLOGY;
	return (ret_code);

    }/* if */
    /* Adjust weights between winning unit (delay-layer) and comparison layer
       (t(j,i) link values)
    
    t(j,i) = c(i)   where j is the number of the winning neuron in the delay
       layer and i ist the number of a comparison unit. */
    topo_ptr = comp_ptr;

    while ((unit_ptr_comp = *topo_ptr++) != NULL) {

	sum_ck += unit_ptr_comp->act;	/* sum up activatons of comparison
					   layer. sum_ck is needed for b(i,j) */

	FOR_ALL_LINKS(unit_ptr_comp, link_ptr) {

	    if (link_ptr->to == unit_ptr_delay) {
		link_ptr->weight = ART1_ADJUST_LINK_DEL_CMP(unit_ptr_comp);
	    }/* if */
	}/* FOR_ALL_LINKS */

    }/* while */


    /* Adjust weights between comparison layer and winning unit (recognition
       layer) (b(i,j) link values)
    
       b(i,j) = c(i) / (beta + sum(k)(c(k)))
    
       where j is the number of the winning neuron in the recognition layer, i
       ist the number of a comparison unit and k runs over all comparison
       units. (sum(k)(c(k))) = sum_ck. 
    */


    FOR_ALL_LINKS(winner_ptr, link_ptr) {

	if (link_ptr->to->lln == ART1_CMP_LAY) {
	    link_ptr->weight = (FlintType)ART1_ADJUST_LINK_CMP_REC(link_ptr->to,
								   beta,sum_ck);
	}/* if */
    }/* FOR_ALL_LINKS */


    return (ret_code);
}/* adjust_ART1_weights () */


/*****************************************************************************
 *****************************************************************************

  GROUP        : ART2 learning function

  AUTHOR       : Kai-Uwe Herrmann

  DATE         : 01.08.1992
  LAST CHANGE  : 06.11.1993

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG
             
******************************************************************************
******************************************************************************/


/*****************************************************************************
  FUNCTION : LEARN_ART2

  PURPOSE  : ART2 learning function.
  RETURNS  : kernel error code
  NOTES    : Parameters: 
                    6 input-parameter  :  1. vigilance parameter RHO
                                          2. Parameter a
                                          3. Parameter b
                                          4. Parameter c
                                          5. Parameter e
                                          6. Parameter THETA

                    output-parameters  :  none


  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err LEARN_ART2(int start_pattern, int end_pattern,
		    float parameterInArray[], int NoOfInParams,
		    float **parameterOutArray, int *NoOfOutParams)
{
    krui_err        ret_code = KRERR_NO_ERROR;
    int             pattern_no, sub_pat_no; /* Contains actual pattern number */
    int             i,n;
    int             start, end;
    struct Unit    *winner_ptr;	/* recognition unit which is the winner of
				   w.t.a */
    TopoPtrArray    topo_layer[12];	/* topo_layer[0] : *first input unit
					   topo_layer[1] : *first w unit
					   topo_layer[2] : *first x unit
					   topo_layer[3] : *first u unit
					   topo_layer[4] : *first v unit
					   topo_layer[5] : *first p unit
					   topo_layer[6] : *first q unit
					   topo_layer[7] : *first r unit
					   topo_layer[8] : *first rec.  unit
					   topo_layer[10] : *first local
					   reset unit */
    TopoPtrArray    topo_ptr;
    FlintType       rho, param_a, param_b, param_c, param_d, theta;


    /* Check number of incoming parameters */

    if (NoOfInParams < 5) {
	ret_code = KRERR_PARAMETERS;
	return (ret_code);
    }/* if */
    rho = parameterInArray[0];
    param_a = parameterInArray[1];
    param_b = parameterInArray[2];
    param_c = parameterInArray[3];
    theta = parameterInArray[4];


    /* Check if network has been modified or learning func has been changed */

    if (NetModified || LearnFuncHasChanged || (TopoSortID != ART2_TOPO_TYPE)) {
	(void) kr_topoSort(ART2_TOPO_TYPE);
	ret_code = KernelErrorCode;
	if (ret_code != KRERR_NO_ERROR) {
	    NetModified = TRUE;
	    return (ret_code);
	}/* if */
	NetModified = FALSE;
	LearnFuncHasChanged = FALSE;
    }/* if */
    /* Read out value of parameter d from bias field of any unit. The value
       has been written into the bias field by the init-function */
    param_d = (*(topo_ptr_array + 1))->bias;


    /* Check values of the parameters */

    if ((rho < 0.0) || (rho > 1.0) ||
	(param_a <= 0.0) || (param_b <= 0.0) ||
	((param_c * param_d) / (1 - param_d) > 1.0) ||
	(theta < 0.0) || (theta > 1.0)
	) {
	ret_code = KRERR_PARAMETERS;
	return (ret_code);
    }/* if */
    ret_code = kra2_set_params(rho, param_a, param_b, param_c, param_d, theta);

    if (ret_code != KRERR_NO_ERROR) {
	return (ret_code);
    }/* if */
    ret_code = kra2_init_propagate();

    if (ret_code != KRERR_NO_ERROR) {
	return (ret_code);
    }/* if */
    /* get pointers to first elements of each layer in topo_ptr_array */
    topo_ptr = topo_ptr_array + 1;

    for (i = 0; i <= 9; i++) {
	topo_layer[i] = topo_ptr;
	do {
	} while (*topo_ptr++ != NULL);

    }/* for */


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    /* Search phase */
    start = kr_AbsPosOfFirstSubPat(start_pattern);
    end   = kr_AbsPosOfFirstSubPat(end_pattern);
    end  += kr_NoOfSubPatPairs(end_pattern) - 1;

    for(n=start; n<=end; n++){

	kr_getSubPatternByNo(&pattern_no,&sub_pat_no,n);

	/* initialize the unit activations of the whole net */

	ret_code = krart_reset_activations();
	if (ret_code != KRERR_NO_ERROR) {
	    return (ret_code);
	}/* if */
	/* put pattern into input units */
	ret_code = put_ART2_in_pattern(pattern_no, sub_pat_no,
				       topo_layer[ART2_INP_LAY-1]);
	if (ret_code != KRERR_NO_ERROR) {
	    return (ret_code);
	}/* if */

	/* initialize of ART2 Simulator for new pattern */
	kra2_init_pattern();

	/* repeat synchronous propagation and look for winner until pattern
	   is classified or network tells us, that pattern is not
	   classifiable */

	do {

	    /* compute vector norms */
	    kra2_compute_norms();

	    /* save old activation values of f1-units */
	    kra2_save_for_stability_check();

	    /* 1 propagation step (all units push their information onto
	       their output and calculate their new activation. */
	    krart_prop_synch();

	    /* look for the recognition unit with the highest activation
	       returns a NULL pointer if all recognition units have
	       activation 0.0 */
	    winner_ptr = krart_get_winner(topo_layer[ART2_REC_LAY-1], param_d);

	    /* Check if F1-Layer is stable */
	    kra2_check_f1_stability();

	    /* Check Reset */
	    kra2_checkReset();

	} while (!(ART2_CLASSIFIED) && !(ART2_NOT_CLASSIFIABLE));


	/* training phase */

	if (ART2_CLASSIFIED) {

	    /* Train network i.e. adjust weights between comparison layer and
	       winner_unit and vice versa */
	    ret_code = adjust_ART2_weights(param_d, topo_layer[ART2_P_LAY - 1],
					   winner_ptr);

	    if (ret_code != KRERR_NO_ERROR) {
		return (ret_code);
	    }/* if */
	}/* if */
    }/* for */

    return (ret_code);

}/* LEARN_ART2 */



/*****************************************************************************
  FUNCTION : krui_err put_ART2_in_pattern

  PURPOSE  : pushes a new pattern into the input units of the network
  RETURNS  : kernel error code
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err put_ART2_in_pattern(int pattern_no, int sub_pat_no,
				    TopoPtrArray topo_inp_ptr)
{
    int               ret_code = KRERR_NO_ERROR;
    register Patterns in_pat;
    struct Unit       *unit_ptr;
    TopoPtrArray      topo_ptr = topo_inp_ptr;

    /* calculate startadress of actual pattern */
    in_pat = kr_getSubPatData(pattern_no,sub_pat_no,INPUT,NULL);

    while ((unit_ptr = *topo_ptr++) != NULL) {

	if (unit_ptr->out_func == OUT_IDENTITY) {
	    unit_ptr->act = unit_ptr->Out.output = *in_pat++;
	} else {
	    unit_ptr->act = *in_pat++;
	    unit_ptr->Out.output = (*unit_ptr->out_func) (unit_ptr->act);
	}/* if */

    }/* while */

    return (ret_code);

}/* put_ART2_in_pattern */



/*****************************************************************************
  FUNCTION : adjust_ART2_weights

  PURPOSE  : training function for ART2 networks
  RETURNS  : kernel error code
  NOTES    : Parameters:  
             param_d     constant value 0 < param_d < 1

             p_ptr       points to pointer to first comparison unit

             delay_ptr   points to pointer to first unit in the delay layer. 
	                 The z(j,i) links are not between recognition layer 
			 and comparison layer but between the respective delay 
			 unit of the recogniton unit and the comparison layer.
			 So first we have to look for the corresponding delay 
			 unit of the winning unit before training these weights.

             winner_ptr  points to winning unit of the recognition layer.

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err adjust_ART2_weights(double param_d, TopoPtrArray p_ptr,
				    struct Unit * winner_ptr)
{
    krui_err        ret_code = KRERR_NO_ERROR;
    TopoPtrArray    topo_ptr = NULL;
    struct Unit    *unit_ptr_p = NULL;
    struct Link    *link_ptr = NULL, *link_ptr_u = NULL;

    /* Adjust weights between winning unit and p layer (z(J,i) link values)
    
       (d/dt) z(J,i) = z(J,i) + d * (1-d) * [ u(i)/(1-d) - z(J,i) ]
    
       for (d/dt) -> 0:        z(J,i) = u(i)/(1-d) 
    */

    topo_ptr = p_ptr;

    while ((unit_ptr_p = *topo_ptr++) != NULL) {
	FOR_ALL_LINKS(unit_ptr_p, link_ptr) {
	    if (link_ptr->to == winner_ptr) {

		/* lookin' for corresponding u unit */
		FOR_ALL_LINKS(unit_ptr_p, link_ptr_u) {
		    if (link_ptr_u->to->lln == ART2_U_LAY) {
			link_ptr->weight =
			    ART2_ADJUST_LINK_REC_P(link_ptr_u->to, param_d);
			break;
		    }/* if */
		}/* FOR_ALL_LINKS */

	    }/* if */
	}/* FOR_ALL_LINKS */
    }/* while */


    /* Adjust weights between p layer and winning unit (recognition layer)
       (z(i,j) link values)
    
       (d/dt) z(i,J) = d * (1-d) * [ u(i)/(1-d) - z(i,J) ]
    
       where J is the number of the winning neuron in the recognition layer, i
       ist the number of a p unit
    
       for (d/dt) -> 0:   z(i,J) = u(i)/(1-d)
    
    */


    FOR_ALL_LINKS(winner_ptr, link_ptr) {
	if (link_ptr->to->lln == ART2_P_LAY) {

	    /* lookin' for corresponding u unit */
	    FOR_ALL_LINKS(link_ptr->to, link_ptr_u) {
		if (link_ptr_u->to->lln == ART2_U_LAY) {
		    link_ptr->weight =
			ART2_ADJUST_LINK_P_REC(link_ptr_u->to, param_d);

		    break;
		}/* if */
	    }/* FOR_ALL_LINKS */
	}/* if */
    }/* FOR_ALL_LINKS */


    return (ret_code);

}/* adjust_ART2_weights () */



/*****************************************************************************
 *****************************************************************************

  GROUP        : ARTMAP learning function

  AUTHOR       : Kai-Uwe Herrmann

  DATE         : 01.08.1992
  LAST CHANGE  : 06.11.1993

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG
             
******************************************************************************
******************************************************************************/



/*****************************************************************************
  FUNCTION : LEARN_ARTMAP

  PURPOSE  : ARTMAP learning function.
  RETURNS  : kernel error code
  NOTES    : Parameters:      
                    3 input-parameter  :  1. vigilance parameter RHOa
                                          2. vigilance parameter RHOb
                                          3. vigilance parameter RHO

                    output-parameters  :  none

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err LEARN_ARTMAP(int start_pattern, int end_pattern,
		      float parameterInArray[], int NoOfInParams,
		      float **parameterOutArray, int *NoOfOutParams)
{
    krui_err        ret_code = KRERR_NO_ERROR;
    int             pattern_no, sub_pat_no; /* Contains actual pattern number */
    int             i,n;
    struct Unit    *winner_ptr_a;	/* recognition unit which is the
					   winner of w.t.a ARTa */
    struct Unit    *winner_ptr_b;	/* recognition unit which is the
					   winner of w.t.a ARTb */
    struct Unit    *unit_ptr;
    TopoPtrArray    topo_layer[14];	/* topo_layer[0] : *first input unit
					   ARTa topo_layer[1] : *first comp.
					   unit ARTa topo_layer[2] : *first
					   rec.  unit ARTa topo_layer[3] :
					   *first delay unit ARTa
					   topo_layer[4] : *first local reset
					   unit ARTa topo_layer[5] : *first
					   special unit ARTa
					   (classified_unit) topo_layer[6] :
					   *first input unit ARTb
					   topo_layer[7] : *first comp. unit
					   ARTb topo_layer[8] : *first rec.
					   unit ARTb topo_layer[9] : *first
					   delay unit ARTb topo_layer[10]:
					   *first local reset unit ARTb
					   topo_layer[11]: *first special
					   unit ARTb (classified_unit)
					   topo_layer[12]: *first map unit
					   topo_layer[13]: *first special map
					   unit */

    TopoPtrArray    topo_ptr;
    FlintType       beta_a;
    FlintType       beta_b;
    float           rho_a;
    float           rho_b;
    float           rho;
    int             start, end;


    /* Check number of incoming parameters */

    if (NoOfInParams < 3) {
	ret_code = KRERR_PARAMETERS;
	return (ret_code);
    }/* if */
    /* rho is the vigilance parameter */
    rho_a = parameterInArray[0];
    rho_b = parameterInArray[1];
    rho = parameterInArray[2];


    /* Check interval in which vigilance parameter and constant value L have
       to be */

    if ((rho_a < 0.0) || (rho_a > 1.0) || (rho_b < 0.0) ||
	(rho_b > 1.0) || (rho < 0.0) || (rho > 1.0)
	) {
	ret_code = KRERR_PARAMETERS;
	return (ret_code);
    }/* if */
    /* Check if network has been modified or learning func has been changed */
    if (NetModified || LearnFuncHasChanged || (TopoSortID != ARTMAP_TOPO_TYPE)){
	(void) kr_topoSort(ARTMAP_TOPO_TYPE);
	ret_code = KernelErrorCode;
	if (ret_code != KRERR_NO_ERROR) {
	    NetModified = TRUE;
	    return (ret_code);
	}/* if */
	NetModified = FALSE;
	LearnFuncHasChanged = FALSE;
    }/* if */

    /* set initial activation values */
    ret_code = kram_init_i_act(rho_a, rho_b, rho);

    if (ret_code != KRERR_NO_ERROR) {
	return (ret_code);
    }/* if */
    /* beta_a, beta_b are other learning parameters of the network which are
       determined when initializing the network. They are there written to
       the bias field of the structure of each unit of the corresponding ART
       1 network. Now we will read these values. */

    /* find an ARTa unit and get ARTa beta value */
    for (unit_ptr=unit_array+1; unit_ptr->lln != ARTMAP_INPa_LAY; unit_ptr++);
    beta_a = unit_ptr->bias;

    /* find an ARTb unit and get ARTb beta value */
    for (unit_ptr=unit_array+1; unit_ptr->lln != ARTMAP_INPb_LAY; unit_ptr++);
    beta_b = unit_ptr->bias;

    if ((beta_a <= 0.0) || (beta_b <= 0.0)) {
	topo_msg.error_code = KRERR_PARAM_BETA;
	topo_msg.src_error_unit = 0;
	topo_msg.dest_error_unit = 1;
	return (topo_msg.error_code);
    }/* if */

    /* # of output parameters is 0 */
    *NoOfOutParams = 0;
    *parameterOutArray = NULL;


    /* get pointers to first elements of each layer in topo_ptr_array */

    topo_ptr = topo_ptr_array + 1;

    for (i = 0; i <= 13; i++) {
	topo_layer[i] = topo_ptr;
	do {
	} while (*topo_ptr++ != NULL);

    }/* for */


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    /* Search phase */
    start = kr_AbsPosOfFirstSubPat(start_pattern);
    end   = kr_AbsPosOfFirstSubPat(end_pattern);
    end  += kr_NoOfSubPatPairs(end_pattern) - 1;

    for(n=start; n<=end; n++){

	kr_getSubPatternByNo(&pattern_no,&sub_pat_no,n);

	/* initialize the unit activations of the whole net */
	ret_code = krart_reset_activations();
	if (ret_code != KRERR_NO_ERROR) {
	    return (ret_code);
	}/* if */

	/* put pattern into input units */
	ret_code = 
	    put_ARTMAP_in_pattern(pattern_no, sub_pat_no, topo_layer[0], 
				  topo_layer[6]);
	if (ret_code != KRERR_NO_ERROR) {
	    return (ret_code);
	}/* if */
	/* repeat synchronous propagation and look for winner until pattern
	   is classified or network tells us, that pattern is not
	   classifiable */
	do {

	    /* 1 propagation step (all units push their information onto
	       their output and calculate their new activation. */

	    krart_prop_synch();

	    /* look for the recognition unit with the highest activation
	       returns a NULL pointer if all recognition units have
	       activation 0.0 */
	    winner_ptr_a = krart_get_winner(topo_layer[2], 1.0);
	    winner_ptr_b = krart_get_winner(topo_layer[8], 1.0);

	} while (!(ARTMAP_CLASSIFIED) && !(ARTMAP_NOT_CLASSIFIABLE));


	/* training phase */

	if (ARTMAP_CLASSIFIED) {

	    /* Train network i.e. adjust weights between comparison layer and
	       winner_unit and vice versa of both, ARTa and ARTb. Further
	       adjust weights between ARTa delay and map field layer. */

	    ret_code = adjust_ARTMAP_weights(beta_a, beta_b,
					     topo_layer[1], topo_layer[7],
					     topo_layer[3], topo_layer[9],
					     topo_layer[12],
					     winner_ptr_a, winner_ptr_b);

	    if (ret_code != KRERR_NO_ERROR) {
		return (ret_code);
	    }/* if */
	} else {

	    /* we're doing nothing */

	}/* if */

    }/* for */

    return (ret_code);

}/* LEARN_ARTMAP */



/*****************************************************************************
  FUNCTION : put_ARTMAP_in_pattern

  PURPOSE  : pushes a new pattern into the input units of the network
  RETURNS  : kernel error code
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err put_ARTMAP_in_pattern(int pattern_no, int sub_pat_no,
				      TopoPtrArray topo_inpa_ptr,
				      TopoPtrArray topo_inpb_ptr)
{
    int               ret_code = KRERR_NO_ERROR;
    register Patterns in_pat;
    struct Unit       *unit_ptr;
    TopoPtrArray      topo_ptr_a = topo_inpa_ptr;
    TopoPtrArray      topo_ptr_b = topo_inpb_ptr;


    /* calculate startadress of actual pattern */
    in_pat = kr_getSubPatData(pattern_no,sub_pat_no,INPUT,NULL);

    while ((unit_ptr = *topo_ptr_a++) != NULL) {
	if (unit_ptr->out_func == OUT_IDENTITY) {
	    unit_ptr->act = unit_ptr->Out.output = *in_pat++;
	} else {
	    unit_ptr->act = *in_pat++;
	    unit_ptr->Out.output = (*unit_ptr->out_func) (unit_ptr->act);
	}/* if */
    }/* while */

    while ((unit_ptr = *topo_ptr_b++) != NULL) {
	if (unit_ptr->out_func == OUT_IDENTITY) {
	    unit_ptr->act = unit_ptr->Out.output = *in_pat++;
	} else {
	    unit_ptr->act = *in_pat++;
	    unit_ptr->Out.output = (*unit_ptr->out_func) (unit_ptr->act);
	}/* if */
    }/* while */


    return (ret_code);

}/* put_ARTMAP_in_pattern */




/*****************************************************************************
  FUNCTION : adjust_ARTMAP_weights

  PURPOSE  : training function for ARTMAP networks
  RETURNS  : kernel error code
  NOTES    : Parameters:  
             beta_a         constant value beta of ARTa > 0.0
             beta_b         constant value beta of ARTb > 0.0

             compa_ptr      points to pointer to 1st comparison unit of ARTa
             compb_ptr      points to pointer to 1st comparison unit of ARTb

             dela_ptr       points to pointer to first unit in the delay layer.
	                    The t(j,i) links are not between recognition layer
			    and comparison layer but between the respective 
			    delay unit of the recogniton unit and the 
			    comparison layer. So first we have to look for the 
			    corresponding delay unit of the winning unit before
			    training these weights.
             delb_ptr       points to pointer to first unit in the delay layer.
	                    The t(j,i) links are not between recognition layer 
			    and comparison layer but between the respective 
			    delay unit of the recogniton unit and the 
			    comparison layer. So first we have to look for the 
			    corresponding delay unit of the winning unit before
			    training these weights.

             map_ptr        points to pointer to first unit in the map layer

             winner_ptr_a   points to winning unit of the recognition layer of 
	                    ARTa.
             winner_ptr_b   points to winning unit of the recognition layer of 
	                    ARTb.


  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err adjust_ARTMAP_weights(double beta_a, double beta_b,
				      TopoPtrArray compa_ptr,
				      TopoPtrArray compb_ptr,
				      TopoPtrArray dela_ptr,
				      TopoPtrArray delb_ptr,
				      TopoPtrArray map_ptr,
				      struct Unit * winner_ptr_a,
				      struct Unit * winner_ptr_b)
{
    krui_err        ret_code = KRERR_NO_ERROR;
    TopoPtrArray    topo_ptr = NULL;
    struct Unit    *unit_ptr_compa = NULL, *unit_ptr_compb = NULL, 
                   *unit_ptr_dela = NULL, *unit_ptr_delb = NULL, 
                   *unit_ptr_map = NULL;

    struct Link    *link_ptr = NULL;

    bool            found_dela_unit = FALSE;
    bool            found_delb_unit = FALSE;

    FlintType       sum_ck = 0.0;


    if ((winner_ptr_a == NULL) || (winner_ptr_b == NULL)) {
	/* We are using ARTMAP in a non-learning mode, wo we are not allowed
	   to adjust weights now. Weights may just be adjusted, if we have an
	   input in ARTa and ARTb each of which brings out a winner in the
	   respective F2-Layer */
	return (ret_code);
    }/* if */
    /* get corresponding unit of the winning unit of ARTa in the delay layer */
    topo_ptr = dela_ptr;

    while ((!found_dela_unit) && (*topo_ptr != NULL)) {
	unit_ptr_dela = *topo_ptr++;
	FOR_ALL_LINKS(unit_ptr_dela, link_ptr) {
	    if (link_ptr->to == winner_ptr_a) {
		found_dela_unit = TRUE;
	    }/* if */
	}/* FOR_ALL_LINKS */
    }/* while */

    /* get corresponding unit of the winning unit of ARTb in the delay layer */

    topo_ptr = delb_ptr;
    while ((!found_delb_unit) && (*topo_ptr != NULL)) {
	unit_ptr_delb = *topo_ptr++;
	FOR_ALL_LINKS(unit_ptr_delb, link_ptr) {
	    if (link_ptr->to == winner_ptr_b) {
		found_delb_unit = TRUE;
		break;
	    }/* if */
	}/* FOR_ALL_LINKS */
    }/* while */

    if ((!found_dela_unit) || (!found_delb_unit)) {

	/* There was no delay unit found corresponding to the winning
	   recognition unit in ARTa or ARTb */

	ret_code = KRERR_TOPOLOGY;
	return (ret_code);

    }/* if */
    /********* ADJUST WEIGHTS *********/
    /* Adjust weights between winning unit (delay-layer) and comparison layer
       (t(j,i) link values) -> ARTa
    
       t(j,i) = c(i)   where j is the number of the winning neuron in the delay
       layer and i ist the number of a comparison unit. 
    */

    topo_ptr = compa_ptr;
    while ((unit_ptr_compa = *topo_ptr++) != NULL) {
	sum_ck += unit_ptr_compa->act;	/* sum up activatons of comparison
					   layer. sum_ck is needed for b(i,j) */
	FOR_ALL_LINKS(unit_ptr_compa, link_ptr) {
	    if (link_ptr->to == unit_ptr_dela) {
		link_ptr->weight = ART1_ADJUST_LINK_DEL_CMP(unit_ptr_compa);
	    }/* if */
	}/* FOR_ALL_LINKS */
    }/* while */

    /* Adjust weights between comparison layer and winning unit (recognition
       layer) -> ARTa
    
       b(i,j) = c(i) / (beta + sum(k)(c(k)))
    
       where j is the number of the winning neuron in the recognition layer, i
       ist the number of a comparison unit and k runs over all comparison
       units. (sum(k)(c(k))) = sum_ck. 
    */

    FOR_ALL_LINKS(winner_ptr_a, link_ptr) {
	if (link_ptr->to->lln == ARTMAP_CMPa_LAY) {
	    link_ptr->weight = (FlintType)ART1_ADJUST_LINK_CMP_REC(link_ptr->to,
								   beta_a,
								   sum_ck);
	}/* if */
    }/* FOR_ALL_LINKS */


    /* Adjust weights between winning unit (delay-layer) and comparison layer
       (t(j,i) link values) -> ARTb
    
       t(j,i) = c(i)   where j is the number of the winning neuron in the delay
       layer and i ist the number of a comparison unit. 
    */

    topo_ptr = compb_ptr;
    sum_ck = 0.0;
    while ((unit_ptr_compb = *topo_ptr++) != NULL) {
	sum_ck += unit_ptr_compb->act;	/* sum up activatons of comparison
					   layer. sum_ck is needed for b(i,j) */
	FOR_ALL_LINKS(unit_ptr_compb, link_ptr) {
	    if (link_ptr->to == unit_ptr_delb) {
		link_ptr->weight = ART1_ADJUST_LINK_DEL_CMP(unit_ptr_compb);
	    }/* if */
	}/* FOR_ALL_LINKS */
    }/* while */

    /* Adjust weights between comparison layer and winning unit (recognition
       layer) (b(i,j) link values)
    
       b(i,j) = c(i) / (beta + sum(k)(c(k)))
    
       where j is the number of the winning neuron in the recognition layer, i
       ist the number of a comparison unit and k runs over all comparison
       units. (sum(k)(c(k))) = sum_ck. 
    */

    FOR_ALL_LINKS(winner_ptr_b, link_ptr) {
	if (link_ptr->to->lln == ARTMAP_CMPb_LAY) {
	    link_ptr->weight = (FlintType)ART1_ADJUST_LINK_CMP_REC(link_ptr->to,
								   beta_b,
								   sum_ck);
	}/* if */
    }/* FOR_ALL_LINKS */


    /* Adjust weights between delay units of ARTa and map units
    
       w(i,j) = map(j) where j is the number of a neuron in the map layer i is
       the number of the winning neuron in the dela layer 
    */

    topo_ptr = map_ptr;
    while ((unit_ptr_map = *topo_ptr++) != NULL) {
	FOR_ALL_LINKS(unit_ptr_map, link_ptr) {
	    if (link_ptr->to == unit_ptr_dela) {
		/* Same as adjustment between delay and comparison layer */
		link_ptr->weight = ART1_ADJUST_LINK_DEL_CMP(unit_ptr_map);
	    }/* if */
	}/* FOR_ALL_LINKS */
    }/* while */

    return (ret_code);

}/* adjust_ARTMAP_weights () */



/*****************************************************************************
 *****************************************************************************

  GROUP        : backpropagation through time learning functions

  AUTHOR       : Martin Reczko
  NOTES        : Implemented are Truncated backpropagation through time with 
                 online-update (BPTT), Truncated backpropagation through time 
                 with batch-update (BBPTT) and truncated quickprop through 
		 time (QPTT) learning functions

  DATE         : 01.08.1992
  LAST CHANGE  : 06.11.1993

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG
             
******************************************************************************
******************************************************************************/


/*****************************************************************************
  FUNCTION : BPTT_clear_deltaw

  PURPOSE  : BPTT weight change reset
  RETURNS  : kernel error code
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err BPTT_clear_deltaw(void)
{
    register struct Unit *unit_ptr;
    struct Link    *link_ptr;


    FOR_ALL_UNITS(unit_ptr) {

	/* reset old weight changes (_a), old gradients (_b) and gradient
	   accumulators (_c) */
	unit_ptr->value_a = 0.0;
	unit_ptr->value_b = 0.0;
	unit_ptr->value_c = 0.0;
	FOR_ALL_LINKS(unit_ptr, link_ptr) {
	    link_ptr->value_a = 0.0;
	    link_ptr->value_b = 0.0;
	    link_ptr->value_c = 0.0;
	}
    }
    return (KRERR_NO_ERROR);
}



/*****************************************************************************
  FUNCTION : initializeBPTT 

  PURPOSE  : BPTT network activity reset 
  RETURNS  : kernel error code
  NOTES    : BPTT data structures: unit:
             unit_ptr->olddelta : delta values, after finished calculation 
	                          for 1 time step
             unit_ptr->newdelta : accumulators for new delta values

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static krui_err initializeBPTT(void)
{
    register struct Unit *unit_ptr;
    int             i;

    FOR_ALL_UNITS(unit_ptr) {
	/* clear netact-copies */
	for (i = 0; i < MAX_BPTT_BACKSTEP; i++)
	    unit_ptr->actbuf[i] = 0.0;
    }
    return (KRERR_NO_ERROR);
}



/*****************************************************************************
  FUNCTION : BPTT_propagateNetForward

  PURPOSE  : topological forward propagation (backprop thru time)
  RETURNS  : 
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static void BPTT_propagateNetForward(int pattern_no, int sub_pat_no, int nhist)
{
    register struct Unit *unit_ptr;
    register Patterns in_pat;
    register TopoPtrArray topo_ptr;
    TopoPtrArray    first_hidden_ptr;
    int             i, done_hidden;
    int             all_zero_input = 1;	/* flag to reset net-copies */

    /* calculate startaddress for input pattern array  */
    in_pat = kr_getSubPatData(pattern_no,sub_pat_no,INPUT,NULL);

    topo_ptr = topo_ptr_array;

    /* ACTIVATE INPUT LAYER */
    /* copy pattern into input unit's activation and calculate output of the
       input units */
    /* topo_ptr points to a (topological sorted) unit stucture (input units
       first)  */
    while ((unit_ptr = *++topo_ptr) != NULL){

	/* apply input pattern */
	if (unit_ptr->out_func == OUT_IDENTITY)
	    /* there is no need to call the output function  */
	    unit_ptr->Out.output = unit_ptr->act = *in_pat++;
	else
	    /* no identity output function: calculate unit's output also  */
	    unit_ptr->Out.output = 
		(*unit_ptr->out_func) (unit_ptr->act = *in_pat++);
	if (fabs(unit_ptr->act) > 0.000001)
	    all_zero_input = 0;	/* no reset-input */

	/* BPTT: shift the actbuf for this input buffer one step back in time */
	for (i = nhist; i > 0; i--) {
	    unit_ptr->actbuf[i] = unit_ptr->actbuf[i - 1];
	}

	/* the new input pattern moves into the second time-layer with index 1,
	   since activations for this pattern are calculated in time-layer 0 */
	unit_ptr->actbuf[1] = unit_ptr->act;

    }

    /* An all-zero input pattern resets all network activities */
    if (all_zero_input) {
	initializeBPTT();    /* reset all netact-copies at start of sequences */
    }

    /* INPUT LAYER DONE */

    /* store first hidden unit pointer */
    first_hidden_ptr = topo_ptr;

    /* shift all actbufs for non-input units one step back in time, make most
       recent activity visible in unit_ptr->Out.output for subsequent calls
       to act_func */
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* hidden layer */
	for (i = nhist; i > 0; i--)
	    unit_ptr->actbuf[i] = unit_ptr->actbuf[i - 1];
	unit_ptr->Out.output = unit_ptr->actbuf[1];
    }

    while ((unit_ptr = *++topo_ptr) != NULL) {	/* output layer */
	for (i = nhist; i > 0; i--)
	    unit_ptr->actbuf[i] = unit_ptr->actbuf[i - 1];
	unit_ptr->Out.output = unit_ptr->actbuf[1];
    }


    /* calculate new activities for hidden and output units */
    /* point to first hidden unit */
    topo_ptr = first_hidden_ptr;
    done_hidden = 0;
    while (((unit_ptr = *++topo_ptr) != NULL) || (done_hidden == 0))
	if (unit_ptr == NULL) {
	    done_hidden = 1;
	} else {
	    /* calc actbuf[0] using actbuf[1], don't update Out.output while
	       updating units, wait until all units are processed  */
	    unit_ptr->act = (*unit_ptr->act_func) (unit_ptr);
	    unit_ptr->actbuf[0] = unit_ptr->act;
	}

    /* set Out.output */
    topo_ptr = first_hidden_ptr;
    done_hidden = 0;
    while (((unit_ptr = *++topo_ptr) != NULL) || (done_hidden == 0))
	if (unit_ptr == NULL) {
	    done_hidden = 1;
	} else {
	    if (unit_ptr->out_func == OUT_IDENTITY)
		/* identity output function: there is no need to call the
		   output function  */
		unit_ptr->Out.output = unit_ptr->act;
	    else
		/* no identity output function: calculate unit's output also  */
		unit_ptr->Out.output = (*unit_ptr->out_func) (unit_ptr->act);
	}
}


/*****************************************************************************
  FUNCTION : initOldDeltas

  PURPOSE  :
  RETURNS  : 
  NOTES    : BPTT starts at the first time-layer (actbuf[0]).
             The deltas for this layer are calculated for the output units by
	     comparison with the target values. All other deltas for hidden 
	     units are zero. The deltas are propagated to the second time-layer
	     (actbuf[1]) into oldelta

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static float initOldDeltas(int pattern_no, int sub_pat_no)
{
    register struct Link *link_ptr;
    register struct Unit *unit_ptr;
    register Patterns out_pat;
    register float  error, sum_error, devit, delta, tmp;
    register TopoPtrArray topo_ptr;
    TopoPtrArray    first_hidden_ptr;
    int             all_correct = 1;	/* flag, wether all bits in the
					   pattern are correct */
    int size;

    /* Initdelta, Step 1: clear all olddeltas (accumulate delta in olddelta) */

    topo_ptr = topo_ptr_array;
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* input units */
	unit_ptr->olddelta = 0.0;
    }

    /* store first hidden unit pointer */
    first_hidden_ptr = topo_ptr;
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* hidden units */
	unit_ptr->olddelta = 0.0;
    }

    while ((unit_ptr = *++topo_ptr) != NULL) {	/* output units */
	unit_ptr->olddelta = 0.0;
    }

    sum_error = 0.0;		/* reset network error  */

    /* calculate address of the output pattern (with number pattern_no + 1)  */
    out_pat = kr_getSubPatData(pattern_no,sub_pat_no,OUTPUT,&size);
    out_pat += size;


    /* last output unit: add 3 to no_of_topo_units because the topologic
       array contains 4 NULL pointers  */
    topo_ptr = topo_ptr_array + (no_of_topo_units + 3);

    /* LOOP FOR ALL OUTPUT UNITS */
    /* calculate olddelta for output units  */
    while ((unit_ptr = *--topo_ptr) != NULL) {
	tmp = unit_ptr->Out.output;
	devit = *(--out_pat);

	/* count correct bits using threshold of 0.5 */
	if (devit > 0.5) {
	    if (tmp > 0.5)
		NoOfLearnedPatterns++;
	    else
		all_correct = 0;
	} else {
	    if (tmp <= 0.5)
		NoOfLearnedPatterns++;
	    else
		all_correct = 0;
	}

	devit = devit - tmp;	/* calc. devitation (target_j - output_j) */
	error = devit * devit;
	sum_error += error;

	/* BPTT uses sum_j ( o_j - t_j )^2 as error function => -2.0 * ... */
	delta = -2.0 * devit * ((unit_ptr->act_deriv_func) (unit_ptr));

	/* Initdelta, Step 2: upstream propagation of gradients for backprop */
	FOR_ALL_LINKS(unit_ptr, link_ptr) {
	    tmp = delta * link_ptr->weight;
	    link_ptr->to->olddelta += tmp;	/* accumulate delta */
	    /* accumulate weight gradient */
	    link_ptr->value_c += link_ptr->to->actbuf[1] * delta;	
	}

	/* accumulate bias gradient */
	unit_ptr->value_c += delta;
    }/* output units done */

    /* Initdelta, Step 3:  clear newdelta */
    topo_ptr = topo_ptr_array;
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* input units */
	unit_ptr->newdelta = 0.0;
    }

    while ((unit_ptr = *++topo_ptr) != NULL) {	/* hidden units */
	unit_ptr->newdelta = 0.0;
    }

    while ((unit_ptr = *++topo_ptr) != NULL) {	/* output units */
	unit_ptr->newdelta = 0.0;
    }

    return (sum_error);
}


/*****************************************************************************
  FUNCTION : oneStepBackprop

  PURPOSE  : calc weight changes between consecutive time steps 
  RETURNS  : network error
  NOTES    : heart of BPTT

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static float oneStepBackprop(int backstep, int pattern_no, int sub_pat_no, 
			     int nhist)
{
    register struct Link *link_ptr;
    register struct Unit *unit_ptr;
    double          delta, sum_error;
    register TopoPtrArray topo_ptr;
    int             done_hidden, nextlayer;
    float           tmp;

    /* CHECK FOR START OF BACKPROP AT THE LAST TIME LAYER */
    if (backstep == 0) {
	sum_error = initOldDeltas(pattern_no,sub_pat_no);
	return (sum_error);	/* start case */
    } else			/* at least for time layer 0, old deltas are
				   known */
	sum_error = 0.0;

    /* index of next layer (used frequently!) */
    nextlayer = backstep + 1;

    /* point to seperator after last input unit */
    topo_ptr = topo_ptr_array;	/* + (NoOfInputUnits + 1); */
    while ((unit_ptr = *++topo_ptr) != NULL);
    done_hidden = 0;

    /* DO BACKPROP FOR ALL NON-INPUT-UNITS */
    while (((unit_ptr = *++topo_ptr) != NULL) || (done_hidden == 0))
	if (unit_ptr == NULL) {	/* skip NULL seperator between hidden and
				   output units */
	    done_hidden = 1;
	} else {		/* delta =  f'(net[backstep]) * olddelta */
	    /* copy actbuf[backstep] to act to enable call to act_deriv_func
	       (overhead: better definition of activation functions required) */
	    unit_ptr->act = unit_ptr->actbuf[backstep];
	    delta = ((unit_ptr->act_deriv_func)(unit_ptr)) * unit_ptr->olddelta;

	    /* propagate gradients upstream */
	    FOR_ALL_LINKS(unit_ptr, link_ptr) {
		tmp = delta * link_ptr->weight;
		link_ptr->to->newdelta += tmp;	/* accumulate delta */
		/* accumulate weight gradient */
		link_ptr->value_c += link_ptr->to->actbuf[nextlayer] * delta;
	    }

	    /* accumulate bias gradient */
	    unit_ptr->value_c += delta;
	}

    /* copy newdeltas to olddeltas, clear newdeltas */
    topo_ptr = topo_ptr_array;
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* input units */
	unit_ptr->olddelta = unit_ptr->newdelta;
	unit_ptr->newdelta = 0.0;
    }

    while ((unit_ptr = *++topo_ptr) != NULL) {	/* hidden units */
	unit_ptr->olddelta = unit_ptr->newdelta;
	unit_ptr->newdelta = 0.0;
    }

    while ((unit_ptr = *++topo_ptr) != NULL) {	/* output units */
	unit_ptr->olddelta = unit_ptr->newdelta;
	unit_ptr->newdelta = 0.0;
    }

    return (sum_error);
}



/*****************************************************************************
  FUNCTION : BPTTadapt 

  PURPOSE  : adapt all weights after BPTT using steepest descent with momentum
  RETURNS  : 
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static void BPTTadapt(float step_size, float bptt_momentum)
{
    register struct Link *link_ptr;
    register struct Unit *unit_ptr;
    register TopoPtrArray topo_ptr;
    int             done_hidden = 0;
    float           delta;

    /* point to seperator after last input unit */
    topo_ptr = topo_ptr_array + (NoOfInputUnits + 1);

    /* for each non-input unit: add weight changes to old weights */
    while (((unit_ptr = *++topo_ptr) != NULL) || (done_hidden == 0)) {
	if (unit_ptr == NULL) {
	    done_hidden = 1;
	} else {
	    delta = step_size * (-unit_ptr->value_c) + 
		bptt_momentum * unit_ptr->value_a;
	    if (!IS_SPECIAL_UNIT(unit_ptr))
		unit_ptr->bias += delta;
	    unit_ptr->value_a = delta;
	    unit_ptr->value_c = 0.0;
	    /* set act to last activity, since it was scrambled by bptt */
	    unit_ptr->act = unit_ptr->Out.output;
	    FOR_ALL_LINKS(unit_ptr, link_ptr) {
		delta = step_size * (-link_ptr->value_c) + 
		    bptt_momentum * link_ptr->value_a;
		link_ptr->value_a = delta;
		link_ptr->value_c = 0.0;
	    }
	    if (!IS_SPECIAL_UNIT(unit_ptr))
		FOR_ALL_LINKS(unit_ptr, link_ptr) {
		    link_ptr->weight += link_ptr->value_a;
		}
	}
    }
}



/*****************************************************************************
  FUNCTION : BPTT_propagateNetBackward

  PURPOSE  : BPTT-main: accumulate weight changes backward thru time
  RETURNS  : network error
  NOTES    :

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
static float BPTT_propagateNetBackward(int pattern_no, int sub_pat_no,int nhist)
{
    float           error = 0.0;
    float           dummy;
    int             backstep;

    /* go nhist steps back thru time */
    for (backstep = 0; backstep < nhist; backstep++)
	if (backstep == 0) {
	    /* start at output, pattern-error is calculated first */
	    error = oneStepBackprop(backstep, pattern_no, sub_pat_no, nhist);
	} else {
	    dummy = oneStepBackprop(backstep, pattern_no, sub_pat_no, nhist);
	}
    return (error);
}



/*****************************************************************************
  FUNCTION : LEARN_BPTT

  PURPOSE  : Backpropagation through time learning function
  RETURNS  : kernel error code
  NOTES    : Input Parameters:   1 : step_size
                                 2 : momentum
				 3 : nhist
             Output Parameters:  1 : error of the network (sum of all cycles)


  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err LEARN_BPTT(int start_pattern, int end_pattern, 
		    float *parameterInArray, int NoOfInParams, 
		    float **parameterOutArray, int *NoOfOutParams)
{
    static float    OutParameter[1];	/* OutParameter[0] stores the
					   learning error  */
    int             i, ret_code, pattern_no, sub_pat_no, patterns;
    int             nhist;	/* number of steps back in time */
    register struct Unit *unit_ptr;

    if (NoOfUnits == 0)
	return (KRERR_NO_UNITS);        /* No Units defined	 */
    if (NoOfInParams < 1)	        /* has to be ... snns habit ? */
	return (KRERR_PARAMETERS);	/* Not enough input parameters  */

    *NoOfOutParams = 1;		/* One return value is available (the
				   learning error)  */
    *parameterOutArray = OutParameter;	/* set the output parameter reference */
    ret_code = KRERR_NO_ERROR;	/* reset return code  */

    if (NetModified || (TopoSortID != TOPOLOGIC_TYPE)) {
	/* Net has been modified or topologic array isn't initialized */
	/* any connected topology allowed */
	/* count the no. of I/O units and check the patterns  */
	ret_code = kr_IOCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);

	/* sort units by ''topologic type'', criterion is visibility
	   (input,hidden,output), not topology */
	ret_code = kr_topoSort(TOPOLOGIC_TYPE);

	if ((ret_code != KRERR_NO_ERROR) && (ret_code != KRERR_DEAD_UNITS))
	    return (ret_code);
	/* sites are not supported, check absence */
	FOR_ALL_UNITS(unit_ptr)
	    if UNIT_HAS_SITES
	    (unit_ptr)
		return (KRERR_SITES_NO_SUPPORT);
	NetModified = FALSE;
    }
    if (NetInitialize || LearnFuncHasChanged) {	/* Net has been modified or
						   initialized, clear weight
						   changes */
	ret_code = BPTT_clear_deltaw();
	if (ret_code != KRERR_NO_ERROR)
	    return (ret_code);
    }
    NET_ERROR(OutParameter) = 0.0;	/* reset network error value  */

    NoOfLearnedPatterns = 0;	/* correct bits using threshold of 0.5 */
    nhist = LEARN_PARAM3(parameterInArray);
    if (nhist > MAX_BPTT_BACKSTEP)
	return (KRERR_NET_DEPTH);	/* actbuf and learning functions
					   support only MAX_BPTT_BACKSTEP net
					   copies */

    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);

    patterns = 0;
    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	/* FORWARD-BPTT */
	/* 1st parameter is the pattern number 2nd parameter is the number of
	   steps back in time */
	BPTT_propagateNetForward(pattern_no,sub_pat_no,nhist); /*Forward pass */

	/* Backward propagation  */
	NET_ERROR(OutParameter) 
	    += BPTT_propagateNetBackward(pattern_no, sub_pat_no, nhist);

	/* online version: adapt net after each pattern has been
	   backpropagated through time and weight changes have accumulated
	   through time */
	BPTTadapt(LEARN_PARAM1(parameterInArray), 
		  LEARN_PARAM2(parameterInArray));

	patterns++;
    }
    return (ret_code);
}



/*****************************************************************************
  FUNCTION : LEARN_BBPTT

  PURPOSE  : Batch backpropagation through time learning function (BBPTT)
  RETURNS  : 
  NOTES    : Input Parameters:   1 : step_size
                                 2 : momentum
				 3 : nhist
             Output Parameters:  1 : error of the network (sum of all cycles)

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err LEARN_BBPTT(int start_pattern, int end_pattern, 
		     float *parameterInArray, int NoOfInParams, 
		     float **parameterOutArray, int *NoOfOutParams)
{
    static float    OutParameter[1];	/* OutParameter[0] stores the
					   learning error  */
    int             i, ret_code, pattern_no, sub_pat_no, patterns;
    int             nhist;	/* number of steps back in time */
    register struct Unit *unit_ptr;

    if (NoOfUnits == 0)
	return (KRERR_NO_UNITS);        /* No Units defined	 */
    if (NoOfInParams < 1)     	        /* has to be ... snns habit ? */
	return (KRERR_PARAMETERS);	/* Not enough input parameters  */

    *NoOfOutParams = 1;		/* One return value is available (the
				   learning error)  */
    *parameterOutArray = OutParameter;	/* set the output parameter reference */
    ret_code = KRERR_NO_ERROR;	/* reset return code  */

    if (NetModified || (TopoSortID != TOPOLOGIC_TYPE)) {
	/* Net has been modified or topologic array isn't initialized */
	/* any connected topology allowed */
	/* count the no. of I/O units and check the patterns  */
	ret_code = kr_IOCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);

	/* sort units by ''topologic type'', criterion is visibility
	   (input,hidden,output), not topology */
	ret_code = kr_topoSort(TOPOLOGIC_TYPE);

	if ((ret_code != KRERR_NO_ERROR) && (ret_code != KRERR_DEAD_UNITS))
	    return (ret_code);
	/* sites are not supported, check absence */
	FOR_ALL_UNITS(unit_ptr)
	    if UNIT_HAS_SITES
	    (unit_ptr)
		return (KRERR_SITES_NO_SUPPORT);
	NetModified = FALSE;
    }
    if (NetInitialize || LearnFuncHasChanged) {	/* Net has been modified or
						   initialized, clear weight
						   changes */
	ret_code = BPTT_clear_deltaw();
	if (ret_code != KRERR_NO_ERROR)
	    return (ret_code);
    }
    NET_ERROR(OutParameter) = 0.0;	/* reset network error value  */

    NoOfLearnedPatterns = 0;	/* correct bits using threshold of 0.5 */
    nhist = LEARN_PARAM3(parameterInArray);
    if (nhist > MAX_BPTT_BACKSTEP)
	return (KRERR_NET_DEPTH);	/* actbuf and learning functions
					   support only MAX_BPTT_BACKSTEP net
					   copies */

    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    patterns = 0;
    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	/* FORWARD-BPTT */
	/* 1st parameter is the pattern number 2nd parameter is the number of
	   steps back in time */
	BPTT_propagateNetForward(pattern_no,sub_pat_no,nhist); /*Forward pass */

	/* Backward propagation  */
	NET_ERROR(OutParameter) 
	    += BPTT_propagateNetBackward(pattern_no, sub_pat_no, nhist);
	patterns++;
    }

    /* batch version */
    BPTTadapt(LEARN_PARAM1(parameterInArray) / patterns, 
	      LEARN_PARAM2(parameterInArray));

    return (ret_code);
}



/*****************************************************************************
  FUNCTION : LEARN_QPTT

  PURPOSE  : Quickprop through time learning function
  RETURNS  : kernel error code
  NOTES    : Input Parameters:   1 : step_size
                                 2 : maximum step growth
				 3 : decay factor
				 4 : nhist
             Output Parameters:  1 : error of the network (sum of all cycles)

  UPDATE   : 06.11.1993 by Guenter Mamier
******************************************************************************/
krui_err  LEARN_QPTT(int start_pattern, int end_pattern, 
		     float *parameterInArray, int NoOfInParams, 
		     float **parameterOutArray, int *NoOfOutParams)
{
    static float    OutParameter[1];	/* OutParameter[0] stores the
					   learning error  */
    int             i, ret_code, pattern_no, sub_pat_no, patterns;
    int             nhist;	/* number of steps back in time */
    register struct Unit *unit_ptr;

    if (NoOfUnits == 0)
	return (KRERR_NO_UNITS);/* No Units defined	 */
    if (NoOfInParams < 1)	/* snns habit ? */
	return (KRERR_PARAMETERS);	/* Not enough input parameters  */

    *NoOfOutParams = 1;		/* One return value is available (the
				   learning error)  */
    *parameterOutArray = OutParameter;	/* set the output parameter reference */
    ret_code = KRERR_NO_ERROR;	/* reset return code  */

    if (NetModified || (TopoSortID != TOPOLOGIC_TYPE)) {
	/* Net has been modified or topologic array isn't initialized */
	/* any connected topology allowed */
	/* count the no. of I/O units and check the patterns  */
	ret_code = kr_IOCheck();
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);

	/* sort units by ''topologic type'', criterion is visibility
	   (input,hidden,output), not topology */
	ret_code = kr_topoSort(TOPOLOGIC_TYPE);

	if ((ret_code != KRERR_NO_ERROR) && (ret_code != KRERR_DEAD_UNITS))
	    return (ret_code);
	/* sites are not supported, check absence */
	FOR_ALL_UNITS(unit_ptr)
	    if UNIT_HAS_SITES
	    (unit_ptr)
		return (KRERR_SITES_NO_SUPPORT);
	NetModified = FALSE;
    }
    if (NetInitialize || LearnFuncHasChanged) {	/* Net has been modified or
						   initialized, clear weight
						   changes */
	ret_code = BPTT_clear_deltaw();
	if (ret_code != KRERR_NO_ERROR)
	    return (ret_code);
    }
    NET_ERROR(OutParameter) = 0.0;	/* reset network error value  */

    NoOfLearnedPatterns = 0;	/* correct bits using threshold of 0.5 */
    nhist = LEARN_PARAM4(parameterInArray);
    if (nhist > MAX_BPTT_BACKSTEP)
	return (KRERR_NET_DEPTH);	/* actbuf and learning functions
					   support only MAX_BPTT_BACKSTEP net
					   copies */


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    patterns = 0;
    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	/* FORWARD-BPTT */
	/* 1st parameter is the pattern number 2nd parameter is the number of
	   steps back in time */
	BPTT_propagateNetForward(pattern_no,sub_pat_no,nhist); /*Forward pass */

	/* Backward propagation  */
	NET_ERROR(OutParameter)
	    += BPTT_propagateNetBackward(pattern_no, sub_pat_no, nhist);

	patterns++;
    }
    MODI_quickprop(LEARN_PARAM1(parameterInArray) / patterns,
		   LEARN_PARAM2(parameterInArray),
		   LEARN_PARAM3(parameterInArray));

    return (ret_code);
}


/*****************************************************************************

  GROUP        : kohonen_learning

  PURPOSE      : learning algorithm for Kohonen Feature Map
  AUTHOR       : Marc Seemann
  DATE         : August 6 1992
  LAST CHANGE  : 07.02.1994 by Sven Doering

             Copyright (c) 1992-1994  Neuro Group, Univ. of Tuebingen, FRG

******************************************************************************/


/*****************************************************************************
  FUNCTION : propagateNet_kohonen

  PURPOSE  : Propagate and train a pattern
  NOTES    :
  UPDATE   : 07.02 1994 by Sven Doering

             Copyright (c) 1992-1994  Neuro Group, Univ. of Tuebingen, FRG

******************************************************************************/

static float propagateNet_kohonen(int pattern_no, int sub_pat_no, float height,
				  float radius, int sizehor)
{
    register struct Link *link_ptr;
    register struct Site *site_ptr;
    register struct Unit *unit_ptr;
    register struct Unit *winner_ptr;
    register Patterns in_pat, out_pat;
    register int    NoOfCompounds, sizever, verwin, horwin, hor, ver, helpver,
    helphor, range, i;
    float           maximum, sum_error, deviat, learn_error, sum;
    float           unit_ptr_net;
    register TopoPtrArray topo_ptr;
    float           adapt;
    int             winner, ret, current_no;


    /* calculate the activation and the output values         */
    /* of the input units (Input Layer)                       */

    NoOfCompounds = NoOfInputUnits;
    sizever = NoOfHiddenUnits / sizehor;

    sum = 0.0;

    /* calculate startaddress for input pattern array  */
    in_pat = kr_getSubPatData(pattern_no,sub_pat_no,INPUT,NULL);

    topo_ptr = topo_ptr_array;

    /* copy pattern into input unit's activation and calculate output of the
       input units */
    while ((unit_ptr = *++topo_ptr) != NULL) { /* topo_ptr points to the
						  unit stuctures (sorted by:
						  input-, hidden- and
						  output-units, separated
						  with NULL pointers) */
	sum += *in_pat * *in_pat;

	if (unit_ptr->out_func == OUT_IDENTITY)
	    /* identity output function: there is no need to call the output
	       function  */
	    unit_ptr->Out.output = unit_ptr->act = *in_pat++;
	else
	    /* no identity output function: calculate unit's output also  */
	    unit_ptr->Out.output = 
		(*unit_ptr->out_func) (unit_ptr->act = *in_pat++);
    }

    if (sum != 0.0)
	/* normalize the inputvector */
	normalize_inputvector(sum);

    /* propagate Kohonen Layer  */

    /* calculate the activation and the output values */
    /* of the cmpetitive units (hidden layer) */

    /* winner is determined using the dot product */


    winner_ptr = NULL;
    maximum = -1.0e30;		/* contains the maximum of the activations */
    current_no = 0;

    /* propagate hidden units  */
    while ((unit_ptr = *++topo_ptr) != NULL) {	/* topo_ptr points to a
						   (topological sorted) unit
						   stucture */
	unit_ptr_net = 0.0;
	if (UNIT_HAS_DIRECT_INPUTS(unit_ptr)) {	/* the unit has direct links */
	    FOR_ALL_LINKS(unit_ptr, link_ptr)
		unit_ptr_net += (link_ptr->weight * link_ptr->to->Out.output);
	} else {		/* the unit has sites  */
	    FOR_ALL_SITES_AND_LINKS(unit_ptr, site_ptr, link_ptr)
		unit_ptr_net += (link_ptr->weight * link_ptr->to->Out.output);
	}

	if (maximum < unit_ptr_net) {	/* determine winner unit  */
	    winner_ptr = unit_ptr;
	    maximum = unit_ptr_net;
	    winner = current_no;
	}
	current_no++;
	/* reset output and activation of hidden units  */
	unit_ptr->Out.output = unit_ptr->act = (FlintType) 0;
    }

    /* the competitive winner is chosen */

    winner_ptr->Out.output = winner_ptr->act = (FlintType) 1;
    winner_ptr->bias++;
    winner_ptr->value_a = (FlintType) (pattern_no + 1);

    /* store number of according pattern in winner unit */

    horwin = winner % sizehor;
    verwin = winner / sizehor;


    /***************************************************************/
    /* Train  the  SOM                                             */

    /* Only the weights of links that go to the winner and its     */
    /* neighbourhood are adjusted, the others remain the same.     */
    /* The incoming weights to the competitive units are adapted   */
    /* as follows:                                                 */

    /* weight(new) = weight(old) + adapt * (output - weight(old))  */

    /* where adapt is the learning rate (0 < adapt <= 1.0)         */
    /* and output is the value of the input unit vector            */

    /***************************************************************/


    for (ver = 0; ver < sizever; ver++)
	for (hor = 0; hor < sizehor; hor++)
	    if ((hor < radius + horwin) &&
		(hor > horwin - radius) &&
		(ver < radius + verwin) &&
		(ver > verwin - radius)) {
		helpver = (float) ((ver - verwin) * (ver - verwin));
		helphor = (float) ((hor - horwin) * (hor - horwin));
		adapt = height * exp(-(helpver + helphor) / 
				     (float) (radius * radius));

		sum = 0.0;
		range = ver * sizehor + hor + 1 + NoOfCompounds;

		/* get unit pointer of unit in adaptation range */
		unit_ptr = kr_getUnitPtr(range);
		
		if(!IS_SPECIAL_UNIT(unit_ptr)){
		    if (unit_ptr->flags & UFLAG_DLINKS) { /* the unit has  */
							  /* direct links  */
			FOR_ALL_LINKS(unit_ptr, link_ptr) {
			    deviat=link_ptr->to->Out.output - link_ptr->weight;
			    learn_error = adapt * deviat;
			    link_ptr->weight += learn_error;
			    /* this is needed for the normalization of the
			       weight_vector */
			    sum += link_ptr->weight * link_ptr->weight;
			}
		    } else {	/* the winner unit has sites  */
			FOR_ALL_SITES_AND_LINKS(winner_ptr,site_ptr,link_ptr) {
			    deviat=link_ptr->to->Out.output - link_ptr->weight;
			    learn_error = adapt * deviat;
			    link_ptr->weight += learn_error;
			    /* this is needed for the normalization of the
			       weight_vector */
			    sum += link_ptr->weight * link_ptr->weight;
			}
		    }
		    if (sum != 0.0)
			normalize_weight(unit_ptr, sum);
		}
	    }
    sum_error = 0.0;		/* 0.0 is chosen arbitrarily and serves no
				   purpose */
    return (sum_error);
}


/*****************************************************************************
  FUNCTION : initializeKohonenLearning

  PURPOSE  : initialize the SOM
  NOTES    :
  UPDATE   : 19.08.1993

             Copyright (c) 1992-1994  Neuro Group, Univ. of Tuebingen, FRG

******************************************************************************/
static krui_err initializeKohonenLearning(void)
{
    register unsigned short flags;
    register struct Link *link_ptr;
    register struct Unit *unit_ptr;
    register struct Site *site_ptr;

    FOR_ALL_UNITS(unit_ptr) {
	flags = unit_ptr->flags;

	if ((flags & UFLAG_IN_USE) == UFLAG_IN_USE)	/* unit is in use  */
	    unit_ptr->value_a = unit_ptr->bias = (FlintType) 0.0;
    }
    return (KRERR_NO_ERROR);
}



/*****************************************************************************
  FUNCTION : LEARN_kohonen

  PURPOSE  :  incorporates the body of the kohonen learning algorithm
  NOTES    :  the parameterInArray must contain 4 parameter
                      1) initial adaptation height
                      2) initial adaptation radius
       	              3) multiplication factor
           	      4) horizontal ize of the competitive (hidden) layer

  UPDATE   : july 13 1993
******************************************************************************/
krui_err LEARN_kohonen(int start_pattern, int end_pattern,
		       float parameterInArray[], int NoOfInParams,
		       float **parameterOutArray, int *NoOfOutParams)
{
    static float    OutParameter[1];	/* OutParameter[0] stores the
					   learning error  */
    int             ret_code, i, pattern_no, sub_pat_no;
    register struct Unit *unit_ptr;
    register struct Link *link_ptr;

    if (NoOfUnits == 0)
	return (KRERR_NO_UNITS);/* No Units defined    */
    if (NoOfInParams < 5)	/* see Note  */
	return (KRERR_PARAMETERS);	/* Not enough input parameters  */

    *NoOfOutParams = 1;		/* one return value is available (the
				   learning error) */
    *parameterOutArray = OutParameter;	/* set output parameter reference  */
    ret_code = KRERR_NO_ERROR;	/* clear return code  */


    if (NetModified || (TopoSortID != TOPOLOGIC_TYPE)) {	
	/* Net has been modified or topologic array isn't initialized */
	/* count the no. of I/O units and check the patterns  */
	ret_code = kr_IOCheck();
	if (ret_code == KRERR_NO_OUTPUT_UNITS)
	    ret_code = KRERR_NO_ERROR;
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);

	/* sort units by topology and by topologic type  */
	ret_code = kr_topoSort(TOPOLOGIC_TYPE);
	if (ret_code == KRERR_NO_OUTPUT_UNITS)
	    ret_code = KRERR_NO_ERROR;
	if ((ret_code != KRERR_NO_ERROR) && (ret_code != KRERR_DEAD_UNITS))
	    return (ret_code);

	NetModified = FALSE;
    }
    if (NetInitialize || LearnFuncHasChanged) {	/* Net has been modified or
						   initialized, initialize
						   kohonen now  */
	ret_code = initializeKohonenLearning();
	if (ret_code != KRERR_NO_ERROR)
	    return (ret_code);
    }
    if ((int) LEARN_PARAM5(parameterInArray) == 0) {
	ret_code = KRERR_PARAMETERS;
	return (ret_code);
    }
    if ((LEARN_PARAM3(parameterInArray) > 1.0) ||
	(LEARN_PARAM3(parameterInArray) < 0.0)) {
	ret_code = KRERR_PARAMETERS;
	return (ret_code);
    }
    if ((LEARN_PARAM4(parameterInArray) > 1.0) ||
	(LEARN_PARAM4(parameterInArray) < 0.0)) {
	ret_code = KRERR_PARAMETERS;
	return (ret_code);
    }


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    NET_ERROR(OutParameter) = 0.0;	/* reset network error value  */
    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	NoOfLearnedPatterns++;
	NET_ERROR(OutParameter) +=
	    propagateNet_kohonen(pattern_no,sub_pat_no,
				 LEARN_PARAM1(parameterInArray),
				 LEARN_PARAM2(parameterInArray),
				 (int) LEARN_PARAM5(parameterInArray));

	LEARN_PARAM1(parameterInArray) *= LEARN_PARAM3(parameterInArray);
	LEARN_PARAM2(parameterInArray) *= LEARN_PARAM4(parameterInArray);

    }
    return (ret_code);
}


/*****************************************************************************
  FUNCTION : spanning_tree

  PURPOSE  : calculate the spanning tree of the kohonen feature map
  NOTES    : evaluating the learn function doesn't affect the net itself

  UPDATE   : july 13 1993
******************************************************************************/
krui_err  spanning_tree(void)
{
    register TopoPtrArray topo_ptr;
    register struct Unit *unit_ptr;
    register struct Link *link_ptr;
    int             ret_code, i, n, pattern_no, sub_pat_no;


    if (NoOfUnits == 0)
	return (KRERR_NO_UNITS);/* No Units defined    */

    ret_code = KRERR_NO_ERROR;	/* clear return code  */


    if (NetModified || (TopoSortID != TOPOLOGIC_TYPE)) {	
	/* Net has been modified or topologic array isn't initialized */
	/* count the no. of I/O units and check the patterns  */
	ret_code = kr_IOCheck();
	if (ret_code == KRERR_NO_OUTPUT_UNITS)
	    ret_code = KRERR_NO_ERROR;
	if (ret_code < KRERR_NO_ERROR)
	    return (ret_code);

	/* sort units by topology and by topologic type  */
	ret_code = kr_topoSort(TOPOLOGIC_TYPE);
	if (ret_code == KRERR_NO_OUTPUT_UNITS)
	    ret_code = KRERR_NO_ERROR;
	if ((ret_code != KRERR_NO_ERROR) && (ret_code != KRERR_DEAD_UNITS))
	    return (ret_code);

	NetModified = FALSE;
    }
    if (NetInitialize || LearnFuncHasChanged) {	/* Net has been modified or
						   initialized, initialize
						   kohonen now  */
	ret_code = initializeKohonenLearning();
	if (ret_code != KRERR_NO_ERROR)
	    return (ret_code);
    }
    topo_ptr = topo_ptr_array;

    while ((unit_ptr = *++topo_ptr) != NULL);
    /* topo_ptr points to the units' stucture (sorted by: input-, hidden- and
       output-units, separated by NULL pointers) */

    while ((unit_ptr = *++topo_ptr) != NULL)
	/* topo_ptr points to hidden_units */
	unit_ptr->value_a = 0;	/* the unit next to a pattern stores the
				   number of that pattern in value_a, at the
				   beginning initialized to 0 */


    n = 0;
    while(kr_getSubPatternByNo(&pattern_no,&sub_pat_no,n++)){

	/* To calculate the winning unit we call the  propagateNet_kohonen
	   function, and treat the map as 1-dimensional array */
	propagateNet_kohonen(pattern_no, sub_pat_no, 0.0, 0.0, 1);
    }

    return (ret_code);

}/* spanning_tree */




/*****************************************************************************

  GROUP        : JORDAN / ELMAN networks 

  PURPOSE      : learning functions for JORDAN / ELMAN networks
  AUTHOR       : Tobias Soyez
  DATE         : 09.11.1993
  LAST CHANGE  : 09.11.1993

             Copyright (c) 1990-1994  SNNS Group, IPVR, Univ. Stuttgart, FRG


******************************************************************************/


/*****************************************************************************
  FUNCTION : update_je_context_units

  PURPOSE  : synchronous update of context units
  NOTES    :

  UPDATE   :
******************************************************************************/

static void update_je_context_units (int pattern_no, int sub_pat_no) 

{
    register TopoPtrArray   topo_ptr, topo_ptr_context ;
    register struct Unit   *unit_ptr ;
    register Patterns       out_pat  ;
    int size;


    out_pat = kr_getSubPatData(pattern_no,sub_pat_no,OUTPUT,&size);
    out_pat += size;
    
    topo_ptr_context = topo_ptr_array + (no_of_topo_units + 3) ;


    /* ----  store real output, force teaching output  ---- */
 
    topo_ptr = topo_ptr_context ;

    while ((unit_ptr = *--topo_ptr) != NULL)
    {
      unit_ptr->actbuf[0]  = unit_ptr->Out.output ;
      unit_ptr->Out.output = *--out_pat ;
    }
    

    /* ----  calculate new activation of context units ---- */

   topo_ptr = topo_ptr_context ;

    while ((unit_ptr = *++topo_ptr) != NULL)
    {
      unit_ptr->act = (*unit_ptr->act_func) (unit_ptr) ;

      if (unit_ptr->out_func == OUT_IDENTITY)
        unit_ptr->Out.output = unit_ptr->act ;
      else
        unit_ptr->Out.output = (*unit_ptr->out_func) (unit_ptr->act) ;
    }

    /* ----  restore real output  ---- */

    topo_ptr = topo_ptr_context ;

    while ((unit_ptr = *--topo_ptr) != NULL)
    {
      unit_ptr->Out.output = unit_ptr->actbuf[0] ;
    }
}


/*****************************************************************************
  FUNCTION : reset_je_context_units

  PURPOSE  : resets the context units 
  NOTES    :

  UPDATE   :
******************************************************************************/

static void reset_je_context_units (void)

{
  register TopoPtrArray   topo_ptr ;
  register struct Unit   *unit_ptr ;

 
  topo_ptr = topo_ptr_array + (no_of_topo_units + 3) ;
  
  while ((unit_ptr = *++topo_ptr) != NULL)
  {
    unit_ptr->act = unit_ptr->i_act ;

    if (unit_ptr->out_func == OUT_IDENTITY)
      unit_ptr->Out.output = unit_ptr->act ;
    else
      unit_ptr->Out.output = (*unit_ptr->out_func) (unit_ptr->act) ;
  }
}


/*****************************************************************************
  FUNCTION : check_je_network

  PURPOSE  : checks the topology of a partial recurrent network
             (i.e. JORDAN and ELMAN networks)
  NOTES    :

  UPDATE   :
******************************************************************************/
static krui_err check_je_network (void) 

{
    /*  check the topology of the network  */
    (void) kr_topoCheckJE () ;
    if (KernelErrorCode != KRERR_NO_ERROR) return (KernelErrorCode) ;

    /*	count the no. of I/O units and check the patterns  */
    if (kr_IOCheck() != KRERR_NO_ERROR) return (KernelErrorCode) ;

    /*	sort units by topology and by topologic type  */
    (void) kr_topoSort (TOPOLOGICAL_JE) ;

    if ((KernelErrorCode != KRERR_NO_ERROR) && 
        (KernelErrorCode != KRERR_DEAD_UNITS))
      return (KernelErrorCode) ;

    NetModified = FALSE;
    return (KRERR_NO_ERROR) ;
}



/*****************************************************************************
  FUNCTION : LEARN_JE_Backprop

  PURPOSE  : backpropagation learning function for JORDAN / ELMAN networks
  NOTES    : input parameters  :   1. learning parameter
                                   2. delta max 
             output parameters :   1. error of the network (sum of all cycles)
             return value      :   kernel error code
  UPDATE   :
******************************************************************************/
krui_err  LEARN_JE_Backprop (int     start_pattern    , int  end_pattern ,
                             float  *parameterInArray , int  NoOfInParams, 
                             float **parameterOutArray, int *NoOfOutParams)

{
  static float  OutParameter[1] ; /* OutParameter[0] stores the */
                                  /* learning error             */
  int	        n, pattern_no,sub_pat_no ;
  int           start, end;


  KernelErrorCode = KRERR_NO_ERROR;  /*  reset return code  */

  if (NoOfInParams < 2) return (KRERR_PARAMETERS) ;

  *NoOfOutParams     = 1            ; /* one return value is available      */
                                      /* (the learning error)               */
  *parameterOutArray = OutParameter ; /* set the output parameter reference */ 
  NET_ERROR (OutParameter) = 0.0    ; /* reset network error value          */

  if (NetModified || (TopoSortID != TOPOLOGICAL_JE))
  {
     KernelErrorCode = check_je_network () ;
     if (KernelErrorCode != KRERR_NO_ERROR) return (KernelErrorCode) ;
  }

  reset_je_context_units () ;


    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);

    start = kr_AbsPosOfFirstSubPat(start_pattern);
    end   = kr_AbsPosOfFirstSubPat(end_pattern);
    end  += kr_NoOfSubPatPairs(end_pattern) - 1;

    for(n=start; n<=end; n++){

	kr_getSubPatternByNo(&pattern_no,&sub_pat_no,n);

	propagateNetForward (pattern_no,sub_pat_no) ;	
	NET_ERROR (OutParameter) +=
	    propagateNetBackward2 (pattern_no,sub_pat_no, 
				   LEARN_PARAM1 (parameterInArray), 
				   LEARN_PARAM2 (parameterInArray)) ;
	update_je_context_units (pattern_no,sub_pat_no) ;
    }

  return (KernelErrorCode) ;
}


/*****************************************************************************
  FUNCTION : LEARN_JE_BackpropMomentum

  PURPOSE  : backpropagation with momentum term learning funcyion
             for JORDAN / ELMAN networks
  NOTES    : input parameters  :   1. learning parameter
                                   2. momentum factor
                                   3. flat spot elimination
                                   4. delta max
             output parameters :   1. error of the network (sum of all cycles)
             return value      :   kernel error code
  UPDATE   :
******************************************************************************/
krui_err LEARN_JE_BackpropMomentum(int start_pattern, int end_pattern,
				   float *parameterInArray, int NoOfInParams, 
				   float **parameterOutArray, 
				   int *NoOfOutParams)

{
  static float  OutParameter[1] ; /* OutParameter[0] stores the */
                                  /* learning error             */
  int	        n, pattern_no,sub_pat_no ;
  int           start, end;

  KernelErrorCode = KRERR_NO_ERROR;  /*  reset return code  */

  if (NoOfInParams < 2) return (KRERR_PARAMETERS) ;

  *NoOfOutParams     = 1            ; /* one return value is available      */
                                      /* (the learning error)               */
  *parameterOutArray = OutParameter ; /* set the output parameter reference */ 
  NET_ERROR (OutParameter) = 0.0    ; /* reset network error value          */

  if (NetModified || (TopoSortID != TOPOLOGICAL_JE))
  {
     KernelErrorCode = check_je_network () ;
     if (KernelErrorCode != KRERR_NO_ERROR) return (KernelErrorCode) ;
  }

  if (NetInitialize || LearnFuncHasChanged)
  {  /*  Net has been modified or initialized, initialize backprop now  */
    KernelErrorCode = initializeBackpropMomentum () ;
    if (KernelErrorCode != KRERR_NO_ERROR) return (KernelErrorCode) ;
  }

  reset_je_context_units () ;


  /* compute the necessary sub patterns */

  KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
  if(KernelErrorCode != KRERR_NO_ERROR)
      return (KernelErrorCode);
  
  start = kr_AbsPosOfFirstSubPat(start_pattern);
  end   = kr_AbsPosOfFirstSubPat(end_pattern);
  end  += kr_NoOfSubPatPairs(end_pattern) - 1;

  for(n=start; n<=end; n++){

      kr_getSubPatternByNo(&pattern_no,&sub_pat_no,n);

      propagateNetForward (pattern_no,sub_pat_no) ;	
      NET_ERROR (OutParameter) +=
	  Backprop_momentum_FSE (pattern_no,sub_pat_no,
				 LEARN_PARAM1( parameterInArray ),
				 LEARN_PARAM2( parameterInArray ),
				 LEARN_PARAM3( parameterInArray ),
				 LEARN_PARAM4( parameterInArray )) ;
      update_je_context_units (pattern_no, sub_pat_no) ;
  }

  return (KernelErrorCode) ;
}


/*****************************************************************************
  FUNCTION : LEARN_JE_Quickprop

  PURPOSE  : quickprop learning function for JORDAN / ELMAN networks
  NOTES    : input parameters  :   1. learning parameter
                                   2. max. growth factor
                                   3. weight decay
                                   4. delta max 
             output parameters :   1. error of the network (sum of all cycles)
             return value      :   kernel error code
  UPDATE   :
******************************************************************************/
krui_err  LEARN_JE_Quickprop (int     start_pattern    , int  end_pattern ,
                              float  *parameterInArray , int  NoOfInParams, 
                              float **parameterOutArray, int *NoOfOutParams)

{
  static float  OutParameter[1] ; /* OutParameter[0] stores the */
                                  /* learning error             */
  int	        n,pattern_no,sub_pat_no ;
  int           start, end;

  KernelErrorCode = KRERR_NO_ERROR;  /*  reset return code  */

  if (NoOfInParams < 2) return (KRERR_PARAMETERS) ;

  *NoOfOutParams     = 1            ; /* one return value is available      */
                                      /* (the learning error)               */
  *parameterOutArray = OutParameter ; /* set the output parameter reference */ 
  NET_ERROR (OutParameter) = 0.0    ; /* reset network error value          */

  if (NetModified || (TopoSortID != TOPOLOGICAL_JE))
  {
     KernelErrorCode = check_je_network () ;
     if (KernelErrorCode != KRERR_NO_ERROR) return (KernelErrorCode) ;
  }

  if (NetInitialize || LearnFuncHasChanged)
  { 
    /*  Net has been modified or initialized, initialize quickprop now  */
    KernelErrorCode = initializeQuickprop () ;
    if (KernelErrorCode != KRERR_NO_ERROR)  return (KernelErrorCode) ;
  }


  reset_je_context_units () ;



  /* compute the necessary sub patterns */

  KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
  if(KernelErrorCode != KRERR_NO_ERROR)
    return (KernelErrorCode);

  start = kr_AbsPosOfFirstSubPat(start_pattern);
  end   = kr_AbsPosOfFirstSubPat(end_pattern);
  end  += kr_NoOfSubPatPairs(end_pattern) - 1;

  for(n=start; n<=end; n++){

      kr_getSubPatternByNo(&pattern_no,&sub_pat_no,n);

      propagateNetForward (pattern_no,sub_pat_no) ;	
      NET_ERROR(OutParameter) +=
	  propagateNetBackwardQuickprop (pattern_no,sub_pat_no,
					 LEARN_PARAM4 (parameterInArray)) ;
      update_je_context_units (pattern_no,sub_pat_no) ;
  }

  MODI_quickprop (LEARN_PARAM1 (parameterInArray),
                  LEARN_PARAM2 (parameterInArray),
                  LEARN_PARAM3 (parameterInArray)) ;

  return (KernelErrorCode) ;
}



/*****************************************************************************
  FUNCTION : LEARN_JE_Rprop

  PURPOSE  : rprop learning function for JORDAN / ELMAN networks
  NOTES    : input parameters  :   1. delta 0
                                   2. delta max 
             output parameters :   1. error of the network (sum of all cycles)
             return value      :   kernel error code
  UPDATE   :
******************************************************************************/
krui_err  LEARN_JE_Rprop    (int     start_pattern    , int  end_pattern ,
                             float  *parameterInArray , int  NoOfInParams, 
                             float **parameterOutArray, int *NoOfOutParams)

{
  static float  OutParameter[1] ; /* OutParameter[0] stores the */
                                  /* learning error             */
  int	        pattern_no,sub_pat_no ;
  int           i, n, ret_code, blocksize ;
  float         maxeps, update_value ;
  int           start, end;


  if (NoOfInParams < 2) return (KRERR_PARAMETERS) ;

  if (( update_value = LEARN_PARAM1 (parameterInArray)) == 0.0) 
    update_value = RPROP_DEFAULT_UPDATE_VALUE;
  if ((maxeps = LEARN_PARAM2 (parameterInArray)) == 0.0) 
    maxeps = RPROP_MAXEPS;
  if (update_value > maxeps) update_value = maxeps;


  KernelErrorCode = ret_code = KRERR_NO_ERROR;  /*  reset return code  */


  *NoOfOutParams     = 1            ; /* one return value is available      */
                                      /* (the learning error)               */
  *parameterOutArray = OutParameter ; /* set the output parameter reference */ 
  NET_ERROR (OutParameter) = 0.0    ; /* reset network error value          */

  if (NetModified || (TopoSortID != TOPOLOGICAL_JE))
  {
     KernelErrorCode = check_je_network () ;
     if (KernelErrorCode != KRERR_NO_ERROR) return (KernelErrorCode) ;
  }

  if (NetInitialize || LearnFuncHasChanged)
  {  /*  Net has been modified or initialized, initialize RPROP */
    ret_code = initializeRprop (update_value) ;
    if (ret_code != KRERR_NO_ERROR)  return (ret_code) ;
  }

  /* DEFAULTS: */
  if ((blocksize = LEARN_PARAM3 (parameterInArray)) == 0)
    blocksize = end_pattern;

  reset_je_context_units () ;


  /* compute the necessary sub patterns */

  KernelErrorCode = kr_initSubPatternOrder(start_pattern,blocksize);
  if(KernelErrorCode != KRERR_NO_ERROR)
      return (KernelErrorCode);

  start = kr_AbsPosOfFirstSubPat(start_pattern);
  end   = kr_AbsPosOfFirstSubPat(end_pattern);
  end  += kr_NoOfSubPatPairs(end_pattern) - 1;

  for(n=start; n<=end; n++){

      kr_getSubPatternByNo(&pattern_no,&sub_pat_no,n);

      propagateNetForward (pattern_no,sub_pat_no) ;	
      NET_ERROR (OutParameter) +=
	  propagateNetBackwardRprop (pattern_no,sub_pat_no) ;
      update_je_context_units (pattern_no,sub_pat_no) ;
  }
  MODI_rprop (maxeps) ;
  return (KernelErrorCode) ;
}



/*****************************************************************************

  GROUP        : Functions for autoassoziative memory networks

  PURPOSE      : Implement autoassoziative memory networks, including learning 
                 functions for Rummelhart & McClelland's Delta Rule and Hebbian
		 learning
  AUTHOR       : Jamie DeCoster
  CHANGES BY   : Guenter Mamier
  DATE         : 08.02.1994
  LAST CHANGE  : 09.03.1994

             Copyright (c) 1994  Jamie DeCoster, Purdue University, USA

******************************************************************************/

/*****************************************************************************
  FUNCTION : RM_propagate

  PURPOSE  : forward propagation for Rummelhart & McClelland's Delta Rule
  NOTES    : 

  UPDATE   : 17.02.1994
******************************************************************************/
static void RM_propagate (int pattern_no, int sub_pat_no, float prop_step)
{

    int t;
    register struct Unit   *unit_ptr;
    register Patterns      in_pat;
    register TopoPtrArray  topo_ptr;


    /*  calculate startaddress for input pattern array  */
    in_pat = kr_getSubPatData(pattern_no,sub_pat_no,INPUT,NULL);
    if(in_pat == NULL){
	KernelErrorCode = KRERR_NP_NO_SUCH_PATTERN;
	return;
    }
    
    topo_ptr = topo_ptr_array;

    /*  copy pattern into input unit's activation and calculate output of the 
	input units */
    while ((unit_ptr = *++topo_ptr) != NULL){

	/*  topo_ptr points to a (topological sorted) unit stucture  */
	if (unit_ptr->out_func == OUT_IDENTITY)
	    /*  identity output function: don't call the output function  */
	    unit_ptr->Out.output = unit_ptr->act = *in_pat++;
	else
	    /*  no identity output function: calculate unit's output also  */
	    unit_ptr->Out.output = 
		(*unit_ptr->out_func) (unit_ptr->act = *in_pat++);
    }

    for (t=0; t < prop_step; ++t){ 

	FOR_ALL_UNITS( unit_ptr )
	    if UNIT_IN_USE( unit_ptr ){

		/* update unit activations first  */
		if ( !IS_INPUT_UNIT( unit_ptr)) 
		    /*  unit isn't an input unit and is in use and enabled  */
		    unit_ptr->act = (*unit_ptr->act_func) (unit_ptr);

		/* update unit outputs  */
		if (unit_ptr->out_func == OUT_IDENTITY)
		    /*  identity output function: don't call output function  */
		    unit_ptr->Out.output = unit_ptr->act;
		else
		    /*  calculate unit's output also  */
		    unit_ptr->Out.output = (*unit_ptr->out_func)(unit_ptr->act);
	    }
    }

}


/*****************************************************************************
  FUNCTION : RM_learn

  PURPOSE  : backward propagation for Rummelhart & McClelland's Delta Rule
  NOTES    : 

  UPDATE   : 11.02.1994
******************************************************************************/
static void RM_learn(float learn_parameter)
{
    register struct Link *link_ptr;
    register struct Site *site_ptr;
    register struct Unit *unit_ptr;
    float ex_in, in_in, error, eta;

    eta = learn_parameter;

    FOR_ALL_UNITS (unit_ptr)
	if (!IS_INPUT_UNIT (unit_ptr)){
	    /* World units don't learn so their inputs are not examined */

	    in_in = 0;

	    FOR_ALL_LINKS (unit_ptr, link_ptr)
		if (IS_INPUT_UNIT (link_ptr->to))
		    /* Determine the input from the world unit */
		    ex_in = link_ptr->to->act * link_ptr->weight;
		else
		    /* Determine the input from the network */
		    in_in += link_ptr->to->act * link_ptr->weight; 

	    /* Error defined as the difference between the world input and 
	       the input from the net */ 
	    error = ex_in - in_in; 
 
	    /* Modify the weights */ 
	    if (UNIT_HAS_DIRECT_INPUTS (unit_ptr)){
		FOR_ALL_LINKS (unit_ptr, link_ptr)
		    if (!IS_INPUT_UNIT (link_ptr->to))  
			/* The link between a world unit and its corresponding 
			   learning unit is always 1 */
			link_ptr->weight += link_ptr->to->act * eta * error;
	    }else{
		FOR_ALL_SITES_AND_LINKS (unit_ptr, site_ptr, link_ptr)
		    if (!IS_INPUT_UNIT (link_ptr->to))
			link_ptr->weight += link_ptr->to->act * eta * error;
	    }
	}
}



/*****************************************************************************
  FUNCTION : LEARN_RM_delta 

  PURPOSE  : McClelland & Rumelhart's learning rule
                      Input parameter:   1: learning parameter
		                         2: no. of propagation steps
		      Output parameter:  1:  Learning error
  NOTES    : 

  UPDATE   : 11.02.1994
******************************************************************************/
krui_err LEARN_RM_delta (int start_pattern, int end_pattern,
			 float *parameterInArray, int NoOfInParams, 
			 float **parameterOutArray, int *NoOfOutParams)
{

    static float OutParameter [1];
    int i, j, pattern_no,sub_pat_no;
    int no_of_layers;
    float Learn_p;
    float prop_step;
    register struct Unit *unit_ptr;
    register Patterns in_pat;

    KernelErrorCode = KRERR_NO_ERROR;

    /* Checking for learning parameter */
    if (NoOfInParams < 2){  
	KernelErrorCode = KRERR_PARAMETERS;
	return (KernelErrorCode);
    }

    Learn_p = LEARN_PARAM1 (parameterInArray);
    prop_step = LEARN_PARAM2 (parameterInArray);
    if (prop_step == 0){  
	KernelErrorCode = KRERR_PARAMETERS;
	return (KernelErrorCode);
    }

    *NoOfOutParams = 1; /* Out Parameter = Learning error */
    *parameterOutArray = OutParameter;

    (void) kr_topoSort (TOPOLOGIC_TYPE);

    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
	return (KernelErrorCode);


    /* reset network error value  */
    NET_ERROR (OutParameter) = 0.0;

    /* Determine order of pattern presentation */
    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){

	/* Propagate the pattern through the network */
	RM_propagate (pattern_no,sub_pat_no,prop_step);        

	/* Update links */ 
	RM_learn (Learn_p);

	/* Compute network error */ 
	NET_ERROR (OutParameter) += Hebb_error(prop_step); 
    }

    return (KernelErrorCode);
} 


/*****************************************************************************
  FUNCTION : Hebb_error

  PURPOSE  : Compute the error of the network for the Hebbian learning rule
  NOTES    : 

  UPDATE   : 09.03.1994
******************************************************************************/
static float Hebb_error(int NoOfTimes)
{

    struct Unit *unit_ptr;
    struct Link *link_ptr;
    float error, sum_error, ex_in;
    int t;


    /* update unit activations first  */
    for(t=0; t < NoOfTimes; ++t){ 
	FOR_ALL_UNITS( unit_ptr )
	    if ( !IS_INPUT_UNIT( unit_ptr)) 
		if UNIT_IN_USE( unit_ptr )
		    /*  unit isn't an input unit and is in use and enabled  */
		    unit_ptr->act = (*unit_ptr->act_func) (unit_ptr);

	/* update unit outputs */
	FOR_ALL_UNITS( unit_ptr )
	    if UNIT_IN_USE( unit_ptr )
		if (unit_ptr->out_func == OUT_IDENTITY)
		    /* there is no need to call the output function  */
		    unit_ptr->Out.output = unit_ptr->act;
		else
		    /* calculate unit's output also  */
		    unit_ptr->Out.output = (*unit_ptr->out_func)(unit_ptr->act);
    }


    /* calculate the error defined as the difference between the internal
       and external inputs */

    sum_error = 0.0;

    FOR_ALL_UNITS (unit_ptr){
	FOR_ALL_LINKS (unit_ptr, link_ptr)
	    if (IS_INPUT_UNIT (link_ptr->to)){
		ex_in = link_ptr->to->act;
		error = ex_in - unit_ptr->act;
	    }
	sum_error += error * error;
    }
    return (sum_error);
}



/*****************************************************************************
  FUNCTION : LEARN_Hebb 

  PURPOSE  : Hebbian learning rule
                      Input parameter:   1: learning parameter
		                         2: Maximum absolute weight strength
		      Output parameter:  1: Network error
  NOTES    : 

  UPDATE   : 09.03.1994
******************************************************************************/
krui_err LEARN_HEBB (int start_pattern, int end_pattern,
		     float *parameterInArray, int NoOfInParams, 
		     float **parameterOutArray, int *NoOfOutParams)
{
    static float OutParameter [1];
    int i, pattern_no, sub_pat_no;
    int NoOfTimes;
    float Learn_p, Weight_MAX;
    register struct Unit *unit_ptr;
    register struct Link *link_ptr;
    register struct Site *site_ptr;
    register Patterns in_pat;
    register TopoPtrArray topo_ptr;


    KernelErrorCode = KRERR_NO_ERROR;

    if (NoOfInParams < 3){  /* Checking for learning parameter */
	KernelErrorCode = KRERR_PARAMETERS;
	return (KernelErrorCode);
    }

    *NoOfOutParams = 1;  /* Out Parameter = Learning error */
    *parameterOutArray = OutParameter;

    Learn_p    = LEARN_PARAM1 (parameterInArray);
    Weight_MAX = LEARN_PARAM2 (parameterInArray);
    NoOfTimes  = (int)LEARN_PARAM3 (parameterInArray);

    if (NoOfTimes == 0){  /* Checking for learning parameter */
	KernelErrorCode = KRERR_PARAMETERS;
	return (KernelErrorCode);
    }
    
    kr_topoSort (TOPOLOGIC_TYPE);

    /* compute the necessary sub patterns */

    KernelErrorCode = kr_initSubPatternOrder(start_pattern,end_pattern);
    if(KernelErrorCode != KRERR_NO_ERROR)
        return (KernelErrorCode);


    /* reset network error value  */
    NET_ERROR (OutParameter) = 0.0;

    /* Determine order of pattern presentation */
    while(kr_getSubPatternByOrder(&pattern_no,&sub_pat_no)){
  
	/* calculate startaddress for input pattern array */
	in_pat = kr_getSubPatData(pattern_no,sub_pat_no,INPUT,NULL);

	topo_ptr = topo_ptr_array;

	/* copy pattern into input units  and calculate their output */
	while ((unit_ptr = *++topo_ptr) != NULL){
	    /* topo_ptr points to a unit structure (input units first) */
	    if (unit_ptr->out_func == OUT_IDENTITY)
		/* identity output function */
		unit_ptr->Out.output = unit_ptr->act = *in_pat++;
	    else
		/* calculate unit's output */
		unit_ptr->Out.output = 
		    (*unit_ptr->out_func) (unit_ptr->act = *in_pat++);
	}

	/* copy pattern from the world units to the learning units */
	FOR_ALL_UNITS (unit_ptr)
	    FOR_ALL_LINKS (unit_ptr, link_ptr)
		if (IS_INPUT_UNIT (link_ptr->to))
		    unit_ptr->act = link_ptr->to->act;

	/* Network has the same structure as the RM_delta autoassociative 
	   network. Here we update the learning unit links. */
	FOR_ALL_UNITS (unit_ptr)
	    if (!IS_INPUT_UNIT (unit_ptr)){

		/* Update the links */
		if (UNIT_HAS_DIRECT_INPUTS (unit_ptr)){
		    FOR_ALL_LINKS (unit_ptr, link_ptr)
			if (!IS_INPUT_UNIT (link_ptr->to)){
			    /* Only change learning links */
			    link_ptr->weight += 
				Learn_p * unit_ptr->act * (link_ptr->to->act);
			    if (link_ptr->weight > Weight_MAX)
				link_ptr->weight = Weight_MAX;
			    if (link_ptr->weight < -Weight_MAX)
				link_ptr->weight = -Weight_MAX;
			}
		}else{
		    FOR_ALL_SITES_AND_LINKS (unit_ptr, site_ptr, link_ptr)
			if (!IS_INPUT_UNIT (link_ptr->to)){
			    link_ptr->weight += 
				Learn_p * unit_ptr->act * (link_ptr->to->act);
			    if (link_ptr->weight > Weight_MAX)
				link_ptr->weight = Weight_MAX;
			    if (link_ptr->weight < -Weight_MAX)
				link_ptr->weight = -Weight_MAX;
			}
		}
	    }

	NET_ERROR (OutParameter) += Hebb_error (NoOfTimes); 
    }
    return (KernelErrorCode);
}

