[ return ]

connector.c




/*
 *  file : connector.c
 *
 * [DEFINE DESCRIPTION = Sparse matrix representation class, with connection strengths]
 *
 *  Name                Date        Description
 *  --------------      --------    -------------------------------------
 *  Andrew H. Fagg      08/24/95    Added stuff() member function.        //OVERLOAD CALL: stuff: gate.c(gate_class), layer.c(layer_class), column.c(column_class), connector.c(connector_class)
 *
 *  Andrew H. Fagg      08/10/92    Original
 *
 *
 *
 *  The connector type provides an efficient way of connecting
 * units together in sparse networks.  The connector class
 * stores a list of connections that have been made into a unit.
 * These connections (information stored within the connector class)
 * consist of a name of the source of the connection and a pointer
 * to the nsl_data value that stores the activity value of the
 * connecting unit, and an initial weight (if applicable).
 *  Once the connections are made, the set of activity values
 * can be accessed as a single vector.
 *
 *
 *  The connector stores connections in dynamically-allocated
 * arrays in blocks of <ALLOC_BLOCK>.  So, every <ALLOC_BLOCK>
 * connections, the old list of pointers must be deallocated
 * and a new (longer) set must be allocated.
 *
 *
 * Requires:
 *   nsl_data& translate_string_to_pointer(char *) to be defined externally.
 *
 */

#include "nsl_include.h"
#include "connector.h"
#include "alib.h"
#include "gen.h"

/* #define DEBUG  */

/*
 * connector_class::connector_class(short fl)
 *
 *  <fl> = connector configuration (as defined in connection.h).
 *
 *  Connector class constructor.  Allocate the first block
 * of storage arrays.
 *
 */

connector_class::connector_class(short fl)
{
#ifdef DEBUG
  cout << "create new connector" nl;
  cout.flush();
#endif
  num_pointers = 0;
  num_allocated = ALLOC_BLOCK;
  pointers = new nsl_data*[num_allocated];
  names = new name_class*[num_allocated];
  initial_weights = new nsl_vector(num_allocated);
  flags = fl;
#ifdef DEBUG
  cout << "end new" nl;
  cout.flush();
#endif
}


/*
 * void connector_class::add_connection(nsl_data* new_value, name_class *name,        //OVERLOAD CALL: add_connection: connector.c(connector_class), reverse_connector.c(reverse_connector_input_class), reverse_connector.c(reverse_connector_output_class)
 *  				     float initial_value)
 *
 *  <new_value> = a pointer to the activity value input (source).
 *  <name> = name of source.
 *  <initial_value> = the initial value of the weight (used only if
 *                    the connector is not a CONNECTOR_RANDOM).
 *
 *  Add a new connection to a connector.  If this addition requires
 * a new block of storage, then create it, copy the relevant info
 * and delete the old lists.
 *
 */

void connector_class::add_connection(nsl_data* new_value, name_class *name,
				     float initial_value)

{
  nsl_data** temp;
  name_class** temp_names;
  nsl_vector* temp_weights;

#ifdef DEBUG
  cout << "Add connection: " << *new_value sp << *name nl;
  cout.flush();
#endif
  if(num_pointers == num_allocated)
    {                          /*  Allocate a new block.  */
      num_allocated += ALLOC_BLOCK;
                         /*  New arrays.  */
      temp = new nsl_data*[num_allocated];
      temp_names = new name_class*[num_allocated];
      temp_weights = new nsl_vector(num_allocated);
                         /*  Copy information.  */

      bcopy(pointers, temp, sizeof(nsl_data *) * num_pointers);
      bcopy(names, temp_names, sizeof(name_class *) * num_pointers);
                         /*  Delete old arrays.  */
      delete pointers;
      delete names;
      pointers = temp;
      names = temp_names;
                         /*  Handle initial-weight arrays.  */
      if(!(flags&CONNECTOR_RANDOM))
	{
	  register int i;
	  for(i = 0; i < num_pointers; ++i)
	    temp_weights->elem(i) = initial_weights->elem(i);
	  delete initial_weights;
	  initial_weights = temp_weights;
	}
    }                          /*  Assign new connection.  */
  pointers[num_pointers] = new_value;
  names[num_pointers] = name;
  if(!(flags&CONNECTOR_RANDOM))
    initial_weights->elem(num_pointers) = initial_value;
  num_pointers++;

#ifdef DEBUG
  cout << "end connect" nl;
  cout.flush();
#endif
}

