/* * 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; };