def _update_default_variable(self, new_default_variable, context): from psyneulink.core.components.projections.pathway.mappingprojection import MappingProjection from psyneulink.core.components.ports.parameterport import ParameterPort # this mirrors the transformation in _function # it is a hack, and a general solution should be found squeezed = np.array(new_default_variable) if squeezed.ndim > 1: squeezed = np.squeeze(squeezed) size = safe_len(squeezed) matrix = self.parameters.matrix._get(context) if isinstance(matrix, MappingProjection): matrix = matrix._parameter_ports[MATRIX] elif isinstance(matrix, ParameterPort): pass else: matrix = get_matrix(self.defaults.matrix, size, size) self.parameters.matrix._set(matrix, context) self._hollow_matrix = get_matrix(HOLLOW_MATRIX, size, size) super()._update_default_variable(new_default_variable, context)
def _instantiate_attributes_before_function(self, function=None, context=None): """Instantiate matrix Specified matrix is convolved with HOLLOW_MATRIX to eliminate the diagonal (self-connections) from the calculation. The `Distance` Function is used for all calculations except ENERGY (which is not really a distance metric). If ENTROPY is specified as the metric, convert to CROSS_ENTROPY for use with the Distance Function. :param function: """ from psyneulink.core.components.projections.pathway.mappingprojection import MappingProjection from psyneulink.core.components.ports.parameterport import ParameterPort # this mirrors the transformation in _function # it is a hack, and a general solution should be found squeezed = np.array(self.defaults.variable) if squeezed.ndim > 1: squeezed = np.squeeze(squeezed) size = safe_len(squeezed) matrix = self.parameters.matrix._get(context) if isinstance(matrix, MappingProjection): matrix = matrix._parameter_ports[MATRIX] elif isinstance(matrix, ParameterPort): pass else: matrix = get_matrix(matrix, size, size) self.parameters.matrix._set(matrix, context) self._hollow_matrix = get_matrix(HOLLOW_MATRIX, size, size) default_variable = [self.defaults.variable, self.defaults.variable] if self.metric is ENTROPY: self.metric_fct = Distance(default_variable=default_variable, metric=CROSS_ENTROPY, normalize=self.normalize) elif self.metric in DISTANCE_METRICS._set(): self.metric_fct = Distance(default_variable=default_variable, metric=self.metric, normalize=self.normalize) else: assert False, "Unknown metric" #FIXME: This is a hack to make sure metric-fct param is set self.parameters.metric_fct.set(self.metric_fct)
def get_hetero_matrix(raw_hetero, size): if isinstance(raw_hetero, numbers.Number): return get_matrix(HOLLOW_MATRIX, size, size) * raw_hetero elif ((isinstance(raw_hetero, np.ndarray) and raw_hetero.ndim == 1) or (isinstance(raw_hetero, list) and np.array(raw_hetero).ndim == 1)): if len(raw_hetero) != 1: return None return get_matrix(HOLLOW_MATRIX, size, size) * raw_hetero[0] elif (isinstance(raw_hetero, np.matrix) or (isinstance(raw_hetero, np.ndarray) and raw_hetero.ndim == 2) or (isinstance(raw_hetero, list) and np.array(raw_hetero).ndim == 2)): np.fill_diagonal(raw_hetero, 0) return np.array(raw_hetero) else: return None
def _instantiate_attributes_before_function(self, function=None, context=None): """Instantiate matrix Specified matrix is convolved with HOLLOW_MATRIX to eliminate the diagonal (self-connections) from the calculation. The `Distance` Function is used for all calculations except ENERGY (which is not really a distance metric). If ENTROPY is specified as the metric, convert to CROSS_ENTROPY for use with the Distance Function. :param function: """ size = len(self.defaults.variable) from psyneulink.core.components.projections.pathway.mappingprojection import MappingProjection from psyneulink.core.components.states.parameterstate import ParameterState matrix = self.parameters.matrix.get() if isinstance(matrix, MappingProjection): matrix = matrix._parameter_states[MATRIX] elif isinstance(matrix, ParameterState): pass else: matrix = get_matrix(matrix, size, size) self.parameters.matrix.set(matrix) self._hollow_matrix = get_matrix(HOLLOW_MATRIX, size, size) default_variable = [self.defaults.variable, self.defaults.variable] if self.metric is ENTROPY: self._metric_fct = Distance(default_variable=default_variable, metric=CROSS_ENTROPY, normalize=self.normalize) elif self.metric in DISTANCE_METRICS._set(): self._metric_fct = Distance(default_variable=default_variable, metric=self.metric, normalize=self.normalize)
def test_recurrent_mech_matrix_keyword_spec(self, matrix): if matrix == RANDOM_CONNECTIVITY_MATRIX: pytest.skip("Random test") R = RecurrentTransferMechanism( name='R', size=4, matrix=matrix ) val = R.execute([10, 10, 10, 10]) np.testing.assert_allclose(val, [[10., 10., 10., 10.]]) np.testing.assert_allclose(R.recurrent_projection.matrix, get_matrix(matrix, R.size[0], R.size[0]))
def _instantiate_parameter_states(self, function=None, context=None): super()._instantiate_parameter_states(function=function, context=context) # FIX: UPDATE FOR LEARNING # FIX: UPDATE WITH MODULATION_MODS # FIX: MOVE THIS TO MappingProjection.__init__; # FIX: AS IT IS, OVER-WRITES USER ASSIGNMENT OF FUNCTION IN params dict FOR MappingProjection matrix = get_matrix(self._parameter_states[MATRIX].value) initial_rate = matrix * 0.0 self._parameter_states[MATRIX].function = AccumulatorIntegrator( owner=self._parameter_states[MATRIX], default_variable=matrix, initializer=matrix, # rate=initial_rate )
def _validate_params(self, request_set, target_set=None, context=None): """Validate **mask** argument""" super()._validate_params(request_set=request_set, target_set=target_set, context=context) if MASK in target_set and target_set[MASK]: mask = target_set[MASK] if isinstance(mask, (int, float)): return mask_shape = np.array(mask).shape matrix = get_matrix(self.user_params[FUNCTION_PARAMS][MATRIX], len(self.sender.defaults.value), len(self.receiver.defaults.value)) matrix_shape = matrix.shape if mask_shape != matrix_shape: raise MaskedMappingProjectionError("Shape of the {} for {} ({}) " "must be the same as its {} ({})". format(repr(MASK), self.name, mask_shape, repr(MATRIX), matrix_shape))
def _instantiate_parameter_ports(self, function=None, context=None): super()._instantiate_parameter_ports(function=function, context=context) # FIX: UPDATE FOR LEARNING # FIX: UPDATE WITH MODULATION_MODS # FIX: MOVE THIS TO MappingProjection.__init__; # FIX: AS IT IS, OVER-WRITES USER ASSIGNMENT OF FUNCTION IN params dict FOR MappingProjection matrix = get_matrix(self._parameter_ports[MATRIX].value) initial_rate = matrix * 0.0 # KDM 7/11/19: instead of simply setting the function, we need to reinstantiate to ensure # new defaults get set properly self._parameter_ports[MATRIX]._instantiate_function( function=AccumulatorIntegrator( owner=self._parameter_ports[MATRIX], default_variable=matrix, initializer=matrix, # rate=initial_rate ), context=context) self._parameter_ports[MATRIX]._instantiate_value(context) self._parameter_ports[MATRIX]._update_parameter_components(context)
def _instantiate_receiver(self, context=None): """Determine matrix needed to map from sender to receiver Assign specification to self.matrix_spec attribute Assign matrix to self.matrix attribute """ self.reshapedWeightMatrix = False # Get sender and receiver lengths # Note: if either is a scalar, manually set length to 1 to avoid TypeError in call to len() try: mapping_input_len = len(self.defaults.variable) except TypeError: mapping_input_len = 1 try: receiver_len = self.receiver.socket_width except TypeError: receiver_len = 1 # Compare length of MappingProjection output and receiver's variable to be sure matrix has proper dimensions try: mapping_output_len = len(self.defaults.value) except TypeError: mapping_output_len = 1 # FIX: CONVERT ALL REFS TO matrix_spec TO self._matrix_spec # FIX: CREATE @PROPERTY FOR self._learning_spec AND ASSIGN IN INIT?? # FIX: HOW DOES mapping_output_len RELATE TO receiver_len?/ if self._matrix_spec is AUTO_ASSIGN_MATRIX: if mapping_input_len == receiver_len: self._matrix_spec = IDENTITY_MATRIX else: self._matrix_spec = FULL_CONNECTIVITY_MATRIX # Length of the output of the Projection doesn't match the length of the receiving InputPort # so consider reshaping the matrix if mapping_output_len != receiver_len: if 'projection' in self.name or 'Projection' in self.name: projection_string = '' else: projection_string = 'projection' if all(string in self.name for string in {'from', 'to'}): states_string = '' else: states_string = "from \'{}\' OuputState of \'{}\' to \'{}\'".format( self.sender.name, self.sender.owner.name, self.receiver.owner.name) if not isinstance(self._matrix_spec, str): # if all(string in self.name for string in {'from', 'to'}): raise ProjectionError( "Width ({}) of the {} of \'{}{}\'{} " "does not match the length of its \'{}\' InputPort ({})". format(mapping_output_len, VALUE, self.name, projection_string, states_string, self.receiver.name, receiver_len)) elif self._matrix_spec == IDENTITY_MATRIX or self._matrix_spec == HOLLOW_MATRIX: # Identity matrix is not reshapable raise ProjectionError( "Output length ({}) of \'{}{}\' from {} to Mechanism \'{}\'" " must equal length of it InputPort ({}) to use {}".format( mapping_output_len, self.name, projection_string, self.sender.name, self.receiver.owner.name, receiver_len, self._matrix_spec)) else: # Flag that matrix is being reshaped self.reshapedWeightMatrix = True if self.prefs.verbosePref: print( "Length ({}) of the output of {}{} does not match the length ({}) " "of the InputPort for the receiver {}; the width of the matrix (number of columns); " "the width of the matrix (number of columns) will be adjusted to accomodate the receiver" .format(mapping_output_len, self.name, projection_string, receiver_len, self.receiver.owner.name)) self.matrix = get_matrix(self._matrix_spec, mapping_input_len, receiver_len, context=context) # Since matrix shape has changed, output of self.function may have changed, so update value self._instantiate_value(context=context) super()._instantiate_receiver(context=context)
def _validate_params(self, variable, request_set, target_set=None, context=None): """Validate matrix param `matrix <Stability.matrix>` argument must be one of the following - 2d list, np.ndarray or np.matrix - ParameterPort for one of the above - MappingProjection with a parameterPorts[MATRIX] for one of the above Parse matrix specification to insure it resolves to a square matrix (but leave in the form in which it was specified so that, if it is a ParameterPort or MappingProjection, its current value can be accessed at runtime (i.e., it can be used as a "pointer") """ # Validate matrix specification if MATRIX in target_set: from psyneulink.core.components.projections.pathway.mappingprojection import MappingProjection from psyneulink.core.components.ports.parameterport import ParameterPort matrix = target_set[MATRIX] if isinstance(matrix, str): matrix = get_matrix(matrix) if isinstance(matrix, MappingProjection): try: matrix = matrix._parameter_ports[MATRIX].value param_type_string = "MappingProjection's ParameterPort" except KeyError: raise FunctionError( "The MappingProjection specified for the {} arg of {} ({}) must have a {} " "ParameterPort that has been assigned a 2d array or matrix" .format(MATRIX, self.name, matrix.shape, MATRIX)) elif isinstance(matrix, ParameterPort): try: matrix = matrix.value param_type_string = "ParameterPort" except KeyError: raise FunctionError( "The value of the {} parameterPort specified for the {} arg of {} ({}) " "must be a 2d array or matrix".format( MATRIX, MATRIX, self.name, matrix.shape)) else: param_type_string = "array or matrix" matrix = np.array(matrix) if matrix.ndim != 2: raise FunctionError( "The value of the {} specified for the {} arg of {} ({}) " "must be a 2d array or matrix".format( param_type_string, MATRIX, self.name, matrix)) rows = matrix.shape[0] cols = matrix.shape[1] size = len(self.defaults.variable) if rows != size: raise FunctionError( "The value of the {} specified for the {} arg of {} is the wrong size;" "it is {}x{}, but must be square matrix of size {}".format( param_type_string, MATRIX, self.name, rows, cols, size)) if rows != cols: raise FunctionError( "The value of the {} specified for the {} arg of {} ({}) " "must be a square matrix".format(param_type_string, MATRIX, self.name, matrix)) super()._validate_params(request_set=request_set, target_set=target_set, context=context)