/*
 * nsl_vector connector_class::get_values()        //OVERLOAD CALL: get_values: connector.c(connector_class), reverse_connector.c(reverse_connector_input_class), reverse_connector.c(reverse_connector_output_class), sensor.c(sensor_class)
 *
 *  Return a vector of input activity values.
 *
 */

nsl_vector connector_class::get_values()
{
	nsl_vector out(num_pointers);
	register int i;

	for(i = 0; i < num_pointers; ++i)
		out.elem(i) = (*pointers[i]).elem();
	return out;
}


#define SMALL 0.000001

nsl_vector& new_normalize(nsl_vector& vec, nsl_data *min, nsl_data *max)

{
  vec = NSLsaturation(vec, *min, *max, *min, *max);

  float excess = 1.0 - vec.sum();
  int N = vec.get_xn();

  float excess_new = 0;
  int n = 0;
  int n_new = 0;
  int i;
  float temp;
  int count = 0;
  float increment;

  if(excess < 0.0)		
    {				/* Negative case. */
      for(i = 0; i < N; ++i)
	{
	  if(vec.elem(i) > min->elem())
	    ++n;
	}
      increment = excess/(float)n;

      while(excess > SMALL || excess < -SMALL)
	{
	  for(i = 0; i < N; ++i)
	    {
	      if(vec.elem(i) > min->elem())
		{
		  temp = vec.elem(i) + increment;
		  if(temp <= min->elem())
		    temp = min->elem();
		  else
		    n_new++;
		  excess_new += (vec.elem(i) + increment) - temp;
		  vec.elem(i) = temp;
		}
	    }
	  excess = excess_new;
	  excess_new = 0.0;
	  n = n_new;
	  n_new = 0;
	  increment = excess/n;
	}
    }
  else
    {				/* Positive case. */
      vec = vec + excess/N;
    }
  return vec;
}


/*
 * nsl_vector connector_class::get_initial_weights(nsl_data* offset)
 *
 *  <offset> = weight offset for random weights
 *
 *  Return a vector of initial weights.
 *     If CONNECTOR_RANDOM is set in the status, then the weights are
 *          randomly selected.  Otherwise, they are copied from the
 *          connector's <initial_weights> vector.
 *     If CONNECTOR_NORMALIZE is selected, then the weights are
 *          L1-normalized.
 *
 */

nsl_vector connector_class::get_initial_weights(nsl_data* offset,
						nsl_data* weight_min,
						nsl_data* weight_max)
{
#ifdef DEBUG
  cout << "*** Entered connector_class::get_initial_weights ***" nl;
  cout.flush();
#endif
  nsl_vector out(num_pointers);

  if(flags & CONNECTOR_RANDOM)
    {
                     /*  Random case.  */
#ifdef DEBUG
      cout << "*** connector_class::get_initial_weights : before random ***" nl;
      cout.flush();
#endif
      random(out);        //OVERLOAD CALL: random: funcs.c(?), funcs.c(?)
#ifdef DEBUG
      cout << "*** connector_class::get_initial_weights : after random ***" nl;
      cout.flush();
#endif
      out =  *offset + out;
    }
  else
    {
                     /*  Copy from initial weights.  */
      register int i;
      for(i = 0; i < num_pointers; ++i)
	out.elem(i) = initial_weights->elem(i);
    }
#ifdef DEBUG
  cout << "*** connector_class::get_initial_weights : before normalize ***" nl;
  cout.flush();
#endif

  if(flags & CONNECTOR_NORMALIZE)
                     /*  Normalize.  */
    out = out/out.sum();

  if(flags & CONNECTOR_NEW_NORMALIZE)
                     /*  Normalize.  */
    out = new_normalize(out, weight_min, weight_max);

  if(flags & CONNECTOR_CLIP_NORMALIZE)
    out = NSLsat(out, 0, 1, weight_min->elem(), weight_max->elem());

#ifdef DEBUG
  cout << "*** End connector_class::get_initial_weights ***" nl;
  cout.flush();
#endif
  return out;
}


/*
 * connector_class::~connector_class()
 *
 *  connector class destructor.
 *
 */

connector_class::~connector_class()
{
  delete pointers;
  delete names;
  if(initial_weights != NULL)
    delete initial_weights;
}

/*
 * name_class* connector_class::get_name(int i)        //OVERLOAD CALL: get_name: column.c(column_class), connector.c(connector_class), gate.c(gate_class), layer.c(layer_class), reverse_connector.c(reverse_connector_input_class), reverse_connector.c(reverse_connector_output_class), sensor.c(sensor_class)
 *
 *  <i> = index of connection.
 *
 *  Return the name of a particular connection (source name).
 *
 */

name_class* connector_class::get_name(int i)
{
     /*  Check for proper index.  */

  if(i >= 0 && i < num_pointers)
	return names[i];
  else
    {
      cmd_error("*** connector_class::get_name ***");
      cmd_error("bad index: ", i);
      exit(0);
    }
}


/*
 * int connector_class::get_num_connections()        //OVERLOAD CALL: get_num_connections: connector.c(connector_class), reverse_connector.c(reverse_connector_input_class), reverse_connector.c(reverse_connector_output_class)
 *
 *  Return the number of connections.
 *
 */

int connector_class::get_num_connections()
{
	return num_pointers;
}

/*
 * short connector_class::get_flags()        //OVERLOAD CALL: get_flags: connector.c(connector_class), reverse_connector.c(reverse_connector_output_class)
 *
 *  Return the configuration flags.
 *
 */

short connector_class::get_flags()
{
  return flags;
}


/*
 * void connector_class::report_state(nsl_vector* v1,        //OVERLOAD CALL: report_state: connector.c(connector_class), reverse_connector.c(reverse_connector_input_class), reverse_connector.c(reverse_connector_output_class)
 *                                    nsl_vector* v2,
 *				      nsl_vector* v3,
 *				      nsl_vector* v4)
 *
 *  Report the state of each connection.  Name, and 4 columns
 *   of values, as determined by the parent function.
 *
 */

void connector_class::report_state(nsl_vector* v1,
				   nsl_vector* v2,
				   nsl_vector* v3,
				   nsl_vector* v4)
{
  int i;
  for(i = 0; i < num_pointers; ++i)
    {
      cout << names[i] << "\t" ;
      if(v1 == NULL)
	cout tab;
      else
	cout << v1->elem(i) tab;
      if(v2 == NULL)
	cout tab;
      else
	cout << v2->elem(i) tab;
      if(v3 == NULL)
	cout tab;
      else
	cout << v3->elem(i) tab;
      if(v4 == NULL)
	cout tab;
      else
	cout << v4->elem(i) tab;
      cout nl;
    }
}


int connector_class::stuff(ifstream& netfile)
{
  int size;
  int i;
  char tmp[STRSIZE];
  float w;
  nsl_data* ptr;
  name_class* name;

  netfile >> size;		/* Number of connections */

  if(size < 0)			/* Legal size? */
    {
      cout << "connector_class::stuff(): Illegal size read from file." nl;        //OVERLOAD CALL: stuff: gate.c(gate_class), layer.c(layer_class), column.c(column_class), connector.c(connector_class)
      return 0;
    }
				/* Loop for each connection */
  for(i = 0; i < size; ++i)
    {
      netfile >> tmp;		/* Get the pre-synaptic name */
      netfile >> w;		/* Weight of connection */

				/* Get the corresponding object and name */
				/*  object. */
      if(!(ptr = translate_string_to_pointer_name(tmp, &name)))
	{
	  cout << "connector_class::stuff(): unable to translate name into object: " << tmp nl;        //OVERLOAD CALL: stuff: gate.c(gate_class), layer.c(layer_class), column.c(column_class), connector.c(connector_class)
	  return 0;
	}
				/* Create the connection. */
      add_connection(ptr, name, w);        //OVERLOAD CALL: add_connection: connector.c(connector_class), reverse_connector.c(reverse_connector_input_class), reverse_connector.c(reverse_connector_output_class)
    }
				/* All ok. */
  return 1;
};


[ return ]