diff --git a/README.rst b/README.rst index e0adfa0ea65..5c28d03cb86 100644 --- a/README.rst +++ b/README.rst @@ -130,9 +130,9 @@ switch to your preferred python3 environment, then run Dependencies that are automatically installed (except those noted as optional) include: - * numpy (version <1.16) + * numpy * matplotlib - * toposort (version 1.4) + * toposort * typecheck-decorator (version 1.2) * pillow * llvmlite diff --git a/dev_requirements.txt b/dev_requirements.txt index 8f28cf65afa..a0352c6e221 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,7 +1,6 @@ ipykernel ipython jupyter -nbconvert<6 psyneulink-sphinx-theme pytest pytest-benchmark diff --git a/docs/source/BasicsAndPrimer.rst b/docs/source/BasicsAndPrimer.rst index bf8687335f6..1c37fac22ce 100644 --- a/docs/source/BasicsAndPrimer.rst +++ b/docs/source/BasicsAndPrimer.rst @@ -723,7 +723,8 @@ a `ModulatorySignal ` belonging to a `ModulatoryMechanism ` or a `Projection ` is modulable, it is assigned a `ParameterPort` -- this is a Component that belongs to the Mechanism or Projection and can receive a Projection from a ModulatorySignal, allowing another component to modulate the value of the parameter. ParameterPorts are created for every modulable parameter of -a Mechanism or its `function `, and similarly for Projections. These determine the value +a Mechanism, its `function `, any of its +secondary functions, and similarly for Projections. These determine the value of the parameter that is actually used when the Component is executed, which may be different than the base value returned by accessing the parameter directly (as in the examples above); see `ModulatorySignal_Modulation` for a more complete description of modulation. The current *modulated* value of a parameter can be accessed from the `value @@ -738,8 +739,9 @@ the `value ` of the ParameterPort for the parameter:: >>> task.mod_gain [0.62] -This works for any modulable parameters of the Mechanism or its `function `. Note that, -here, neither the ``parameters`` nor the ``function`` atributes of the Mechanism need to be included in the reference. +This works for any modulable parameters of the Mechanism, its +`function `, or secondary functions. Note that, +here, neither the ``parameters`` nor the ``function`` attributes of the Mechanism need to be included in the reference. Note also that, as explained above, the value returned is different from the base value of the function's gain parameter:: diff --git a/docs/source/index.rst b/docs/source/index.rst index c59e47c2d7d..fdffc23f5d6 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -153,9 +153,9 @@ switch to your preferred python3 environment, then run Dependencies that are automatically installed (except those noted as optional) include: - * numpy (version < 1.16) + * numpy * matplotlib - * toposort (version 1.4) + * toposort * typecheck-decorator (version 1.2) * pillow * llvmlite diff --git a/docs/source/index_logo_with_text.rst b/docs/source/index_logo_with_text.rst index 920aeed9e29..97d0748a170 100644 --- a/docs/source/index_logo_with_text.rst +++ b/docs/source/index_logo_with_text.rst @@ -153,9 +153,9 @@ switch to your preferred python3 environment, then run Dependencies that are automatically installed (except those noted as optional) include: - * numpy (version < 1.16) + * numpy * matplotlib - * toposort (version 1.4) + * toposort * typecheck-decorator (version 1.2) * pillow * llvmlite diff --git a/psyneulink/core/components/component.py b/psyneulink/core/components/component.py index 7292a309f23..a1a990fbf0f 100644 --- a/psyneulink/core/components/component.py +++ b/psyneulink/core/components/component.py @@ -1132,7 +1132,15 @@ def __init__(self, isinstance(param, SharedParameter) and not isinstance(param.source, ParameterAlias) ): - self.initial_shared_parameters[param.attribute_name][param.shared_parameter_name] = param.default_value + try: + if parameter_values[param_name] is not None: + isp_val = parameter_values[param_name] + else: + isp_val = copy.deepcopy(param.default_value) + except KeyError: + isp_val = copy.deepcopy(param.default_value) + + self.initial_shared_parameters[param.attribute_name][param.shared_parameter_name] = isp_val # we must know the final variable shape before setting up parameter # Functions or they will mismatch @@ -1311,7 +1319,7 @@ def _get_compilation_params(self): # Reference to other components "objective_mechanism", "agent_rep", "projections", # Shape mismatch - "costs", "auto", "hetero", + "auto", "hetero", "cost", "costs", "combined_costs", # autodiff specific types "pytorch_representation", "optimizer"} # Mechanism's need few extra entires: @@ -1345,12 +1353,12 @@ def llvm_param_ids(self): def _is_param_modulated(self, p): try: - if p.name in self.owner.parameter_ports: + if p in self.owner.parameter_ports: return True except AttributeError: pass try: - if p.name in self.parameter_ports: + if p in self.parameter_ports: return True except AttributeError: pass @@ -1676,7 +1684,8 @@ def _assign_default_name(self, **kwargs): return def _set_parameter_value(self, param, val, context=None): - getattr(self.parameters, param)._set(val, context) + param = getattr(self.parameters, param) + param._set(val, context) if hasattr(self, "parameter_ports"): if param in self.parameter_ports: new_port_value = self.parameter_ports[param].execute( @@ -1685,7 +1694,16 @@ def _set_parameter_value(self, param, val, context=None): self.parameter_ports[param].parameters.value._set(new_port_value, context) elif hasattr(self, "owner"): if hasattr(self.owner, "parameter_ports"): + # skip Components, assume they are to be run to provide the + # value instead of given as a variable to a parameter port if param in self.owner.parameter_ports: + try: + if any([isinstance(v, Component) for v in val]): + return + except TypeError: + if isinstance(val, Component): + return + new_port_value = self.owner.parameter_ports[param].execute( context=Context(execution_phase=ContextFlags.EXECUTING, execution_id=context.execution_id) ) @@ -1982,7 +2000,7 @@ def _is_user_specified(parameter): else: param_defaults[p.source.name] = param_defaults[p.name] - for p in filter(lambda x: not isinstance(x, ParameterAlias), self.parameters): + for p in filter(lambda x: not isinstance(x, (ParameterAlias, SharedParameter)), self.parameters): p._user_specified = _is_user_specified(p) # copy spec so it is not overwritten later @@ -2816,6 +2834,11 @@ def _instantiate_function(self, function, function_params=None, context=None): except KeyError: pass + try: + kwargs_to_instantiate.update(self.initial_shared_parameters[FUNCTION]) + except KeyError: + pass + # matrix is determined from ParameterPort based on string value in function_params # update it here if needed if MATRIX in kwargs_to_instantiate: @@ -2840,9 +2863,20 @@ def _instantiate_function(self, function, function_params=None, context=None): def _instantiate_attributes_after_function(self, context=None): if hasattr(self, "_parameter_ports"): + shared_params = [p for p in self.parameters if isinstance(p, (ParameterAlias, SharedParameter))] + sources = [p.source for p in shared_params] + for param_port in self._parameter_ports: - setattr(self.__class__, "mod_" + param_port.name, make_property_mod(param_port.name)) - setattr(self.__class__, "get_mod_" + param_port.name, make_stateful_getter_mod(param_port.name)) + property_names = {param_port.name} + try: + alias_index = sources.index(param_port.source) + property_names.add(shared_params[alias_index].name) + except ValueError: + pass + + for property_name in property_names: + setattr(self.__class__, "mod_" + property_name, make_property_mod(property_name, param_port.name)) + setattr(self.__class__, "get_mod_" + property_name, make_stateful_getter_mod(property_name, param_port.name)) def _instantiate_value(self, context=None): # - call self.execute to get value, since the value of a Component is defined as what is returned by its @@ -3009,13 +3043,59 @@ def is_finished(self, context=None): return self.parameters.is_finished_flag._get(context) def _parse_param_port_sources(self): - try: + if hasattr(self, '_parameter_ports'): for param_port in self._parameter_ports: - if param_port.source == FUNCTION: - param_port.source = self.function - except AttributeError: + try: + orig_source = param_port.source + param_port.source = param_port.source(self) + del self.parameter_ports.parameter_mapping[orig_source] + self.parameter_ports.parameter_mapping[param_port.source] = param_port + except TypeError: + pass + + def _get_current_parameter_value(self, parameter, context=None): + from psyneulink.core.components.ports.parameterport import ParameterPortError + + if parameter == "variable" or parameter == self.parameters.variable: + raise ComponentError( + f"The method '_get_current_parameter_value' is intended for retrieving the current " + f"value of a modulable parameter; 'variable' is not a modulable parameter. If looking " + f"for {self.name}'s default variable, try '{self.name}.defaults.variable'." + ) + + try: + parameter = getattr(self.parameters, parameter) + # just fail now if string and no corresponding parameter (AttributeError) + except TypeError: pass + parameter_port_list = None + try: + # parameter is SharedParameter and ultimately points to + # something with a corresponding ParameterPort + parameter_port_list = parameter.final_source._owner._owner.parameter_ports + except AttributeError: + # prefer parameter ports from self over owner + try: + parameter_port_list = self._parameter_ports + except AttributeError: + try: + parameter_port_list = self.owner._parameter_ports + except AttributeError: + pass + + if parameter_port_list is not None: + try: + return parameter_port_list[parameter].parameters.value._get(context) + # *parameter* string or Parameter didn't correspond to a parameter port + except TypeError: + pass + except ParameterPortError as e: + if 'Multiple ParameterPorts' in str(e): + raise + + return parameter._get(context) + def _increment_execution_count(self, count=1): self.parameters.execution_count.set(self.execution_count + count, override=True) return self.execution_count @@ -3260,7 +3340,8 @@ def _dict_summary(self): from psyneulink.core.compositions.composition import Composition from psyneulink.core.components.ports.port import Port from psyneulink.core.components.ports.outputport import OutputPort - from psyneulink.core.components.projections.pathway.mappingprojection import MappingProjection + from psyneulink.core.components.ports.parameterport import ParameterPortError + from psyneulink.core.components.functions.transferfunctions import LinearMatrix def parse_parameter_value(value): if isinstance(value, (list, tuple)): @@ -3336,14 +3417,14 @@ def parse_parameter_value(value): # class default val = p.default_value else: - # special handling because MappingProjection matrix just - # refers to its function's matrix but its default values are - # PNL-specific + # special handling because LinearMatrix default values + # can be PNL-specific keywords. In future, generalize + # this workaround if ( - isinstance(self, MappingProjection) + isinstance(self, LinearMatrix) and p.name == 'matrix' ): - val = self.function.defaults.matrix + val = self.parameters.matrix.values[None] elif p.spec is not None: val = p.spec else: @@ -3354,7 +3435,7 @@ def parse_parameter_value(value): try: matching_parameter_port = self.owner.parameter_ports[p.name] - if matching_parameter_port.source is self: + if matching_parameter_port.source._owner._owner is self: val = { MODEL_SPEC_ID_PARAMETER_SOURCE: '{0}.{1}.{2}'.format( self.owner.name, @@ -3365,7 +3446,7 @@ def parse_parameter_value(value): MODEL_SPEC_ID_TYPE: type(val) } # ContentAddressableList uses TypeError when key not found - except (AttributeError, TypeError): + except (AttributeError, TypeError, ParameterPortError): pass # split parameters designated as PsyNeuLink-specific and @@ -3533,11 +3614,13 @@ def _model_spec_parameter_blacklist(self): COMPONENT_BASE_CLASS = Component -def make_property_mod(param_name): +def make_property_mod(param_name, parameter_port_name=None): + if parameter_port_name is None: + parameter_port_name = param_name def getter(self): try: - return self._parameter_ports[param_name].value + return self._parameter_ports[parameter_port_name].value except TypeError: raise ComponentError("{} does not have a '{}' ParameterPort." .format(self.name, param_name)) @@ -3551,11 +3634,13 @@ def setter(self, value): return prop -def make_stateful_getter_mod(param_name): +def make_stateful_getter_mod(param_name, parameter_port_name=None): + if parameter_port_name is None: + parameter_port_name = param_name def getter(self, context=None): try: - return self._parameter_ports[param_name].parameters.value.get(context) + return self._parameter_ports[parameter_port_name].parameters.value.get(context) except TypeError: raise ComponentError("{} does not have a '{}' ParameterPort." .format(self.name, param_name)) diff --git a/psyneulink/core/components/functions/combinationfunctions.py b/psyneulink/core/components/functions/combinationfunctions.py index 0a0fae9e70b..fac23daaad1 100644 --- a/psyneulink/core/components/functions/combinationfunctions.py +++ b/psyneulink/core/components/functions/combinationfunctions.py @@ -271,8 +271,8 @@ def _function(self, in an array that is one dimension less than `variable `. """ - scale = self._get_current_function_param(SCALE, context) - offset = self._get_current_function_param(OFFSET, context) + scale = self._get_current_parameter_value(SCALE, context) + offset = self._get_current_parameter_value(OFFSET, context) result = np.hstack(variable) * scale + offset @@ -537,8 +537,8 @@ def _function(self, """ variable = np.atleast_2d(variable) - scale = self._get_current_function_param(SCALE, context) - offset = self._get_current_function_param(OFFSET, context) + scale = self._get_current_parameter_value(SCALE, context) + offset = self._get_current_parameter_value(OFFSET, context) arrangement = self.parameters.arrangement.get(context) if arrangement is None: @@ -820,11 +820,11 @@ def _function(self, """ - weights = self._get_current_function_param(WEIGHTS, context) - exponents = self._get_current_function_param(EXPONENTS, context) - operation = self._get_current_function_param(OPERATION, context) - scale = self._get_current_function_param(SCALE, context) - offset = self._get_current_function_param(OFFSET, context) + weights = self._get_current_parameter_value(WEIGHTS, context) + exponents = self._get_current_parameter_value(EXPONENTS, context) + operation = self._get_current_parameter_value(OPERATION, context) + scale = self._get_current_parameter_value(SCALE, context) + offset = self._get_current_parameter_value(OFFSET, context) # FIX FOR EFFICIENCY: CHANGE THIS AND WEIGHTS TO TRY/EXCEPT // OR IS IT EVEN NECESSARY, GIVEN VALIDATION ABOVE?? # Apply exponents if they were specified @@ -854,7 +854,7 @@ def _function(self, result = np.product(np.atleast_2d(variable), axis=1) * scale + offset else: raise FunctionError("Unrecognized operator ({0}) for Reduce function". - format(self._get_current_function_param(OPERATION, context))) + format(self._get_current_parameter_value(OPERATION, context))) return self.convert_output_type(result) @@ -1305,16 +1305,16 @@ def _function(self, the result of linearly combining the arrays in `variable `. """ - weights = self._get_current_function_param(WEIGHTS, context) - exponents = self._get_current_function_param(EXPONENTS, context) + weights = self._get_current_parameter_value(WEIGHTS, context) + exponents = self._get_current_parameter_value(EXPONENTS, context) # if self.initialization_status == ContextFlags.INITIALIZED: # if weights is not None and weights.shape != variable.shape: # weights = weights.reshape(variable.shape) # if exponents is not None and exponents.shape != variable.shape: # exponents = exponents.reshape(variable.shape) - operation = self._get_current_function_param(OPERATION, context) - scale = self._get_current_function_param(SCALE, context) - offset = self._get_current_function_param(OFFSET, context) + operation = self._get_current_parameter_value(OPERATION, context) + scale = self._get_current_parameter_value(SCALE, context) + offset = self._get_current_parameter_value(OFFSET, context) # QUESTION: WHICH IS LESS EFFICIENT: # A) UNECESSARY ARITHMETIC OPERATIONS IF SCALE AND/OR OFFSET ARE 1.0 AND 0, RESPECTIVELY? @@ -1808,11 +1808,11 @@ def _function(self, the result of taking the means of each array in `variable ` and combining them. """ - exponents = self._get_current_function_param(EXPONENTS, context) - weights = self._get_current_function_param(WEIGHTS, context) - operation = self._get_current_function_param(OPERATION, context) - offset = self._get_current_function_param(OFFSET, context) - scale = self._get_current_function_param(SCALE, context) + exponents = self._get_current_parameter_value(EXPONENTS, context) + weights = self._get_current_parameter_value(WEIGHTS, context) + operation = self._get_current_parameter_value(OPERATION, context) + offset = self._get_current_parameter_value(OFFSET, context) + scale = self._get_current_parameter_value(SCALE, context) # QUESTION: WHICH IS LESS EFFICIENT: # A) UNECESSARY ARITHMETIC OPERATIONS IF SCALE AND/OR OFFSET ARE 1.0 AND 0, RESPECTIVELY? @@ -1859,7 +1859,7 @@ def _function(self, else: raise FunctionError("Unrecognized operator ({0}) for CombineMeans function". - format(self._get_current_function_param(OPERATION, context))) + format(self._get_current_parameter_value(OPERATION, context))) return self.convert_output_type(result) @@ -2043,7 +2043,7 @@ def _function(self, delta values : 1d np.array """ - gamma = self._get_current_function_param(GAMMA, context) + gamma = self._get_current_parameter_value(GAMMA, context) sample = variable[0] reward = variable[1] delta = np.zeros(sample.shape) diff --git a/psyneulink/core/components/functions/distributionfunctions.py b/psyneulink/core/components/functions/distributionfunctions.py index 225412a5454..325aa8c8e6a 100644 --- a/psyneulink/core/components/functions/distributionfunctions.py +++ b/psyneulink/core/components/functions/distributionfunctions.py @@ -188,9 +188,9 @@ def _function(self, context=None, params=None, ): - mean = self._get_current_function_param(DIST_MEAN, context) - standard_deviation = self._get_current_function_param(STANDARD_DEVIATION, context) - random_state = self._get_current_function_param("random_state", context) + mean = self._get_current_parameter_value(DIST_MEAN, context) + standard_deviation = self._get_current_parameter_value(STANDARD_DEVIATION, context) + random_state = self._get_current_parameter_value("random_state", context) result = random_state.normal(mean, standard_deviation) @@ -372,9 +372,9 @@ def _function(self, except: raise FunctionError("The UniformToNormalDist function requires the SciPy package.") - mean = self._get_current_function_param(DIST_MEAN, context) - standard_deviation = self._get_current_function_param(STANDARD_DEVIATION, context) - random_state = self._get_current_function_param('random_state', context) + mean = self._get_current_parameter_value(DIST_MEAN, context) + standard_deviation = self._get_current_parameter_value(STANDARD_DEVIATION, context) + random_state = self._get_current_parameter_value('random_state', context) sample = random_state.rand(1)[0] result = ((np.sqrt(2) * erfinv(2 * sample - 1)) * standard_deviation) + mean @@ -494,8 +494,8 @@ def _function(self, context=None, params=None, ): - random_state = self._get_current_function_param('random_state', context) - beta = self._get_current_function_param(BETA, context) + random_state = self._get_current_parameter_value('random_state', context) + beta = self._get_current_parameter_value(BETA, context) result = random_state.exponential(beta) @@ -626,9 +626,9 @@ def _function(self, params=None, ): - random_state = self._get_current_function_param('random_state', context) - low = self._get_current_function_param(LOW, context) - high = self._get_current_function_param(HIGH, context) + random_state = self._get_current_parameter_value('random_state', context) + low = self._get_current_parameter_value(LOW, context) + high = self._get_current_parameter_value(HIGH, context) result = random_state.uniform(low, high) return self.convert_output_type(result) @@ -763,9 +763,9 @@ def _function(self, params=None, ): - random_state = self._get_current_function_param('random_state', context) - scale = self._get_current_function_param(SCALE, context) - dist_shape = self._get_current_function_param(DIST_SHAPE, context) + random_state = self._get_current_parameter_value('random_state', context) + scale = self._get_current_parameter_value(SCALE, context) + dist_shape = self._get_current_parameter_value(DIST_SHAPE, context) result = random_state.gamma(dist_shape, scale) @@ -899,9 +899,9 @@ def _function(self, params=None, ): - random_state = self._get_current_function_param('random_state', context) - scale = self._get_current_function_param(SCALE, context) - mean = self._get_current_function_param(DIST_MEAN, context) + random_state = self._get_current_parameter_value('random_state', context) + scale = self._get_current_parameter_value(SCALE, context) + mean = self._get_current_parameter_value(DIST_MEAN, context) result = random_state.wald(mean, scale) @@ -1204,13 +1204,13 @@ def _function(self, """ - attentional_drift_rate = float(self._get_current_function_param(DRIFT_RATE, context)) + attentional_drift_rate = float(self._get_current_parameter_value(DRIFT_RATE, context)) stimulus_drift_rate = float(variable) drift_rate = attentional_drift_rate * stimulus_drift_rate - threshold = self._get_current_function_param(THRESHOLD, context) - starting_point = float(self._get_current_function_param(STARTING_POINT, context)) - noise = float(self._get_current_function_param(NOISE, context)) - t0 = float(self._get_current_function_param(NON_DECISION_TIME, context)) + threshold = self._get_current_parameter_value(THRESHOLD, context) + starting_point = float(self._get_current_parameter_value(STARTING_POINT, context)) + noise = float(self._get_current_parameter_value(NOISE, context)) + t0 = float(self._get_current_parameter_value(NON_DECISION_TIME, context)) # drift_rate = float(self.drift_rate) * float(variable) # threshold = float(self.threshold) @@ -1699,9 +1699,9 @@ def derivative(self, output=None, input=None, context=None): `. """ - Z = output or self._get_current_function_param(THRESHOLD, context) - A = input or self._get_current_function_param(DRIFT_RATE, context) - c = self._get_current_function_param(NOISE, context) + Z = output or self._get_current_parameter_value(THRESHOLD, context) + A = input or self._get_current_parameter_value(DRIFT_RATE, context) + c = self._get_current_parameter_value(NOISE, context) c_sq = c ** 2 E = np.exp(-2 * Z * A / c_sq) D_iti = 0 diff --git a/psyneulink/core/components/functions/function.py b/psyneulink/core/components/functions/function.py index 22fdd03c01e..2281f566b45 100644 --- a/psyneulink/core/components/functions/function.py +++ b/psyneulink/core/components/functions/function.py @@ -617,18 +617,17 @@ def _validate_parameter_spec(self, param, param_name, numeric_only=True): raise FunctionError(f"{param} is not a valid specification for " f"the {param_name} argument of {self.__class__.__name__}{owner_name}.") - def _get_current_function_param(self, param_name, context=None): - if param_name == "variable": - raise FunctionError(f"The method '_get_current_function_param' is intended for retrieving " - f"the current value of a function parameter. 'variable' is not a function parameter. " - f"If looking for {self.name}'s default variable, try {self.name}.defaults.variable.") + def _get_current_parameter_value(self, param_name, context=None): try: - return self.owner._parameter_ports[param_name].parameters.value._get(context) - except (AttributeError, TypeError): - try: - return getattr(self.parameters, param_name)._get(context) - except AttributeError: - raise FunctionError(f"{self} has no parameter '{param_name}'.") + param = getattr(self.parameters, param_name) + except TypeError: + param = param_name + except AttributeError: + # don't accept strings that don't correspond to Parameters + # on this function + raise + + return super()._get_current_parameter_value(param, context) def get_previous_value(self, context=None): # temporary method until previous values are integrated for all parameters @@ -914,8 +913,8 @@ def _function(self, """ # Compute the function statement = variable - propensity = self._get_current_function_param(PROPENSITY, context) - pertinacity = self._get_current_function_param(PERTINACITY, context) + propensity = self._get_current_parameter_value(PROPENSITY, context) + pertinacity = self._get_current_parameter_value(PERTINACITY, context) whim = np.random.randint(-10, 10) if propensity == self.Manner.OBSEQUIOUS: diff --git a/psyneulink/core/components/functions/learningfunctions.py b/psyneulink/core/components/functions/learningfunctions.py index 2e6515085fb..84fba33190e 100644 --- a/psyneulink/core/components/functions/learningfunctions.py +++ b/psyneulink/core/components/functions/learningfunctions.py @@ -573,11 +573,11 @@ def _function( # MODIFIED 10/26/18 END # Today's prior is yesterday's posterior - Lambda_prior = self._get_current_function_param('Lambda_n', context) - mu_prior = self._get_current_function_param('mu_n', context) + Lambda_prior = self._get_current_parameter_value('Lambda_n', context) + mu_prior = self._get_current_parameter_value('mu_n', context) # # MODIFIED 6/3/19 OLD: [JDC]: THE FOLLOWING ARE YOTAM'S ADDITION (NOT in FALK's CODE) - # gamma_shape_prior = self._get_current_function_param('gamma_shape_n', context) - # gamma_size_prior = self._get_current_function_param('gamma_size_n', context) + # gamma_shape_prior = self._get_current_parameter_value('gamma_shape_n', context) + # gamma_size_prior = self._get_current_parameter_value('gamma_size_n', context) # MODIFIED 6/3/19 NEW: gamma_shape_prior = self.parameters.gamma_shape_n.default_value gamma_size_prior = self.parameters.gamma_size_n.default_value @@ -617,7 +617,7 @@ def sample_weights(self, gamma_shape_n, gamma_size_n, mu_n, Lambda_n, context): `Lambda_n `, `gamma_shape_n `, and `gamma_size_n `. """ - random_state = self._get_current_function_param('random_state', context) + random_state = self._get_current_parameter_value('random_state', context) phi = random_state.gamma(gamma_shape_n / 2, gamma_size_n / 2) return random_state.multivariate_normal(mu_n.reshape(-1,), phi * np.linalg.inv(Lambda_n)) @@ -871,7 +871,7 @@ def _function(self, # 2) if neither the system nor the process assigns a value to the learning_rate, # then need to assign it to the default value # If learning_rate was not specified for instance or composition, use default value - learning_rate = self._get_current_function_param(LEARNING_RATE, context) + learning_rate = self._get_current_parameter_value(LEARNING_RATE, context) if learning_rate is None: learning_rate = self.defaults.learning_rate @@ -1100,7 +1100,7 @@ def _function(self, # 2) if neither the system nor the process assigns a value to the learning_rate, # then need to assign it to the default value # If learning_rate was not specified for instance or composition, use default value - learning_rate = self._get_current_function_param(LEARNING_RATE, context) + learning_rate = self._get_current_parameter_value(LEARNING_RATE, context) # learning_rate = self.learning_rate if learning_rate is None: learning_rate = self.defaults.learning_rate @@ -1331,7 +1331,7 @@ def _function(self, # 2) if neither the system nor the process assigns a value to the learning_rate, # then need to assign it to the default value # If learning_rate was not specified for instance or composition, use default value - learning_rate = self._get_current_function_param(LEARNING_RATE, context) + learning_rate = self._get_current_parameter_value(LEARNING_RATE, context) if learning_rate is None: learning_rate = self.defaults.learning_rate @@ -1632,9 +1632,9 @@ def _function(self, self._check_args(variable=variable, context=context, params=params) - output = self._get_current_function_param(ACTIVATION_OUTPUT, context) - error = self._get_current_function_param(ERROR_SIGNAL, context) - learning_rate = self._get_current_function_param(LEARNING_RATE, context) + output = self._get_current_parameter_value(ACTIVATION_OUTPUT, context) + error = self._get_current_parameter_value(ERROR_SIGNAL, context) + learning_rate = self._get_current_parameter_value(LEARNING_RATE, context) # IMPLEMENTATION NOTE: have to do this here, rather than in validate_params for the following reasons: # 1) if no learning_rate is specified for the Mechanism, need to assign None @@ -2105,26 +2105,26 @@ def _function(self, # 2) if neither the system nor the process assigns a value to the learning_rate, # then need to assign it to the default value # If learning_rate was not specified for instance or composition, use default value - learning_rate = self._get_current_function_param(LEARNING_RATE, context) + learning_rate = self._get_current_parameter_value(LEARNING_RATE, context) if learning_rate is None: learning_rate = self.defaults.learning_rate # make activation_input a 1D row array - activation_input = self._get_current_function_param(ACTIVATION_INPUT, context) + activation_input = self._get_current_parameter_value(ACTIVATION_INPUT, context) activation_input = np.array(activation_input).reshape(len(activation_input), 1) # Derivative of error with respect to output activity (contribution of each output unit to the error above) loss_function = self.parameters.loss_function.get(context) if loss_function == MSE: - num_output_units = self._get_current_function_param(ERROR_SIGNAL, context).shape[0] - dE_dA = np.dot(error_matrix, self._get_current_function_param(ERROR_SIGNAL, context)) / num_output_units * 2 + num_output_units = self._get_current_parameter_value(ERROR_SIGNAL, context).shape[0] + dE_dA = np.dot(error_matrix, self._get_current_parameter_value(ERROR_SIGNAL, context)) / num_output_units * 2 elif loss_function == SSE: - dE_dA = np.dot(error_matrix, self._get_current_function_param(ERROR_SIGNAL, context)) * 2 + dE_dA = np.dot(error_matrix, self._get_current_parameter_value(ERROR_SIGNAL, context)) * 2 else: - dE_dA = np.dot(error_matrix, self._get_current_function_param(ERROR_SIGNAL, context)) + dE_dA = np.dot(error_matrix, self._get_current_parameter_value(ERROR_SIGNAL, context)) # Derivative of the output activity - activation_output = self._get_current_function_param(ACTIVATION_OUTPUT, context) + activation_output = self._get_current_parameter_value(ACTIVATION_OUTPUT, context) # FIX: THIS ASSUMES DERIVATIVE CAN BE COMPUTED FROM output OF FUNCTION (AS IT CAN FOR THE Logistic) dA_dW = self.activation_derivative_fct(input=None, output=activation_output, context=context) diff --git a/psyneulink/core/components/functions/objectivefunctions.py b/psyneulink/core/components/functions/objectivefunctions.py index 4ba632bb7c9..d32a0febeee 100644 --- a/psyneulink/core/components/functions/objectivefunctions.py +++ b/psyneulink/core/components/functions/objectivefunctions.py @@ -456,7 +456,7 @@ def _function(self, variable = np.squeeze(variable) # MODIFIED 6/12/19 END - matrix = self._get_current_function_param(MATRIX, context) + matrix = self._get_current_parameter_value(MATRIX, context) current = variable diff --git a/psyneulink/core/components/functions/optimizationfunctions.py b/psyneulink/core/components/functions/optimizationfunctions.py index a6729fdb16b..836e3f89c0b 100644 --- a/psyneulink/core/components/functions/optimizationfunctions.py +++ b/psyneulink/core/components/functions/optimizationfunctions.py @@ -1735,7 +1735,7 @@ def _function(self, # swap with probability = 1/optimal_value_count in order to achieve # uniformly random selection from identical outcomes probability = 1 / optimal_value_count - random_state = self._get_current_function_param("random_state", context) + random_state = self._get_current_parameter_value("random_state", context) random_value = random_state.rand() if random_value < probability: @@ -2457,7 +2457,7 @@ def function(self, # number of simulations. N is not the total number of simulation # samples. We will return a random sample from this set for the # "optimal" control allocation. - random_state = self._get_current_function_param("random_state", context) + random_state = self._get_current_parameter_value("random_state", context) sample_idx = random_state.randint(low=0, high=result.n_samples) return_optimal_sample = np.array(result.samples_array[sample_idx]) return_optimal_value = result.discrepancies[sample_idx] diff --git a/psyneulink/core/components/functions/selectionfunctions.py b/psyneulink/core/components/functions/selectionfunctions.py index 9ab2cd66d7f..700d7c9e395 100644 --- a/psyneulink/core/components/functions/selectionfunctions.py +++ b/psyneulink/core/components/functions/selectionfunctions.py @@ -422,7 +422,7 @@ def _function(self, if not prob_dist.any(): return self.convert_output_type(v) cum_sum = np.cumsum(prob_dist) - random_state = self._get_current_function_param("random_state", context) + random_state = self._get_current_parameter_value("random_state", context) random_value = random_state.uniform() chosen_item = next(element for element in cum_sum if element > random_value) chosen_in_cum_sum = np.where(cum_sum == chosen_item, 1, 0) diff --git a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py index 17a2fcc7053..6ba0764cd8a 100644 --- a/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py +++ b/psyneulink/core/components/functions/statefulfunctions/integratorfunctions.py @@ -653,9 +653,9 @@ def _function(self, warnings.warn("{} does not use its variable; value passed ({}) will be ignored". format(self.__class__.__name__, variable)) - rate = self._get_current_function_param(RATE, context) - increment = self._get_current_function_param(INCREMENT, context) - noise = self._try_execute_param(self._get_current_function_param(NOISE, context), variable) + rate = self._get_current_parameter_value(RATE, context) + increment = self._get_current_parameter_value(INCREMENT, context) + noise = self._try_execute_param(self._get_current_parameter_value(NOISE, context), variable) previous_value = np.atleast_2d(self.get_previous_value(context)) @@ -850,12 +850,12 @@ def _function(self, updated value of integral : 2d array """ - rate = np.array(self._get_current_function_param(RATE, context)).astype(float) + rate = np.array(self._get_current_parameter_value(RATE, context)).astype(float) - offset = self._get_current_function_param(OFFSET, context) + offset = self._get_current_parameter_value(OFFSET, context) # execute noise if it is a function - noise = self._try_execute_param(self._get_current_function_param(NOISE, context), variable) + noise = self._try_execute_param(self._get_current_parameter_value(NOISE, context), variable) previous_value = self.get_previous_value(context) new_value = variable @@ -1190,10 +1190,10 @@ def _function(self, updated value of integral : ndarray (dimension equal to variable) """ - rate = np.array(self._get_current_function_param(RATE, context)).astype(float) - offset = self._get_current_function_param(OFFSET, context) + rate = np.array(self._get_current_parameter_value(RATE, context)).astype(float) + offset = self._get_current_parameter_value(OFFSET, context) # execute noise if it is a function - noise = self._try_execute_param(self._get_current_function_param(NOISE, context), variable) + noise = self._try_execute_param(self._get_current_parameter_value(NOISE, context), variable) # # MODIFIED 6/14/19 OLD: # previous_value = np.atleast_2d(self.get_previous_value(context)) @@ -1695,11 +1695,11 @@ def _function(self, updated value of integral : 2d array """ - # rate = np.array(self._get_current_function_param(RATE, context)).astype(float) + # rate = np.array(self._get_current_parameter_value(RATE, context)).astype(float) # execute noise if it is a function - # noise = self._try_execute_param(self._get_current_function_param(NOISE, context), variable) - short_term_rate = self._get_current_function_param("short_term_rate", context) - long_term_rate = self._get_current_function_param("long_term_rate", context) + # noise = self._try_execute_param(self._get_current_parameter_value(NOISE, context), variable) + short_term_rate = self._get_current_parameter_value("short_term_rate", context) + long_term_rate = self._get_current_parameter_value("long_term_rate", context) # Integrate Short Term Utility: short_term_avg = self._EWMA_filter(short_term_rate, @@ -1720,13 +1720,13 @@ def _function(self, def _combine_terms(self, short_term_avg, long_term_avg, context=None): - short_term_gain = self._get_current_function_param("short_term_gain", context) - short_term_bias = self._get_current_function_param("short_term_bias", context) - long_term_gain = self._get_current_function_param("long_term_gain", context) - long_term_bias = self._get_current_function_param("long_term_bias", context) - rate = self._get_current_function_param(RATE, context) - operation = self._get_current_function_param(OPERATION, context) - offset = self._get_current_function_param(OFFSET, context) + short_term_gain = self._get_current_parameter_value("short_term_gain", context) + short_term_bias = self._get_current_parameter_value("short_term_bias", context) + long_term_gain = self._get_current_parameter_value("long_term_gain", context) + long_term_bias = self._get_current_parameter_value("long_term_bias", context) + rate = self._get_current_parameter_value(RATE, context) + operation = self._get_current_parameter_value(OPERATION, context) + offset = self._get_current_parameter_value(OFFSET, context) # s = 2*rate if rate <= 0.5 else 1 # l = 2-(2*rate) if rate >= 0.5 else 1 @@ -1775,9 +1775,9 @@ def reset(self, short=None, long=None, context=NotImplemented): context.execution_id = self.most_recent_context.execution_id if short is None: - short = self._get_current_function_param("initial_short_term_avg", context) + short = self._get_current_parameter_value("initial_short_term_avg", context) if long is None: - long = self._get_current_function_param("initial_long_term_avg", context) + long = self._get_current_parameter_value("initial_long_term_avg", context) self.parameters.previous_short_term_avg.set(short, context) self.parameters.previous_long_term_avg.set(long, context) @@ -2089,15 +2089,15 @@ def _function(self, variable=None, context=None, params=None): updated value of integral : 2d array """ - rate = np.array(self._get_current_function_param(RATE, context)).astype(float) - decay = np.array(self._get_current_function_param(DECAY, context)).astype(float) - rest = np.array(self._get_current_function_param(REST, context)).astype(float) + rate = np.array(self._get_current_parameter_value(RATE, context)).astype(float) + decay = np.array(self._get_current_parameter_value(DECAY, context)).astype(float) + rest = np.array(self._get_current_parameter_value(REST, context)).astype(float) # FIX: only works with "max_val". Keyword MAX_VAL = "MAX_VAL", not max_val - max_val = np.array(self._get_current_function_param("max_val", context)).astype(float) - min_val = np.array(self._get_current_function_param("min_val", context)).astype(float) + max_val = np.array(self._get_current_parameter_value("max_val", context)).astype(float) + min_val = np.array(self._get_current_parameter_value("min_val", context)).astype(float) # execute noise if it is a function - noise = self._try_execute_param(self._get_current_function_param(NOISE, context), variable) + noise = self._try_execute_param(self._get_current_parameter_value(NOISE, context), variable) current_input = variable @@ -2473,12 +2473,12 @@ def _function(self, updated value of integral : 2d array """ - rate = np.array(self._get_current_function_param(RATE, context)).astype(float) - noise = self._get_current_function_param(NOISE, context) - offset = self._get_current_function_param(OFFSET, context) - threshold = self._get_current_function_param(THRESHOLD, context) - time_step_size = self._get_current_function_param(TIME_STEP_SIZE, context) - random_state = self._get_current_function_param("random_state", context) + rate = np.array(self._get_current_parameter_value(RATE, context)).astype(float) + noise = self._get_current_parameter_value(NOISE, context) + offset = self._get_current_parameter_value(OFFSET, context) + threshold = self._get_current_parameter_value(THRESHOLD, context) + time_step_size = self._get_current_parameter_value(TIME_STEP_SIZE, context) + random_state = self._get_current_parameter_value("random_state", context) previous_value = np.atleast_2d(self.get_previous_value(context)) @@ -2491,7 +2491,7 @@ def _function(self, # If this NOT an initialization run, update the old value and time # If it IS an initialization run, leave as is # (don't want to count it as an execution step) - previous_time = self._get_current_function_param('previous_time', context) + previous_time = self._get_current_parameter_value('previous_time', context) if not self.is_initializing: previous_value = adjusted_value previous_time = previous_time + time_step_size @@ -2891,12 +2891,12 @@ def _function(self, updated value of integral : 2d array """ - rate = np.array(self._get_current_function_param(RATE, context)).astype(float) - decay = self._get_current_function_param(DECAY, context) - noise = self._get_current_function_param(NOISE, context) - offset = self._get_current_function_param(OFFSET, context) - time_step_size = self._get_current_function_param(TIME_STEP_SIZE, context) - random_state = self._get_current_function_param('random_state', context) + rate = np.array(self._get_current_parameter_value(RATE, context)).astype(float) + decay = self._get_current_parameter_value(DECAY, context) + noise = self._get_current_parameter_value(NOISE, context) + offset = self._get_current_parameter_value(OFFSET, context) + time_step_size = self._get_current_parameter_value(TIME_STEP_SIZE, context) + random_state = self._get_current_parameter_value('random_state', context) previous_value = np.atleast_2d(self.get_previous_value(context)) @@ -2911,7 +2911,7 @@ def _function(self, # (don't want to count it as an execution step) adjusted_value = value + offset - previous_time = self._get_current_function_param('previous_time', context) + previous_time = self._get_current_parameter_value('previous_time', context) if not self.is_initializing: previous_value = adjusted_value previous_time = previous_time + time_step_size @@ -3166,13 +3166,13 @@ def _function(self, updated value of integral : 2d array """ - rate = np.atleast_1d(self._get_current_function_param(RATE, context)) - initializer = self._get_current_function_param(INITIALIZER, context) # unnecessary? - time_step_size = self._get_current_function_param(TIME_STEP_SIZE, context) - offset = self._get_current_function_param(OFFSET, context) + rate = np.atleast_1d(self._get_current_parameter_value(RATE, context)) + initializer = self._get_current_parameter_value(INITIALIZER, context) # unnecessary? + time_step_size = self._get_current_parameter_value(TIME_STEP_SIZE, context) + offset = self._get_current_parameter_value(OFFSET, context) # execute noise if it is a function - noise = self._try_execute_param(self._get_current_function_param(NOISE, context), variable) + noise = self._try_execute_param(self._get_current_parameter_value(NOISE, context), variable) previous_value = self.get_previous_value(context) new_value = variable @@ -4059,7 +4059,7 @@ def _runge_kutta_4_FitzHughNagumo( return new_v, new_w def dv_dt(self, variable, time, v, w, a_v, threshold, b_v, c_v, d_v, e_v, f_v, time_constant_v, context=None): - previous_w = self._get_current_function_param('previous_w', context) + previous_w = self._get_current_parameter_value('previous_w', context) val = (a_v * (v ** 3) + (1 + threshold) * b_v * (v ** 2) + (-threshold) * c_v * v + d_v + e_v * previous_w + f_v * variable) / time_constant_v @@ -4071,7 +4071,7 @@ def dv_dt(self, variable, time, v, w, a_v, threshold, b_v, c_v, d_v, e_v, f_v, t return val def dw_dt(self, variable, time, w, v, mode, a_w, b_w, c_w, uncorrelated_activity, time_constant_w, context=None): - previous_v = self._get_current_function_param('previous_v', context) + previous_v = self._get_current_parameter_value('previous_v', context) # val = np.ones_like(variable)*(mode*a_w*self.previous_v + b_w*w + c_w + (1-mode)*uncorrelated_activity)/time_constant_w val = (mode * a_w * previous_v + b_w * w + c_w + (1 - mode) * uncorrelated_activity) / time_constant_w @@ -4108,30 +4108,30 @@ def _function(self, """ - # FIX: SHOULDN'T THERE BE A CALL TO _get_current_function_param('variable', context) HERE?? + # FIX: SHOULDN'T THERE BE A CALL TO _get_current_parameter_value('variable', context) HERE?? # # FIX: TEMPORARY CHECK UNTIL ARRAY IS SUPPORTED # if variable is not None and not np.isscalar(variable) and len(variable)>1: # raise FunctionError("{} presently supports only a scalar variable".format(self.__class__.__name__)) - a_v = self._get_current_function_param("a_v", context) - b_v = self._get_current_function_param("b_v", context) - c_v = self._get_current_function_param("c_v", context) - d_v = self._get_current_function_param("d_v", context) - e_v = self._get_current_function_param("e_v", context) - f_v = self._get_current_function_param("f_v", context) - time_constant_v = self._get_current_function_param("time_constant_v", context) - threshold = self._get_current_function_param("threshold", context) - a_w = self._get_current_function_param("a_w", context) - b_w = self._get_current_function_param("b_w", context) - c_w = self._get_current_function_param("c_w", context) - uncorrelated_activity = self._get_current_function_param("uncorrelated_activity", context) - time_constant_w = self._get_current_function_param("time_constant_w", context) - mode = self._get_current_function_param("mode", context) - time_step_size = self._get_current_function_param(TIME_STEP_SIZE, context) - previous_v = self._get_current_function_param("previous_v", context) - previous_w = self._get_current_function_param("previous_w", context) - previous_time = self._get_current_function_param("previous_time", context) + a_v = self._get_current_parameter_value("a_v", context) + b_v = self._get_current_parameter_value("b_v", context) + c_v = self._get_current_parameter_value("c_v", context) + d_v = self._get_current_parameter_value("d_v", context) + e_v = self._get_current_parameter_value("e_v", context) + f_v = self._get_current_parameter_value("f_v", context) + time_constant_v = self._get_current_parameter_value("time_constant_v", context) + threshold = self._get_current_parameter_value("threshold", context) + a_w = self._get_current_parameter_value("a_w", context) + b_w = self._get_current_parameter_value("b_w", context) + c_w = self._get_current_parameter_value("c_w", context) + uncorrelated_activity = self._get_current_parameter_value("uncorrelated_activity", context) + time_constant_w = self._get_current_parameter_value("time_constant_w", context) + mode = self._get_current_parameter_value("mode", context) + time_step_size = self._get_current_parameter_value(TIME_STEP_SIZE, context) + previous_v = self._get_current_parameter_value("previous_v", context) + previous_w = self._get_current_parameter_value("previous_w", context) + previous_time = self._get_current_parameter_value("previous_time", context) # integration_method is a compile time parameter integration_method = self.parameters.integration_method.get() diff --git a/psyneulink/core/components/functions/statefulfunctions/memoryfunctions.py b/psyneulink/core/components/functions/statefulfunctions/memoryfunctions.py index d33d15549bb..b375772cae9 100644 --- a/psyneulink/core/components/functions/statefulfunctions/memoryfunctions.py +++ b/psyneulink/core/components/functions/statefulfunctions/memoryfunctions.py @@ -269,7 +269,7 @@ def reset(self, *args, context=None): # no arguments were passed in -- use current values of initializer attributes if len(args) == 0 or args is None: - reinitialization_value = self._get_current_function_param("initializer", context) + reinitialization_value = self._get_current_parameter_value("initializer", context) elif len(args) == 1: reinitialization_value = args[0] @@ -316,10 +316,10 @@ def _function(self, updated value of deque : deque """ - rate = np.array(self._get_current_function_param(RATE, context)).astype(float) + rate = np.array(self._get_current_parameter_value(RATE, context)).astype(float) # execute noise if it is a function - noise = self._try_execute_param(self._get_current_function_param(NOISE, context), variable) + noise = self._try_execute_param(self._get_current_parameter_value(NOISE, context), variable) # If this is an initialization run, leave deque empty (don't want to count it as an execution step); # Just return current input (for validation). @@ -949,7 +949,7 @@ def _validate(self, context=None): selection_function = self.selection_function test_var = np.asfarray([distance_result if i==0 else np.zeros_like(distance_result) - for i in range(self._get_current_function_param('max_entries', context))]) + for i in range(self._get_current_parameter_value('max_entries', context))]) if isinstance(selection_function, type): selection_function = selection_function(default_variable=test_var, context=context) fct_string = 'Function type' @@ -1028,7 +1028,7 @@ def reset(self, *args, context=None): # no arguments were passed in -- use current values of initializer attributes if len(args) == 0 or args is None: - reinitialization_value = self._get_current_function_param("initializer", context) + reinitialization_value = self._get_current_parameter_value("initializer", context) elif len(args) == 1: reinitialization_value = args[0] @@ -1083,14 +1083,14 @@ def _function(self, # if len(variable)==2: val = variable[VALS] - retrieval_prob = np.array(self._get_current_function_param(RETRIEVAL_PROB, context)).astype(float) - storage_prob = np.array(self._get_current_function_param(STORAGE_PROB, context)).astype(float) + retrieval_prob = np.array(self._get_current_parameter_value(RETRIEVAL_PROB, context)).astype(float) + storage_prob = np.array(self._get_current_parameter_value(STORAGE_PROB, context)).astype(float) # execute noise if it is a function - noise = self._try_execute_param(self._get_current_function_param(NOISE, context), variable) + noise = self._try_execute_param(self._get_current_parameter_value(NOISE, context), variable) # get random state - random_state = self._get_current_function_param('random_state', context) + random_state = self._get_current_parameter_value('random_state', context) # If this is an initialization run, leave memory empty (don't want to count it as an execution step), # and return current value (variable[1]) for validation. @@ -1194,7 +1194,7 @@ def get_memory(self, query_key:tc.any(list, np.ndarray), context=None): return [[0]* self.parameters.key_size._get(context), [0]* self.parameters.val_size._get(context)] if self.equidistant_keys_select == RANDOM: - random_state = self._get_current_function_param('random_state', context) + random_state = self._get_current_parameter_value('random_state', context) index_of_selected_item = random_state.choice(indices_of_selected_items) elif self.equidistant_keys_select == OLDEST: index_of_selected_item = indices_of_selected_items[0] diff --git a/psyneulink/core/components/functions/statefulfunctions/statefulfunction.py b/psyneulink/core/components/functions/statefulfunctions/statefulfunction.py index 63b7239b542..cbd4f95d3c1 100644 --- a/psyneulink/core/components/functions/statefulfunctions/statefulfunction.py +++ b/psyneulink/core/components/functions/statefulfunctions/statefulfunction.py @@ -301,7 +301,7 @@ def _validate_params(self, request_set, target_set=None, context=None): def _validate_initializers(self, default_variable, context=None): for initial_value_name in self.initializers: - initial_value = self._get_current_function_param(initial_value_name, context=context) + initial_value = self._get_current_parameter_value(initial_value_name, context=context) if isinstance(initial_value, (list, np.ndarray)): if len(initial_value) != 1: @@ -389,12 +389,6 @@ def _validate_noise(self, noise): raise FunctionError("The elements of a noise list or array must be scalars or functions. " "{} is not a valid noise element for {}".format(noise[i], self.name)) - # Otherwise, must be a float, int or function - elif noise is not None and not isinstance(noise, (float, int)) and not callable(noise): - raise FunctionError( - "Noise parameter ({}) for {} must be a float, function, or array/list of these." - .format(noise, self.name)) - def _try_execute_param(self, param, var, context=None): # FIX: [JDC 12/18/18 - HACK TO DEAL WITH ENFORCEMENT OF 2D BELOW] @@ -511,13 +505,13 @@ def reset(self, *args, context=None): if len(args) == 0 or args is None or all(arg is None for arg in args): for i in range(len(self.initializers)): initializer_name = self.initializers[i] - reinitialization_values.append(self._get_current_function_param(initializer_name, context)) + reinitialization_values.append(self._get_current_parameter_value(initializer_name, context)) elif len(args) == len(self.initializers): for i in range(len(self.initializers)): initializer_name = self.initializers[i] if args[i] is None: - reinitialization_values.append(self._get_current_function_param(initializer_name, context)) + reinitialization_values.append(self._get_current_parameter_value(initializer_name, context)) else: # Not sure if np.atleast_1d is necessary here: reinitialization_values.append(np.atleast_1d(args[i])) diff --git a/psyneulink/core/components/functions/transferfunctions.py b/psyneulink/core/components/functions/transferfunctions.py index 853c8e36eb8..72de681925d 100644 --- a/psyneulink/core/components/functions/transferfunctions.py +++ b/psyneulink/core/components/functions/transferfunctions.py @@ -430,8 +430,8 @@ def _function(self, linear transformation of variable : number or array """ - slope = self._get_current_function_param(SLOPE, context) - intercept = self._get_current_function_param(INTERCEPT, context) + slope = self._get_current_parameter_value(SLOPE, context) + intercept = self._get_current_parameter_value(INTERCEPT, context) # MODIFIED 11/9/17 NEW: try: @@ -478,7 +478,7 @@ def derivative(self, input=None, output=None, context=None): """ - return self._get_current_function_param(SLOPE, context) + return self._get_current_parameter_value(SLOPE, context) def _is_identity(self, context=None): return ( @@ -701,10 +701,10 @@ def _function(self, Exponential transformation of variable : number or array """ - rate = self._get_current_function_param(RATE, context) - bias = self._get_current_function_param(BIAS, context) - scale = self._get_current_function_param(SCALE, context) - offset = self._get_current_function_param(OFFSET, context) + rate = self._get_current_parameter_value(RATE, context) + bias = self._get_current_parameter_value(BIAS, context) + scale = self._get_current_parameter_value(SCALE, context) + offset = self._get_current_parameter_value(OFFSET, context) # The following doesn't work with autograd (https://github.com/HIPS/autograd/issues/416) # result = scale * np.exp(rate * variable + bias) + offset @@ -731,9 +731,9 @@ def derivative(self, input, output=None, context=None): """ - rate = self._get_current_function_param(RATE, context) - scale = self._get_current_function_param(SCALE, context) - bias = self._get_current_function_param(BIAS, context) + rate = self._get_current_parameter_value(RATE, context) + scale = self._get_current_parameter_value(SCALE, context) + bias = self._get_current_parameter_value(BIAS, context) return rate * scale * e**(rate * input + bias) @@ -988,11 +988,11 @@ def _function(self, Logistic transformation of variable : number or array """ - gain = self._get_current_function_param(GAIN, context) - bias = self._get_current_function_param(BIAS, context) - x_0 = self._get_current_function_param(X_0, context) - offset = self._get_current_function_param(OFFSET, context) - scale = self._get_current_function_param(SCALE, context) + gain = self._get_current_parameter_value(GAIN, context) + bias = self._get_current_parameter_value(BIAS, context) + x_0 = self._get_current_parameter_value(X_0, context) + offset = self._get_current_parameter_value(OFFSET, context) + scale = self._get_current_parameter_value(SCALE, context) # The following doesn't work with autograd (https://github.com/HIPS/autograd/issues/416) # result = 1. / (1 + np.exp(-gain * (variable - bias) + offset)) @@ -1037,8 +1037,8 @@ def derivative(self, input=None, output=None, context=None): format(repr('output'), self.__class__.__name__ + '.' + 'derivative', output, repr('input'), input)) - gain = self._get_current_function_param(GAIN, context) - scale = self._get_current_function_param(SCALE, context) + gain = self._get_current_parameter_value(GAIN, context) + scale = self._get_current_parameter_value(SCALE, context) if output is None: output = self.function(input, context=context) @@ -1307,11 +1307,11 @@ def _function(self, hyperbolic tangent of variable : number or array """ - gain = self._get_current_function_param(GAIN, context) - bias = self._get_current_function_param(BIAS, context) - x_0 = self._get_current_function_param(X_0, context) - offset = self._get_current_function_param(OFFSET, context) - scale = self._get_current_function_param(SCALE, context) + gain = self._get_current_parameter_value(GAIN, context) + bias = self._get_current_parameter_value(BIAS, context) + x_0 = self._get_current_parameter_value(X_0, context) + offset = self._get_current_parameter_value(OFFSET, context) + scale = self._get_current_parameter_value(SCALE, context) # The following probably doesn't work with autograd (https://github.com/HIPS/autograd/issues/416) # (since np.exp doesn't work) @@ -1340,11 +1340,11 @@ def derivative(self, input, output=None, context=None): derivative : number or array """ - gain = self._get_current_function_param(GAIN, context) - bias = self._get_current_function_param(BIAS, context) - x_0 = self._get_current_function_param(X_0, context) - offset = self._get_current_function_param(OFFSET, context) - scale = self._get_current_function_param(SCALE, context) + gain = self._get_current_parameter_value(GAIN, context) + bias = self._get_current_parameter_value(BIAS, context) + x_0 = self._get_current_parameter_value(X_0, context) + offset = self._get_current_parameter_value(OFFSET, context) + scale = self._get_current_parameter_value(SCALE, context) exponent = -2 * (gain * (input + bias - x_0) + offset) mult = -2 * gain * scale @@ -1513,9 +1513,9 @@ def _function(self, ReLU transformation of variable : number or array """ - gain = self._get_current_function_param(GAIN, context) - bias = self._get_current_function_param(BIAS, context) - leak = self._get_current_function_param(LEAK, context) + gain = self._get_current_parameter_value(GAIN, context) + bias = self._get_current_parameter_value(BIAS, context) + leak = self._get_current_parameter_value(LEAK, context) # KAM modified 2/15/19 to match https://en.wikipedia.org/wiki/Rectifier_(neural_networks)#Leaky_ReLUs x = gain * (variable - bias) @@ -1570,8 +1570,8 @@ def derivative(self, input, output=None, context=None): derivative : number or array """ - gain = self._get_current_function_param(GAIN, context) - leak = self._get_current_function_param(LEAK, context) + gain = self._get_current_parameter_value(GAIN, context) + leak = self._get_current_parameter_value(LEAK, context) input = np.asarray(input).copy() input[input>0] = gain @@ -1801,10 +1801,10 @@ def _function(self, Gaussian transformation of variable : number or array """ - standard_deviation = self._get_current_function_param(STANDARD_DEVIATION, context) - bias = self._get_current_function_param(BIAS, context) - scale = self._get_current_function_param(SCALE, context) - offset = self._get_current_function_param(OFFSET, context) + standard_deviation = self._get_current_parameter_value(STANDARD_DEVIATION, context) + bias = self._get_current_parameter_value(BIAS, context) + scale = self._get_current_parameter_value(SCALE, context) + offset = self._get_current_parameter_value(OFFSET, context) gaussian = e**(-(variable - bias)**2 / (2 * standard_deviation**2)) / sqrt(2 * pi * standard_deviation) result = scale * gaussian + offset @@ -1832,8 +1832,8 @@ def derivative(self, input, output=None, context=None): Derivative of Guassian of variable : number or array """ - sigma = self._get_current_function_param(STANDARD_DEVIATION, context) - bias = self._get_current_function_param(BIAS, context) + sigma = self._get_current_parameter_value(STANDARD_DEVIATION, context) + bias = self._get_current_parameter_value(BIAS, context) adjusted_input = input - bias result = (-adjusted_input * e**(-(adjusted_input**2 / (2 * sigma**2)))) / sqrt(2 * pi * sigma**3) @@ -2075,11 +2075,11 @@ def _function(self, Sample from Gaussian distribution for each element of variable : number or array """ - variance = self._get_current_function_param(VARIANCE, context) - bias = self._get_current_function_param(BIAS, context) - scale = self._get_current_function_param(SCALE, context) - offset = self._get_current_function_param(OFFSET, context) - random_state = self._get_current_function_param('random_state', context) + variance = self._get_current_parameter_value(VARIANCE, context) + bias = self._get_current_parameter_value(BIAS, context) + scale = self._get_current_parameter_value(SCALE, context) + offset = self._get_current_parameter_value(OFFSET, context) + random_state = self._get_current_parameter_value('random_state', context) # The following doesn't work with autograd (https://github.com/HIPS/autograd/issues/416) result = scale * random_state.normal(variable + bias, variance) + offset @@ -2101,10 +2101,10 @@ def _function(self, # Derivative of Guassian of variable : number or array # # """ - # variance = self._get_current_function_param(VARIANCE, context) - # bias = self._get_current_function_param(BIAS, context) - # scale = self._get_current_function_param(SCALE, context) - # offset = self._get_current_function_param(OFFSET, context) + # variance = self._get_current_parameter_value(VARIANCE, context) + # bias = self._get_current_parameter_value(BIAS, context) + # scale = self._get_current_parameter_value(SCALE, context) + # offset = self._get_current_parameter_value(OFFSET, context) # # # The following doesn't work with autograd (https://github.com/HIPS/autograd/issues/416) # f = scale * np.random.normal(input+bias, variance) + offset @@ -2471,9 +2471,9 @@ def _function(self, """ # Assign the params and return the result - output_type = self._get_current_function_param(OUTPUT_TYPE, context) - gain = self._get_current_function_param(GAIN, context) - per_item = self._get_current_function_param(PER_ITEM, context) + output_type = self._get_current_parameter_value(OUTPUT_TYPE, context) + gain = self._get_current_parameter_value(GAIN, context) + per_item = self._get_current_parameter_value(PER_ITEM, context) # Compute softmax and assign to sm if per_item and len(np.shape(variable)) > 1: @@ -3031,7 +3031,7 @@ def _function(self, length of the array returned equals the number of columns of `matrix `. """ - matrix = self._get_current_function_param(MATRIX, context) + matrix = self._get_current_parameter_value(MATRIX, context) result = np.dot(variable, matrix) return self.convert_output_type(result) @@ -3836,7 +3836,7 @@ def _function(self, if enabled_cost_functions: # For each cost function that is enabled: - # - get params for the cost functon using _get_current_function_param: + # - get params for the cost functon using _get_current_parameter_value: # - if TransferWithControl is owned by a Mechanism, get value from ParameterPort for param # - otherwise, get from TransferWithControl modulation parameter (which is also subject to modulation) @@ -3844,9 +3844,9 @@ def _function(self, if enabled_cost_functions & CostFunctions.INTENSITY: # Assign modulatory param values to intensity_cost_function self.intensity_cost_fct_mult_param = \ - self._get_current_function_param(INTENSITY_COST_FCT_MULTIPLICATIVE_PARAM, context) + self._get_current_parameter_value(INTENSITY_COST_FCT_MULTIPLICATIVE_PARAM, context) self.intensity_cost_fct_add_param = \ - self._get_current_function_param(INTENSITY_COST_FCT_ADDITIVE_PARAM, context) + self._get_current_parameter_value(INTENSITY_COST_FCT_ADDITIVE_PARAM, context) # Execute intensity_cost function intensity_cost = self.intensity_cost_fct(intensity, context=context) self.parameters.intensity_cost._set(intensity_cost, context) @@ -3861,9 +3861,9 @@ def _function(self, intensity_change = np.zeros_like(self.parameters_intensity._get(context)) # Assign modulatory param values to adjustment_cost_function self.adjustment_cost_fct_mult_param = \ - self._get_current_function_param(ADJUSTMENT_COST_FCT_MULTIPLICATIVE_PARAM, context) + self._get_current_parameter_value(ADJUSTMENT_COST_FCT_MULTIPLICATIVE_PARAM, context) self.adjustment_cost_fct_add_param = \ - self._get_current_function_param(ADJUSTMENT_COST_FCT_ADDITIVE_PARAM, context) + self._get_current_parameter_value(ADJUSTMENT_COST_FCT_ADDITIVE_PARAM, context) # Execute adjustment_cost function adjustment_cost = self.adjustment_cost_fct(intensity_change, context=context) self.parameters.adjustment_cost._set(adjustment_cost, context) @@ -3873,9 +3873,9 @@ def _function(self, if enabled_cost_functions & CostFunctions.DURATION: # Assign modulatory param values to duration_cost_function self.duration_cost_fct_mult_param = \ - self._get_current_function_param(DURATION_COST_FCT_MULTIPLICATIVE_PARAM, context) + self._get_current_parameter_value(DURATION_COST_FCT_MULTIPLICATIVE_PARAM, context) self.duration_cost_fct_add_param = \ - self._get_current_function_param(DURATION_COST_FCT_ADDITIVE_PARAM, context) + self._get_current_parameter_value(DURATION_COST_FCT_ADDITIVE_PARAM, context) # Execute duration_cost function duration_cost = self.duration_cost_fct(intensity, context=context) self.parameters.duration_cost._set(duration_cost, context) @@ -3885,9 +3885,9 @@ def _function(self, # Assign modulatory param values to combine_costs_function self.combine_costs_fct_mult_param = \ - self._get_current_function_param(COMBINE_COSTS_FCT_MULTIPLICATIVE_PARAM, context) + self._get_current_parameter_value(COMBINE_COSTS_FCT_MULTIPLICATIVE_PARAM, context) self.combine_costs_fct_add_param = \ - self._get_current_function_param(COMBINE_COSTS_FCT_ADDITIVE_PARAM, context) + self._get_current_parameter_value(COMBINE_COSTS_FCT_ADDITIVE_PARAM, context) # Execute combine_costs function combined_costs = self.combine_costs_fct(enabled_costs, context=context) diff --git a/psyneulink/core/components/functions/userdefinedfunction.py b/psyneulink/core/components/functions/userdefinedfunction.py index 6b4295ade27..6eb5a458e52 100644 --- a/psyneulink/core/components/functions/userdefinedfunction.py +++ b/psyneulink/core/components/functions/userdefinedfunction.py @@ -9,11 +9,10 @@ # # ***************************************** USER-DEFINED FUNCTION **************************************************** -import ctypes import numpy as np import typecheck as tc - -from inspect import signature, _empty +from inspect import signature, _empty, getsourcelines +import ast from psyneulink.core.components.component import ComponentError from psyneulink.core.components.functions.function import FunctionError, Function_Base @@ -284,6 +283,39 @@ class UserDefinedFunction(Function_Base): `Function_Modulatory_Params` for gating any InputPort or OutputPort to which the function is assigned (see `GatingMechanism_Specifying_Gating` and `GatingSignal_Examples`). +.. _UDF_Compilation: + + **Compiling a User Defined Function** + + User defined functions may also be `automatically compiled `, by adding them as a mechanism or projection function. + There are several restrictions to take into account: + +.. _UDF_Compilation_Restrictions: + + * *Lambda Functions* -- User defined functions currently do not support Python Lambda functions + + * *Loops* -- User defined functions currently do not support Loops + + * *Python Data Types* -- User defined functions currently do not support *dict* and *class* types + + * *Nested Functions* -- User defined functions currently do not support recursive calls, nested functions, or closures + + * *Slicing and comprehensions* -- User defined functions currently have limited support for slicing, and do not support comprehensions + + * *Libraries* -- User defined functions currently do not support libraries, aside from **NumPy** (with limited support) + +.. _UDF_Compilation_Numpy: + + **NumPy Support for Compiled User Defined Functions** + + Compiled User Defined Functions also provide access to limited compiled NumPy functionality; The supported features are listed as follows: + + * *Data Types* -- Numpy Arrays and Matrices are supported, as long as their dimensionality is less than 3. In addition, the elementwise multiplication and addition of NumPy arrays and matrices is fully supported + + * *Numerical functions* -- the `exp` and `tanh` methods are currently supported in compiled mode + + It is highly recommended that users who require additional functionality request it as an issue `here `_. + **Class Definition:** @@ -521,7 +553,7 @@ def _function(self, variable, context=None, **kwargs): self.cust_fct_params[param] = kwargs[PARAMS][param] else: # Otherwise, get current value from ParameterPort (in case it is being modulated by ControlSignal(s) - self.cust_fct_params[param] = self._get_current_function_param(param, context) + self.cust_fct_params[param] = self._get_current_parameter_value(param, context) call_params = self.cust_fct_params.copy() @@ -549,59 +581,15 @@ def _function(self, variable, context=None, **kwargs): def _gen_llvm_function_body(self, ctx, builder, params, state, arg_in, arg_out, *, tags:frozenset): - # Instantiate needed ctypes - arg_in_ct = pnlvm._convert_llvm_ir_to_ctype(arg_in.type.pointee) - params_ct = pnlvm._convert_llvm_ir_to_ctype(params.type.pointee) - state_ct = pnlvm._convert_llvm_ir_to_ctype(state.type.pointee) - arg_out_ct = pnlvm._convert_llvm_ir_to_ctype(arg_out.type.pointee) - wrapper_ct = ctypes.CFUNCTYPE(None, - ctypes.POINTER(params_ct), - ctypes.POINTER(state_ct), - ctypes.POINTER(arg_in_ct), - ctypes.POINTER(arg_out_ct)) - - # we don't support passing any stateful params - for i, p in enumerate(self.llvm_state_ids): - assert p not in self.cust_fct_params - - def _carr_to_list(carr): - try: - return [_carr_to_list(x) for x in carr] - except TypeError: - return carr - - def _assign_to_carr(carr, vals): - assert len(carr) == len(vals) - for i, x in enumerate(vals): - try: - carr[i] = x - except TypeError: - _assign_to_carr(carr[i], x) - - def _wrapper(params, state, arg_in, arg_out): - variable = _carr_to_list(arg_in.contents) - - llvm_params = {} - for i, p in enumerate(self.llvm_param_ids): - if p in self.cust_fct_params: - field_name = params.contents._fields_[i][0] - val = getattr(params.contents, field_name) - llvm_params[p] = val - - if self.context_arg: - # FIXME: We can't get the context - # and do not support runtime params - llvm_params[CONTEXT] = None - llvm_params[PARAMS] = None - - value = self.custom_function(np.asfarray(variable), **llvm_params) - _assign_to_carr(arg_out.contents, np.atleast_2d(value)) - - self.__wrapper_f = wrapper_ct(_wrapper) - # To get the right callback pointer, we need to cast to void* - wrapper_address = ctypes.cast(self.__wrapper_f, ctypes.c_void_p) - # Direct pointer constants don't work - wrapper_ptr = builder.inttoptr(pnlvm.ir.IntType(64)(wrapper_address.value), builder.function.type) - builder.call(wrapper_ptr, [params, state, arg_in, arg_out]) + srclines = getsourcelines(self.custom_function)[0] + # strip preceeding space characters + first_line = srclines[0] + prefix_len = len(first_line) - len(first_line.lstrip()) + formatted_src = ''.join(line[prefix_len:] for line in srclines) + func_ast = ast.parse(formatted_src) + + func_globals = self.custom_function.__globals__ + func_params = {param_id: pnlvm.helpers.get_param_ptr(builder, self, params, param_id) for param_id in self.llvm_param_ids} + pnlvm.codegen.UserDefinedFunctionVisitor(ctx, builder, func_globals, func_params, arg_in, arg_out).visit(func_ast) return builder diff --git a/psyneulink/core/components/mechanisms/mechanism.py b/psyneulink/core/components/mechanisms/mechanism.py index 037813425a1..76585c20d1f 100644 --- a/psyneulink/core/components/mechanisms/mechanism.py +++ b/psyneulink/core/components/mechanisms/mechanism.py @@ -2132,14 +2132,15 @@ def _instantiate_attributes_after_function(self, context=None): try: for param_name, param_value in self.function.cust_fct_params.items(): if param_name not in self.parameter_ports.names: + source_param = getattr(self.function.parameters, param_name) _instantiate_parameter_port( self, param_name, param_value, context=context, - function=self.function + function=self.function, + source=source_param, ) - self._parse_param_port_sources() except AttributeError: pass @@ -2284,16 +2285,6 @@ def reset(self, *args, force=False, context=None): raise MechanismError(f"Resetting '{self.name}' is not allowed because this Mechanism is not stateful; " f"it does not have an accumulator to reset.") - def _get_current_mechanism_param(self, param_name, context=None): - if param_name == "variable": - raise MechanismError(f"The method '_get_current_mechanism_param' is intended for retrieving the current " - f"value of a mechanism parameter; 'variable' is not a mechanism parameter. If looking " - f"for {self.name}'s default variable, try '{self.name}.defaults.variable'.") - try: - return self._parameter_ports[param_name].parameters.value._get(context) - except (AttributeError, TypeError): - return getattr(self.parameters, param_name)._get(context) - # when called externally, ContextFlags.PROCESSING is not set. Maintain this behavior here # even though it will not update input ports for example @handle_external_context(execution_phase=ContextFlags.IDLE) diff --git a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py index a78eced49ce..eee3929ad0c 100644 --- a/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py +++ b/psyneulink/core/components/mechanisms/modulatory/control/controlmechanism.py @@ -738,7 +738,7 @@ def _function(self, context=None, params=None, ): - num_ctl_sigs = self._get_current_function_param('num_control_signals') + num_ctl_sigs = self._get_current_parameter_value('num_control_signals') result = np.array([variable[0]] * num_ctl_sigs) return self.convert_output_type(result) diff --git a/psyneulink/core/components/mechanisms/processing/transfermechanism.py b/psyneulink/core/components/mechanisms/processing/transfermechanism.py index fb95527fe15..f2a0a736f4a 100644 --- a/psyneulink/core/components/mechanisms/processing/transfermechanism.py +++ b/psyneulink/core/components/mechanisms/processing/transfermechanism.py @@ -636,7 +636,7 @@ INITIALIZER, INSTANTANEOUS_MODE_VALUE, LESS_THAN_OR_EQUAL, MAX_ABS_DIFF, \ NAME, NOISE, NUM_EXECUTIONS_BEFORE_FINISHED, OWNER_VALUE, RATE, RESET, RESULT, RESULTS, \ SELECTION_FUNCTION_TYPE, TRANSFER_FUNCTION_TYPE, TRANSFER_MECHANISM, VARIABLE -from psyneulink.core.globals.parameters import Parameter +from psyneulink.core.globals.parameters import Parameter, FunctionParameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.utilities import \ @@ -1053,14 +1053,25 @@ class Parameters(ProcessingMechanism_Base.Parameters): :type: """ integrator_mode = Parameter(False, setter=_integrator_mode_setter) - integration_rate = Parameter(0.5, modulable=True) + integration_rate = FunctionParameter( + 0.5, + function_name='integrator_function', + function_parameter_name='rate' + ) + # TODO: replace initial_value with this FunctionParameter + # it's complicated. + # initial_value = FunctionParameter( + # None, + # function_name='integrator_function', + # function_parameter_name='initializer' + # ) initial_value = None integrator_function = Parameter(AdaptiveIntegrator, stateful=False, loggable=False) integrator_function_value = Parameter([[0]], read_only=True) has_integrated = Parameter(False, user=False) on_resume_integrator_mode = Parameter(INSTANTANEOUS_MODE_VALUE, stateful=False, loggable=False) clip = None - noise = Parameter(0.0, modulable=True) + noise = FunctionParameter(0.0, function_name='integrator_function') termination_measure = Parameter( Distance(metric=MAX_ABS_DIFF), modulable=False, @@ -1237,7 +1248,6 @@ def _validate_params(self, request_set, target_set=None, context=None): noise = target_set[NOISE] # If assigned as a Function, set TransferMechanism as its owner, and assign its actual function to noise if isinstance(noise, DistributionFunction): - noise.owner = self target_set[NOISE] = noise.execute self._validate_noise(target_set[NOISE]) @@ -1517,7 +1527,7 @@ def _get_instantaneous_function_input(self, function_variable, noise): def _get_integrated_function_input(self, function_variable, initial_value, noise, context, **kwargs): - integration_rate = self._get_current_mechanism_param(INTEGRATION_RATE, context) + integration_rate = self._get_current_parameter_value(self.parameters.integration_rate, context) if ( self.initialization_status == ContextFlags.INITIALIZING @@ -1719,7 +1729,7 @@ def _execute(self, # FIX: JDC 7/2/18 - THIS SHOULD BE MOVED TO A STANDARD OUTPUT_PORT # Clip outputs - clip = self._get_current_mechanism_param("clip", context) + clip = self.parameters.clip._get(context) value = super(Mechanism, self)._execute(variable=variable, context=context, @@ -1742,12 +1752,12 @@ def _parse_function_variable(self, variable, context=None): # FIX: NEED TO GET THIS TO WORK WITH CALL TO METHOD: integrator_mode = self.parameters.integrator_mode._get(context) - noise = self._get_current_mechanism_param(NOISE, context) + noise = self._get_current_parameter_value(self.parameters.noise, context) # FIX: SHOULD UPDATE PARAMS PASSED TO integrator_function WITH ANY RUNTIME PARAMS THAT ARE RELEVANT TO IT # Update according to time-scale of integration if integrator_mode: - initial_value = self._get_current_mechanism_param(INITIAL_VALUE, context) + initial_value = self._get_current_parameter_value(self.parameters.initial_value, context) value = self._get_integrated_function_input(variable, initial_value, diff --git a/psyneulink/core/components/ports/parameterport.py b/psyneulink/core/components/ports/parameterport.py index 274b0bc96ae..afd2406c6ea 100644 --- a/psyneulink/core/components/ports/parameterport.py +++ b/psyneulink/core/components/ports/parameterport.py @@ -27,7 +27,8 @@ ParameterPorts belong to either a `Mechanism ` or a `Projection `. A ParameterPort is created to represent each `modulatable parameter ` of the `Mechanism -` or a `Projection `, as well as those of the component's `function `. A +` or a `Projection `, as well as those of the component's `function ` and +any of its secondary functions (e.g. `TransferMechanism.integrator_function`). A ParameterPort provides the current value of the parameter it represents during any relevant computations, and serves as an interface for parameter modulation. @@ -64,7 +65,8 @@ belong is created. The `owner ` of a ParameterPort must be a `Mechanism ` or `MappingProjection` (the initialization of a ParameterPort cannot be `deferred `). One ParameterPort is created for each modulable Parameter of its owner, as well as for each modulable Parameter of the owner's -`function ` (modulable Parameters of a Component +`function ` or secondary functions (modulable +Parameters of a Component are listed in its `Parameters` class, and have the attribute `modulable ` set to True.) Each ParameterPort is created using the value specified for the corresponding parameter, as described below. The @@ -359,8 +361,10 @@ """ +import collections from copy import deepcopy import inspect +import operator import types import warnings @@ -377,11 +381,11 @@ CONTEXT, CONTROL_PROJECTION, CONTROL_SIGNAL, CONTROL_SIGNALS, FUNCTION, FUNCTION_PARAMS, \ LEARNING_SIGNAL, LEARNING_SIGNALS, MECHANISM, NAME, PARAMETER_PORT, PARAMETER_PORTS, \ PARAMETER_PORT_PARAMS, PATHWAY_PROJECTION, PROJECTION, PROJECTIONS, PROJECTION_TYPE, REFERENCE_VALUE, SENDER, VALUE -from psyneulink.core.globals.parameters import ParameterAlias +from psyneulink.core.globals.parameters import ParameterBase, ParameterAlias, SharedParameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set from psyneulink.core.globals.preferences.preferenceset import PreferenceLevel from psyneulink.core.globals.utilities \ - import ContentAddressableList, ReadOnlyOrderedDict, is_iterable, is_numeric, is_value_spec, iscompatible, is_instance_or_subclass + import ContentAddressableList, ReadOnlyOrderedDict, is_iterable, is_numeric, is_value_spec, iscompatible, is_instance_or_subclass, UtilitiesError, gen_friendly_comma_str __all__ = [ 'ParameterPort', 'ParameterPortError', 'port_type_keywords', @@ -390,6 +394,182 @@ port_type_keywords = port_type_keywords.update({PARAMETER_PORT}) +class ParameterPortList(ContentAddressableList): + + separator = '-' + legal_key_type_strings = ContentAddressableList.legal_key_type_strings + ['Parameter'] + + def __init__( + self, + component_type, + key=None, + list=None, + name=None, + owner=None, + **kwargs + ): + # cache, Parameter keys added when creating Ports, others upon lookup + self.parameter_mapping = {} + self.owner = owner + + super().__init__(component_type, key, list, name, **kwargs) + + def __contains__(self, item): + try: + return super().__contains__(item) + except ParameterPortError: + return False + + def __getitem__(self, key): + try: + return self.parameter_mapping[key] + except KeyError: + pass + + try: + return super().__getitem__(key) + except TypeError as e: + # ContentAddressableList throws TypeError when key/index lookup fails + names = self._get_possible_port_names(key) + possible_ports = set() + for name in names: + try: + r = super().__getitem__(name) + possible_ports.add(r) + except TypeError: + pass + if len(possible_ports) == 0: + raise e from None + elif len(possible_ports) == 1: + res = next(iter(possible_ports)) + else: + raise ParameterPortError( + f'Multiple ParameterPorts for {key} exist. Did you want' + f' {gen_friendly_comma_str(sorted([p.name for p in possible_ports]))}?' + ) from None + except UtilitiesError as e: + # ContentAddressableList throws UtilitiesError if key is not an int + # or string. handle only Parameter key here + if not isinstance(key, ParameterBase): + raise e from None + + try: + final_source = key.final_source + except AttributeError: + final_source = key + + try: + res = self.parameter_mapping[final_source] + except KeyError: + try: + raise ParameterPortError( + f'No ParameterPort corresponds to {key._owner._owner}' + f'.parameters.{key.name}' + ) from None + except AttributeError: + raise e from None + + if res is not None: + self.parameter_mapping[key] = res + + return res + + def _get_possible_port_names(self, param_name): + """ + Returns: + a list of possible parameter port names to check if + *param_name* is actually an alias or alias-with-suffix + (e.g. "leak" is an alias of "integration_rate", and + "leak__integrator_function" should refer to + "integration_rate__integrator_function") + """ + unsuffixed_name = ParameterPortList._get_base_name(param_name) + if unsuffixed_name == param_name: + # all possible function-suffixed names + names = sorted([ + p.name for p in self.owner.parameters + if is_instance_or_subclass(p.default_value, Function) + ]) + # put 'function' at beginning + try: + function_index = names.index(FUNCTION) + names = ( + [names[function_index]] + + names[0:function_index] + + names[function_index + 1:] + ) + except ValueError: + pass + + names = [self._get_explicit_name(param_name, name) for name in names] + else: + names = [] + + # try to get a Parameter that corresponds to param_name, which + # can have a "shared parameter suffix" that disambiguates which + # desired port it refers to if there are multiple + try: + param = getattr(self.owner.parameters, param_name) + except AttributeError: + try: + param = getattr(self.owner.parameters, unsuffixed_name) + except AttributeError: + return names + + # if it's a shared parameter with identical name, there are no + # other aliases we need to add + try: + source_name = param.source.name + except AttributeError: + return names + + if source_name != param.name: + if unsuffixed_name == param_name: + # basic alias, e.g. "leak" -> "integration_rate" + names.append(source_name) + else: + # alias with suffix, e.g. "leak__function" + # -> "integration_rate__function" + suffix = ParameterPortList._get_suffix(param_name) + names.append( + ParameterPortList._get_explicit_name(source_name, suffix) + ) + + if isinstance(param, ParameterAlias): + # alias to another alias or a shared parameter + # e.g. leak -> integration_rate -> rate + names.extend(self._get_possible_port_names(source_name)) + else: + # e.g. integration_rate__integrator_function + # -> rate__integrator_function + names.append( + ParameterPortList._get_explicit_name( + source_name, + param.attribute_name + ) + ) + + return names + + @classmethod + def _get_explicit_name(cls, port_name, parameter_name=None): + return f'{port_name}{cls.separator}{parameter_name}' + + @classmethod + def _get_base_name(cls, explicit_name): + try: + return explicit_name.split(cls.separator)[0] + except IndexError: + return explicit_name + + @classmethod + def _get_suffix(cls, explicit_name): + try: + return explicit_name.split(cls.separator)[1] + except IndexError: + return '' + + class ParameterPortError(Exception): def __init__(self, error_value): self.error_value = error_value @@ -503,6 +683,7 @@ def __init__(self, projections=None, params=None, name=None, + parameter_name=None, prefs:is_pref_set=None, **kwargs): @@ -756,7 +937,7 @@ def _get_variable_from_projections(self, context=None): """ # FIX 3/6/19: source does not yet seem to have been assigned to owner.function - return getattr(self.source.parameters, self.name)._get(context) + return self.source._get(context) @property def pathway_projections(self): @@ -780,8 +961,11 @@ def _instantiate_parameter_ports(owner, function=None, context=None): # TBI / IMPLEMENT: use specs to implement ParameterPorts below - owner._parameter_ports = ContentAddressableList(component_type=ParameterPort, - name=owner.name + '.parameter_ports') + owner._parameter_ports = ParameterPortList( + component_type=ParameterPort, + name=owner.name + '.parameter_ports', + owner=owner, + ) # Check that all ParameterPorts for owner have not been explicitly suppressed try: @@ -797,33 +981,103 @@ def _instantiate_parameter_ports(owner, function=None, context=None): # values/defaults should take precedence def skip_parameter_port(parameter): return ( - isinstance(parameter, ParameterAlias) + isinstance(parameter, (ParameterAlias, SharedParameter)) or parameter.name in owner.exclude_from_parameter_ports or not parameter.modulable ) + port_parameters = collections.defaultdict(set) + port_aliases = set() + # function may be a custom function not yet parsed to a UDF # function may also be a Function class, in which case parameter # ports are still created for the modulable Parameters - if is_instance_or_subclass(function, Function): - for p in function.parameters: - if not skip_parameter_port(p): - try: - value = owner.initial_shared_parameters['function'][p.name] - except (KeyError, TypeError): - if p.spec is not None: - value = p.spec - else: - value = p.default_value + for p in owner.parameters: + func = p.default_value + if ( + not p.reference + and is_instance_or_subclass(func, Function) + and not isinstance(p, (ParameterAlias, SharedParameter)) + ): + for func_param in func.parameters: + if not skip_parameter_port(func_param): + port_parameters[func_param.name].add(p.name) + if isinstance(p, ParameterAlias): + port_aliases.add(p.name) + + duplicates = [p for p in port_parameters if len(port_parameters[p]) > 1] + if len(duplicates) > 0: + dup_str = '\n\t'.join([f'{name}: {", ".join(port_parameters[name])}' for name in duplicates]) + ex_func_name = next(iter(port_parameters[duplicates[0]])) + ex_port_name = duplicates[0] + warnings.warn( + 'Multiple ParameterPorts will be created for Parameters with the' + f' same name:\n{owner}\n\t{dup_str}' + '\nTo explicitly access the correct Port, you will need to' + " include the function's name as suffix or use the Parameter object." + f" For example,\nself.parameter_ports['{ex_port_name}{owner.parameter_ports.separator}{ex_func_name}']\nor\n" + f'self.parameter_ports[self.{ex_func_name}.parameters.{ex_port_name}]' + ) + + for parameter_port_name in port_parameters: + if ( + len(port_parameters[parameter_port_name]) > 1 + or parameter_port_name in port_aliases + ): + add_suffix = True + else: + add_suffix = False + + for corresponding_parameter_component_name in port_parameters[parameter_port_name]: + corresponding_parameter_component = getattr( + owner.parameters, + corresponding_parameter_component_name + )._get(context) + + p = getattr( + corresponding_parameter_component.parameters, + parameter_port_name + ) - _instantiate_parameter_port( - owner, + # .function is not finalized yet, because this happens before + # _instantiate_function + if corresponding_parameter_component_name is FUNCTION: + source = operator.attrgetter(f'{FUNCTION}.parameters.{p.name}') + else: + source = p + + # use Shared/FunctionParameter value as fallback + try: + value = owner.initial_shared_parameters[corresponding_parameter_component_name][p.name] + except (KeyError, TypeError): + value = None + + # if parameter value on actual Parameter was specified or there is + # no Shared/FunctionParameter value, use the actual Parameter default + if p._user_specified or value is None: + if p.spec is not None: + value = p.spec + else: + value = p.default_value + + if add_suffix: + explicit_name = ParameterPortList._get_explicit_name( p.name, - value, - context=context, - function=function + corresponding_parameter_component_name ) + else: + explicit_name = p.name + + _instantiate_parameter_port( + owner, + p.name, + value, + context=context, + function=corresponding_parameter_component, + source=source, + explicit_name=explicit_name + ) for p in owner.parameters: if ( @@ -840,12 +1094,21 @@ def skip_parameter_port(parameter): p.name, value, context=context, - function=function + function=function, + source=p ) owner.parameter_ports.sort(key=lambda port: port.name) -def _instantiate_parameter_port(owner, param_name, param_value, context, function=None): +def _instantiate_parameter_port( + owner, + param_name, + param_value, + context, + function=None, + source=None, + explicit_name=None +): """Call _instantiate_port for allowable params, to instantiate a ParameterPort for it Include ones in function.parameters @@ -881,6 +1144,9 @@ def _get_tuple_for_single_item_modulatory_spec(obj, name, value): raise ParameterPortError("Unrecognized specification for {} paramater of {} ({})". format(param_name, owner.name, param_value)) + if explicit_name is None: + explicit_name = param_name + # EXCLUSIONS: # # Skip if ParameterPort already exists @@ -944,98 +1210,77 @@ def _get_tuple_for_single_item_modulatory_spec(obj, name, value): # - a function or method # - have a value of None (see IMPLEMENTATION_NOTE below) # - they have the same name as another parameter of the component (raise exception for this) - if param_name in function.parameters.names(): - function_param_name = param_name - if ( - hasattr(function.parameters, function_param_name) - and not getattr(function.parameters, function_param_name).modulable - ): - # skip non modulable function parameters - return - - function_param_value = param_value - - # IMPLEMENTATION NOTE: - # The following is necessary since, if ANY parameters of a function are specified, entries are made - # in the FUNCTION_PARAMS dict of its owner for ALL of the function's params; however, their values - # will be set to None (and there may not be a way to determine a - # default; e.g., the length of the array for the weights or exponents params for LinearCombination). - # Therefore, None will be passed as the reference_value, which will cause validation of the - # ParameterPort's function (in _instantiate_function()) to fail. - # Current solution is to simply not instantiate a ParameterPort for any function_param that has - # not been explicitly specified - if function_param_value is None: - return - if not _is_legal_param_value(owner, function_param_value): - return - - elif (_is_modulatory_spec(function_param_value, include_matrix_spec=False) - and not isinstance(function_param_value, tuple)): - # If parameter is a single Modulatory specification (e.g., ControlSignal, or CONTROL, etc.) - # try to place it in a tuple (for interpretation by _parse_port_spec) using default value as 1st item - # (note: exclude matrix since it is allowed as a value specification vs. a projection reference) - try: - function_param_value = _get_tuple_for_single_item_modulatory_spec( - function, - function_param_name, - function_param_value - ) - except ParameterPortError: - function_param_value = _get_tuple_for_single_item_modulatory_spec( - owner, - function_param_name, - function_param_value - ) + # IMPLEMENTATION NOTE: + # The following is necessary since, if ANY parameters of a function are specified, entries are made + # in the FUNCTION_PARAMS dict of its owner for ALL of the function's params; however, their values + # will be set to None (and there may not be a way to determine a + # default; e.g., the length of the array for the weights or exponents params for LinearCombination). + # Therefore, None will be passed as the reference_value, which will cause validation of the + # ParameterPort's function (in _instantiate_function()) to fail. + # Current solution is to simply not instantiate a ParameterPort for any function_param that has + # not been explicitly specified + if param_value is None: + return + if not _is_legal_param_value(owner, param_value): + return - # # FIX: 10/3/17 - ??MOVE THIS TO _parse_port_specific_specs ---------------- - # # Use function_param_value as constraint - # # IMPLEMENTATION NOTE: need to copy, since _instantiate_port() calls _parse_port_value() - # # for constraints before port_spec, which moves items to subdictionaries, - # # which would make them inaccessible to the subsequent parse of port_spec - from psyneulink.core.components.ports.modulatorysignals.modulatorysignal import ModulatorySignal - from psyneulink.core.components.mechanisms.modulatory.modulatorymechanism import ModulatoryMechanism_Base - if ( - is_iterable(function_param_value) - and any(isinstance(item, (ModulatorySignal, ModulatoryProjection_Base, ModulatoryMechanism_Base)) for item in function_param_value) - ): - reference_value = function_param_value - else: - reference_value = deepcopy(function_param_value) - - # Assign parameterPort for function_param to the component - port = _instantiate_port(owner=owner, - port_type=ParameterPort, - name=function_param_name, - port_spec=function_param_value, - reference_value=reference_value, - reference_value_name=function_param_name, - params=None, - context=context) - if port: - owner._parameter_ports[function_param_name] = port - # will be parsed on assignment of function - # FIX: if the function is manually changed after assignment, - # FIX: the source will remain pointing to the original Function - port.source = FUNCTION - - elif _is_legal_param_value(owner, param_value): - port = _instantiate_port(owner=owner, - port_type=ParameterPort, - name=param_name, - port_spec=param_value, - reference_value=param_value, - reference_value_name=param_name, - params=None, - context=context) - if port: - if param_name in function.parameters.names(): - port.source = function - else: - port.source = owner + elif (_is_modulatory_spec(param_value, include_matrix_spec=False) + and not isinstance(param_value, tuple)): + # If parameter is a single Modulatory specification (e.g., ControlSignal, or CONTROL, etc.) + # try to place it in a tuple (for interpretation by _parse_port_spec) using default value as 1st item + # (note: exclude matrix since it is allowed as a value specification vs. a projection reference) + try: + param_value = _get_tuple_for_single_item_modulatory_spec( + function, + param_name, + param_value + ) + except ParameterPortError: + param_value = _get_tuple_for_single_item_modulatory_spec( + owner, + param_name, + param_value + ) - owner._parameter_ports[param_name] = port + # # FIX: 10/3/17 - ??MOVE THIS TO _parse_port_specific_specs ---------------- + # # Use param_value as constraint + # # IMPLEMENTATION NOTE: need to copy, since _instantiate_port() calls _parse_port_value() + # # for constraints before port_spec, which moves items to subdictionaries, + # # which would make them inaccessible to the subsequent parse of port_spec + from psyneulink.core.components.ports.modulatorysignals.modulatorysignal import ModulatorySignal + from psyneulink.core.components.mechanisms.modulatory.modulatorymechanism import ModulatoryMechanism_Base + if ( + is_iterable(param_value) + and any(isinstance(item, (ModulatorySignal, ModulatoryProjection_Base, ModulatoryMechanism_Base)) for item in param_value) + ): + reference_value = param_value + else: + reference_value = deepcopy(param_value) + + # Assign parameterPort for function_param to the component + port = _instantiate_port( + owner=owner, + port_type=ParameterPort, + name=explicit_name, + port_spec=param_value, + reference_value=reference_value, + reference_value_name=param_name, + params=None, + context=context + ) + if port: + owner._parameter_ports[explicit_name] = port + # will be parsed on assignment of function + # FIX: if the function is manually changed after assignment, + # FIX: the source will remain pointing to the original Function + port.source = source + # if the source parameter is not added here, we can't reference + # a ParameterPort by Parameter + owner.parameter_ports.parameter_mapping[source] = port + + return port def _is_legal_param_value(owner, value): diff --git a/psyneulink/core/components/projections/pathway/mappingprojection.py b/psyneulink/core/components/projections/pathway/mappingprojection.py index 191d483bf8b..bd8027fdc7d 100644 --- a/psyneulink/core/components/projections/pathway/mappingprojection.py +++ b/psyneulink/core/components/projections/pathway/mappingprojection.py @@ -299,7 +299,7 @@ HOLLOW_MATRIX, IDENTITY_MATRIX, INPUT_PORT, LEARNING, LEARNING_PROJECTION, MAPPING_PROJECTION, MATRIX, \ OUTPUT_PORT, PROJECTION_SENDER, VALUE from psyneulink.core.globals.log import ContextFlags -from psyneulink.core.globals.parameters import Parameter +from psyneulink.core.globals.parameters import FunctionParameter, Parameter from psyneulink.core.globals.preferences.basepreferenceset import is_pref_set from psyneulink.core.globals.preferences.preferenceset import PreferenceEntry, PreferenceLevel @@ -418,10 +418,10 @@ class Parameters(PathwayProjection_Base.Parameters): :type: ``str`` """ function = Parameter(LinearMatrix, stateful=False, loggable=False) - matrix = Parameter(DEFAULT_MATRIX, modulable=True, - function_parameter=True, - getter=_mapping_projection_matrix_getter, - setter=_mapping_projection_matrix_setter) + matrix = FunctionParameter( + DEFAULT_MATRIX, + setter=_mapping_projection_matrix_setter + ) classPreferenceLevel = PreferenceLevel.TYPE diff --git a/psyneulink/core/compositions/composition.py b/psyneulink/core/compositions/composition.py index 9e63719c93c..aa1f4c4c1a0 100644 --- a/psyneulink/core/compositions/composition.py +++ b/psyneulink/core/compositions/composition.py @@ -831,8 +831,7 @@ `, in which case it is automatically wrapped as `UserDefinedFunction`. For example, the `forward and backward methods `_ of a PyTorch object can be assigned in this way. The advanatage of this approach is that it can be applied to any Python function that adheres to the requirements -of a `UserDefinedFunction`. The disadvantage is that it can't be `compiled`, so efficiency may be compromised. It must -also be carefully coordinated with the execution of other learning-related Components in the Composition, to insure +of a `UserDefinedFunction`. It must be carefully coordinated with the execution of other learning-related Components in the Composition, to insure that each function is called at the appropriate times during execution. Furthermore, as with an `AutodiffComposition`, the internal constituents of the object (e.g., intermediates layers of a neural network model) are not accessible to other Components in the Composition (e.g., as a source of information or for modulation). @@ -1284,6 +1283,9 @@ def input_function(env, result): cycle in that run, whereas any Nodes not specified will retain the last `value ` they were assigned in the uprevious call to `run ` or `learn `. +Nodes in a cycle can also be initialized outside of a call to `run ` or `learn ` using +the `initialize ` method. + .. note:: If a `Mechanism` belonging to a cycle in a Composition is first executed on its own (i.e., using its own `execute ` method), the value it is assigned will be used as its initial value when it is executed @@ -1532,8 +1534,8 @@ def input_function(env, result): Compilation is supported for most CPUs (including x86, arm64, and powerpc64le). Several modes can be specified, that that tradeoff power (i.e., degree of speed-up) against level of support (i.e., likelihood of success). Most PsyNeuLink `Components ` and methods are supported for compilation; however, Python native functions and methods -(e.g., used to specify the `function ` of a Component) are not supported at present, including -their use in a `UserDefinedFunction`. Users are strongly urged to report any other compilation failures to +(e.g., used to specify the `function ` of a Component) are not supported at present. Users who wish +to compile custom functions should refer to `compiled User Defined Functions ` for more information. Users are strongly urged to report any other compilation failures to psyneulinkhelp@princeton.edu, or as an issue `here `_. Known failure conditions are listed `here `_. @@ -2378,7 +2380,7 @@ def input_function(env, result): from psyneulink.core.globals.context import Context, ContextFlags, handle_external_context from psyneulink.core.globals.keywords import \ AFTER, ALL, ANY, BEFORE, BOLD, BOTH, \ - COMPONENT, COMPOSITION, CONDITIONS, CONTROL, CONTROL_PATHWAY, CONTROLLER, CONTROL_SIGNAL, \ + COMPONENT, COMPOSITION, CONDITIONS, CONTROL, CONTROL_PATHWAY, CONTROLLER, CONTROL_SIGNAL, DEFAULT, \ FEEDBACK, FUNCTIONS, HARD_CLAMP, IDENTITY_MATRIX, INPUT, INPUT_PORTS, INPUTS, INPUT_CIM_NAME, INSET, \ LABELS, LEARNED_PROJECTIONS, LEARNING_FUNCTION, LEARNING_MECHANISM, LEARNING_MECHANISMS, LEARNING_PATHWAY, \ MATRIX, MATRIX_KEYWORD_VALUES, MAYBE, MECHANISM, MECHANISMS, \ @@ -8200,65 +8202,6 @@ def run( else: node.parameters.num_executions._get(context)._set_by_time_scale(TimeScale.RUN, 0) - if initialize_cycle_values is not None: - for node in initialize_cycle_values: - if node not in self.nodes: - raise CompositionError(f"{node.name} " - f"(entry in initialize_cycle_values arg) is not a node in '{self.name}'") - else: - if (node not in self.get_nodes_by_role(NodeRole.CYCLE) and - node not in self.get_nodes_by_role(NodeRole.FEEDBACK_SENDER)): - warnings.warn( - f"A value is specified for {node.name} of {self.name} in the 'initialize_cycle_values' " - f"argument of call to run, but it is neither part of a cycle nor a FEEDBACK_SENDER. " - f"Its value will be overwritten when the node first executes, and therefore not used.") - node.initialize(initialize_cycle_values[node], context) - - if not reset_stateful_functions_to: - reset_stateful_functions_to = {} - - for node, vals in reset_stateful_functions_to.items(): - try: - iter(vals) - except TypeError: - vals = [vals] - reset_stateful_functions_to[node] = vals - if (isinstance(reset_stateful_functions_when, Never) or - node not in reset_stateful_functions_when) and \ - isinstance(node.reset_stateful_function_when, Never): - node.reset(*vals, context=context) - - # cache and set reset_stateful_function_when conditions for nodes, matching old System behavior - # Validate - valid_reset_type = True - if not isinstance(reset_stateful_functions_when, (Condition, dict)): - valid_reset_type = False - elif type(reset_stateful_functions_when) == dict: - if False in {True if isinstance(k, Mechanism) and isinstance(v, Condition) else - False for k,v in reset_stateful_functions_when.items()}: - valid_reset_type = False - - if not valid_reset_type: - raise CompositionError( - f"{reset_stateful_functions_when} is not a valid specification for reset_integrator_nodes_when of {self.name}. " - "reset_integrator_nodes_when must be a Condition or a dict comprised of {Node: Condition} pairs.") - - self._reset_stateful_functions_when_cache = {} - - # use type here to avoid another costly call to isinstance - if not type(reset_stateful_functions_when) == dict: - for node in self.nodes: - try: - if isinstance(node.reset_stateful_function_when, Never): - self._reset_stateful_functions_when_cache[node] = node.reset_stateful_function_when - node.reset_stateful_function_when = reset_stateful_functions_when - except AttributeError: - pass - else: - for node in reset_stateful_functions_when: - self._reset_stateful_functions_when_cache[node] = node.reset_stateful_function_when - node.reset_stateful_function_when = reset_stateful_functions_when[node] - if ContextFlags.SIMULATION_MODE not in context.runmode: try: self.parameters.input_specification._set(copy(inputs), context) @@ -8344,6 +8287,54 @@ def run( context.composition = self + if initialize_cycle_values is not None: + self.initialize(values=initialize_cycle_values, include_unspecified_nodes=False, context=context) + + if not reset_stateful_functions_to: + reset_stateful_functions_to = {} + + for node, vals in reset_stateful_functions_to.items(): + try: + iter(vals) + except TypeError: + vals = [vals] + reset_stateful_functions_to[node] = vals + if (isinstance(reset_stateful_functions_when, Never) or + node not in reset_stateful_functions_when) and \ + isinstance(node.reset_stateful_function_when, Never): + node.reset(*vals, context=context) + + # cache and set reset_stateful_function_when conditions for nodes, matching old System behavior + # Validate + valid_reset_type = True + if not isinstance(reset_stateful_functions_when, (Condition, dict)): + valid_reset_type = False + elif type(reset_stateful_functions_when) == dict: + if False in {True if isinstance(k, Mechanism) and isinstance(v, Condition) else + False for k,v in reset_stateful_functions_when.items()}: + valid_reset_type = False + + if not valid_reset_type: + raise CompositionError( + f"{reset_stateful_functions_when} is not a valid specification for reset_integrator_nodes_when of {self.name}. " + "reset_integrator_nodes_when must be a Condition or a dict comprised of {Node: Condition} pairs.") + + self._reset_stateful_functions_when_cache = {} + + # use type here to avoid another costly call to isinstance + if not type(reset_stateful_functions_when) == dict: + for node in self.nodes: + try: + if isinstance(node.reset_stateful_function_when, Never): + self._reset_stateful_functions_when_cache[node] = node.reset_stateful_function_when + node.reset_stateful_function_when = reset_stateful_functions_when + except AttributeError: + pass + else: + for node in reset_stateful_functions_when: + self._reset_stateful_functions_when_cache[node] = node.reset_stateful_function_when + node.reset_stateful_function_when = reset_stateful_functions_when[node] + is_simulation = (context is not None and ContextFlags.SIMULATION_MODE in context.runmode) @@ -9263,7 +9254,7 @@ def _update_learning_parameters(self, context): pass @handle_external_context(execution_id=NotImplemented) - def reset(self, values=None, context=NotImplemented): + def reset(self, values=None, include_unspecified_nodes=True, context=NotImplemented): if context is NotImplemented: context = self.most_recent_context @@ -9271,9 +9262,66 @@ def reset(self, values=None, context=NotImplemented): values = {} for node in self.stateful_nodes: + if not include_unspecified_nodes and node not in values: + continue reset_val = values.get(node) node.reset(reset_val, context=context) + def initialize(self, values=None, include_unspecified_nodes=True, context=NotImplemented): + """ + Initializes the values of nodes within cycles. If `include_unspecified_nodes` is True and a value is + provided for a given node, the node will be initialized to that value. If `include_unspecified_nodes` is + True and a value is not provided, the node will be initialized to its default value. If + `include_unspecified_nodes` is False, then all nodes must have corresponding initialization values. The + `DEFAULT` keyword can be used in lieu of a numerical value to reset a node's value to its default. + + If a context is not provided, the most recent context under which the Composition has executed will be used. + + Arguments + ---------- + values: Dict { Node: Node Value } + A dictionary contaning key-value pairs of Nodes and initialization values. Nodes within cycles that are + not included in this dict will be initialized to their default values. + + include_unspecified_nodes: bool + Specifies whether all nodes within cycles should be initialized or only ones specified in the provided + values dictionary. + + context: Context + The context under which the nodes should be initialized. context will be set to + self.most_recent_execution_context if one is not specified. + + """ + if context is NotImplemented: + context = self.most_recent_context + + # comp must be initialized from context before cycle values are initialized + self._initialize_from_context(context, Context(execution_id=None), override=False) + + if not values: + values = {} + + cycle_nodes = set(self.get_nodes_by_role(NodeRole.CYCLE) + self.get_nodes_by_role(NodeRole.FEEDBACK_SENDER)) + + for node in values: + if node not in self.nodes: + raise CompositionError(f"{node.name} " + f"(entry in initialize values arg) is not a node in '{self.name}'") + if node not in cycle_nodes: + warnings.warn( + f"A value is specified for {node.name} of {self.name} in the 'initialize_cycle_values' " + f"argument of call to run, but it is neither part of a cycle nor a FEEDBACK_SENDER. " + f"Its value will be overwritten when the node first executes, and therefore not used." + ) + + for node in cycle_nodes: + if not include_unspecified_nodes: + if node not in values: + continue + provided_value = values.get(node) + value = provided_value if not provided_value == DEFAULT else node.defaults.value + node.initialize(value, context) + def disable_all_history(self): """ When run, disables history tracking for all Parameters of all Components used in the Composition diff --git a/psyneulink/core/globals/log.py b/psyneulink/core/globals/log.py index ce95e5b4974..2ac04763ac6 100644 --- a/psyneulink/core/globals/log.py +++ b/psyneulink/core/globals/log.py @@ -719,7 +719,13 @@ def output_port_items(self): @property def parameter_port_items(self): try: - return [MODULATED_PARAMETER_PREFIX + name for name in self.owner.parameter_ports.names] + return [ + name for name in self.owner.__dir__() + if ( + name.startswith(MODULATED_PARAMETER_PREFIX) + and name.split(MODULATED_PARAMETER_PREFIX)[1] in self.owner.parameter_ports + ) + ] except AttributeError: return [] diff --git a/psyneulink/core/globals/parameters.py b/psyneulink/core/globals/parameters.py index 648e95a0566..ae3a02fc7bd 100644 --- a/psyneulink/core/globals/parameters.py +++ b/psyneulink/core/globals/parameters.py @@ -1702,6 +1702,14 @@ def source(self): except AttributeError: return None + @property + def final_source(self): + base_param = self + while hasattr(base_param, 'source'): + base_param = base_param.source + + return base_param + class FunctionParameter(SharedParameter): """ diff --git a/psyneulink/core/globals/utilities.py b/psyneulink/core/globals/utilities.py index 9ff2729f9e6..20eeece166f 100644 --- a/psyneulink/core/globals/utilities.py +++ b/psyneulink/core/globals/utilities.py @@ -1111,6 +1111,8 @@ class ContentAddressableList(UserList): """ + legal_key_type_strings = ['int', 'str', 'Port'] + def __init__(self, component_type, key=None, list=None, name=None, **kwargs): self.component_type = component_type self.key = key or 'name' @@ -1154,7 +1156,6 @@ def __getitem__(self, key): format(key, self.name)) return self.data[key_num] - def __setitem__(self, key, value): # For efficiency, first assume the key is numeric (duck typing in action!) try: @@ -1166,7 +1167,7 @@ def __setitem__(self, key, value): raise UtilitiesError("Non-numeric key used for {} ({}) must be " "a string)".format(self.name, key)) # The specified string must also match the value of the attribute of the class used for addressing - if not key == value.name: + if not key.startswith(value.name): # if not key == type(value).__name__: raise UtilitiesError("The key of the entry for {} {} ({}) " "must match the value of its {} attribute " @@ -1185,7 +1186,11 @@ def __contains__(self, item): if super().__contains__(item): return True else: - return any(item == obj.name for obj in self.data) + try: + self.__getitem__(item) + return True + except (KeyError, TypeError, UtilitiesError, ValueError): + return False def _get_key_for_item(self, key): if isinstance(key, str): @@ -1197,9 +1202,13 @@ def _get_key_for_item(self, key): elif isinstance(key, self.component_type): return self.data.index(key) else: - raise UtilitiesError("{} is not a legal key for {} (must be " - "number, string or Port)".format(key, - self.key)) + raise UtilitiesError( + "{} is not a legal key for {} (must be {})".format( + key, + self.key, + gen_friendly_comma_str(self.legal_key_type_strings) + ) + ) def __delitem__(self, key): if key is None: @@ -1749,3 +1758,24 @@ def merge_dictionaries(a: dict, b: dict) -> typing.Tuple[dict, bool]: new_dict.update({k: create_union_set(a[k], b[k]) for k in shared_keys}) return new_dict, len(new_dict) < (len(a) + len(b)) + + +def gen_friendly_comma_str(items): + """ + Returns: + a proper English comma-separated string of each item in + **items** + """ + if isinstance(items, str) or not is_iterable(items): + return str(items) + + items = [str(x) for x in items] + + if len(items) < 2: + return ''.join(items) + else: + divider = ' or ' + if len(items) > 2: + divider = f',{divider}' + + return f"{', '.join(items[:-1])}{divider}{items[-1]}" diff --git a/psyneulink/core/llvm/__init__.py b/psyneulink/core/llvm/__init__.py index 8eeafc157d1..851b4f73bbe 100644 --- a/psyneulink/core/llvm/__init__.py +++ b/psyneulink/core/llvm/__init__.py @@ -133,6 +133,7 @@ def init_builtins(): builtins.setup_vxm_transposed(ctx) builtins.setup_mersenne_twister(ctx) builtins.setup_vec_add(ctx) + builtins.setup_vec_sum(ctx) builtins.setup_mat_add(ctx) builtins.setup_vec_sub(ctx) builtins.setup_mat_sub(ctx) diff --git a/psyneulink/core/llvm/builtins.py b/psyneulink/core/llvm/builtins.py index 83cc7b67c77..06c8b141a78 100644 --- a/psyneulink/core/llvm/builtins.py +++ b/psyneulink/core/llvm/builtins.py @@ -135,6 +135,28 @@ def setup_vec_add(ctx): builder.ret_void() +# Setup vector sum builtin +def setup_vec_sum(ctx): + # Setup types + double_ptr_ty = ctx.float_ty.as_pointer() + + # builtin vector sum func (i.e. sum(vec)) + # param1: ptr to vector 1 + # param2: sizeof vector + # param3: scalar output ptr + + builder = _setup_builtin_func_builder(ctx, "vec_sum", (double_ptr_ty, ctx.int32_ty, double_ptr_ty)) + u, x, o = builder.function.args + + # Sum + builder.store(ctx.float_ty(-0), o) + with helpers.for_loop_zero_inc(builder, x, "sum") as (b1, index): + u_ptr = b1.gep(u, [index]) + u_val = b1.load(u_ptr) + u_sum = b1.fadd(u_val, builder.load(o)) + b1.store(u_sum, o) + + builder.ret_void() # Setup vector copy builtin def setup_vec_copy(ctx): diff --git a/psyneulink/core/llvm/codegen.py b/psyneulink/core/llvm/codegen.py index f176be74e7d..48881283fb0 100644 --- a/psyneulink/core/llvm/codegen.py +++ b/psyneulink/core/llvm/codegen.py @@ -9,7 +9,7 @@ # ********************************************* LLVM IR Generation ************************************************************** import ast import warnings - +import numpy as np from llvmlite import ir from contextlib import contextmanager @@ -21,6 +21,430 @@ from . import helpers from .debug import debug_env +class UserDefinedFunctionVisitor(ast.NodeVisitor): + def __init__(self, ctx, builder, func_globals, func_params, arg_in, arg_out): + self.ctx = ctx + self.builder = builder + self.func_params = func_params + self.arg_in = arg_in + self.arg_out = arg_out + self.register = {} + + #setup default functions + def _vec_sum(x): + dim = len(x.type.pointee) + output_scalar = builder.alloca(ctx.float_ty) + # Get the pointer to the first element of the array to convert from [? x double]* -> double* + vec_u = builder.gep(x, [ctx.int32_ty(0), ctx.int32_ty(0)]) + builder.call(ctx.import_llvm_function("__pnl_builtin_vec_sum"), [vec_u, ctx.int32_ty(dim), output_scalar]) + return output_scalar + + def _tanh(x): + output_ptr = builder.alloca(x.type.pointee) + helpers.call_elementwise_operation(self.ctx, self.builder, x, helpers.tanh, output_ptr) + return output_ptr + + def _exp(x): + output_ptr = builder.alloca(x.type.pointee) + helpers.call_elementwise_operation(self.ctx, self.builder, x, helpers.exp, output_ptr) + return output_ptr + + self.register['sum'] = _vec_sum + + # setup numpy + numpy_handlers = { + 'tanh': _tanh, + 'exp': _exp + } + + for k, v in func_globals.items(): + if v is np: + self.register[k] = numpy_handlers + + name_constants = { + True: ir.IntType(1)(1), + False: ir.IntType(1)(0), + } + self.name_constants = name_constants + super().__init__() + + def visit_arguments(self, node): + args = node.args + variable = args[0] + # update register + self.register[variable.arg] = self.arg_in + parameters = args[1:] + for param in parameters: + assert param.arg not in ["self", "owner"], f"Unable to reference {param.arg} in a compiled UserDefinedFunction!" + if param.arg == 'params': + assert False, "Runtime parameters are not supported in compiled mode" + elif param.arg == 'context': + # Since contexts are implicit in the structs in compiled mode, we do not compile it. + pass + else: + self.register[param.arg] = self.func_params[param.arg] + + def _generate_binop(self, x, y, callback): + # unpack scalars from pointers + if helpers.is_floating_point(x) and helpers.is_pointer(x): + x = self.builder.load(x) + if helpers.is_floating_point(y) and helpers.is_pointer(y): + y = self.builder.load(y) + + return callback(self.ctx, self.builder, x, y) + + def visit_Add(self, node): + def _add_vec(ctx, builder, u, v): + assert u.type == v.type + + dim = len(u.type.pointee) + output_vec = builder.alloca(u.type.pointee) + + # Get the pointer to the first element of the array to convert from [? x double]* -> double* + vec_u = builder.gep(u, [ctx.int32_ty(0), ctx.int32_ty(0)]) + vec_v = builder.gep(v, [ctx.int32_ty(0), ctx.int32_ty(0)]) + vec_out = builder.gep(output_vec, [ctx.int32_ty(0), ctx.int32_ty(0)]) + + builtin = ctx.import_llvm_function("__pnl_builtin_vec_add") + builder.call(builtin, [vec_u, vec_v, ctx.int32_ty(dim), vec_out]) + return output_vec + + def _add_mat(ctx, builder, m1, m2): + assert m1.type == m2.type + + x = len(m1.type.pointee) + y = len(m1.type.pointee.element) + + output_mat = builder.alloca(m1.type.pointee) + + m1_ptr = builder.gep(m1, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(0)]) + m2_ptr = builder.gep(m2, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(0)]) + output_ptr = builder.gep(output_mat, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(0)]) + + builtin = ctx.import_llvm_function("__pnl_builtin_mat_add") + builder.call(builtin, [m1_ptr, m2_ptr, ctx.int32_ty(x), ctx.int32_ty(y), output_ptr]) + return output_mat + + def _add_mat_scalar(ctx, builder, m1, s): + x = len(m1.type.pointee) + y = len(m1.type.pointee.element) + output_mat = builder.alloca(m1.type.pointee) + + m1_ptr = builder.gep(m1, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(0)]) + output_ptr = builder.gep(output_mat, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(0)]) + + builtin = ctx.import_llvm_function("__pnl_builtin_mat_scalar_add") + builder.call(builtin, [m1_ptr, s, ctx.int32_ty(x), ctx.int32_ty(y), output_ptr]) + return output_mat + + def _add_vec_scalar(ctx, builder, u, s): + output_vec = builder.alloca(u.type.pointee) + + helpers.call_elementwise_operation(ctx, builder, u, lambda ctx, builder, x: builder.fadd(x, s), output_vec) + return output_vec + + def _add(x, y): + if helpers.is_floating_point(x) and helpers.is_floating_point(y): + return self._generate_binop(x, y, lambda ctx, builder, x, y: builder.fadd(x,y)) + elif helpers.is_vector(x) and helpers.is_floating_point(y): + return self._generate_binop(x, y, _add_vec_scalar) + elif helpers.is_floating_point(x) and helpers.is_vector(y): + return self._generate_binop(y, x, _add_vec_scalar) + elif helpers.is_2d_matrix(x) and helpers.is_floating_point(y): + return self._generate_binop(x, y, _add_mat_scalar) + elif helpers.is_floating_point(x) and helpers.is_2d_matrix(y): + return self._generate_binop(y, x, _add_mat_scalar) + elif helpers.is_vector(x) and helpers.is_vector(y): + return self._generate_binop(x, y, _add_vec) + elif helpers.is_2d_matrix(x) and helpers.is_2d_matrix(y): + return self._generate_binop(x, y, _add_mat) + + return _add + + def visit_Mult(self, node): + def _mul_vec(ctx, builder, u, v): + assert u.type == v.type + + dim = len(u.type.pointee) + output_vec = builder.alloca(u.type.pointee) + + # Get the pointer to the first element of the array to convert from [? x double]* -> double* + vec_u = builder.gep(u, [ctx.int32_ty(0), ctx.int32_ty(0)]) + vec_v = builder.gep(v, [ctx.int32_ty(0), ctx.int32_ty(0)]) + vec_out = builder.gep(output_vec, [ctx.int32_ty(0), ctx.int32_ty(0)]) + + builtin = ctx.import_llvm_function("__pnl_builtin_vec_hadamard") + builder.call(builtin, [vec_u, vec_v, ctx.int32_ty(dim), vec_out]) + return output_vec + + def _mul_mat(ctx, builder, m1, m2): + assert m1.type == m2.type + + x = len(m1.type.pointee) + y = len(m1.type.pointee.element) + + output_mat = builder.alloca(m1.type.pointee) + + m1_ptr = builder.gep(m1, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(0)]) + m2_ptr = builder.gep(m2, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(0)]) + output_ptr = builder.gep(output_mat, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(0)]) + + builtin = ctx.import_llvm_function("__pnl_builtin_mat_hadamard") + builder.call(builtin, [m1_ptr, m2_ptr, ctx.int32_ty(x), ctx.int32_ty(y), output_ptr]) + return output_mat + + def _mul_mat_scalar(ctx, builder, m1, s): + x = len(m1.type.pointee) + y = len(m1.type.pointee.element) + output_mat = builder.alloca(m1.type.pointee) + + m1_ptr = builder.gep(m1, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(0)]) + output_ptr = builder.gep(output_mat, [ctx.int32_ty(0), ctx.int32_ty(0), ctx.int32_ty(0)]) + + builtin = ctx.import_llvm_function("__pnl_builtin_mat_scalar_mult") + builder.call(builtin, [m1_ptr, s, ctx.int32_ty(x), ctx.int32_ty(y), output_ptr]) + return output_mat + + def _mul_vec_scalar(ctx, builder, v, s): + x = len(v.type.pointee) + output_vec = builder.alloca(v.type.pointee) + + v_ptr = builder.gep(v, [ctx.int32_ty(0), ctx.int32_ty(0)]) + output_ptr = builder.gep(output_vec, [ctx.int32_ty(0), ctx.int32_ty(0)]) + + builtin = ctx.import_llvm_function("__pnl_builtin_vec_scalar_mult") + builder.call(builtin, [v_ptr, s, ctx.int32_ty(x), output_ptr]) + return output_vec + + def _mul(x, y): + if helpers.is_floating_point(x) and helpers.is_floating_point(y): + return self._generate_binop(x, y, lambda ctx, builder, x, y: builder.fmul(x,y)) + elif helpers.is_vector(x) and helpers.is_floating_point(y): + return self._generate_binop(x, y, _mul_vec_scalar) + elif helpers.is_floating_point(x) and helpers.is_vector(y): + return self._generate_binop(y, x, _mul_vec_scalar) + elif helpers.is_2d_matrix(x) and helpers.is_floating_point(y): + return self._generate_binop(x, y, _mul_mat_scalar) + elif helpers.is_floating_point(x) and helpers.is_2d_matrix(y): + return self._generate_binop(y, x, _mul_mat_scalar) + elif helpers.is_vector(x) and helpers.is_vector(y): + return self._generate_binop(x, y, _mul_vec) + elif helpers.is_2d_matrix(x) and helpers.is_2d_matrix(y): + return self._generate_binop(x, y, _mul_mat) + + return _mul + + def _generate_unop(self, x, callback): + if helpers.is_floating_point(x) and helpers.is_pointer(x): + x = self.builder.load(x) + + return callback(self.ctx, self.builder, x) + + def visit_USub(self, node): + def _usub_vec_mat(ctx, builder, x): + output = builder.alloca(x.type.pointee) + + helpers.call_elementwise_operation(ctx, builder, x, lambda ctx, builder, x: helpers.fneg(builder, x), output) + return output + + def _usub(x): + if helpers.is_floating_point(x): + return self._generate_unop(x, lambda ctx, builder, x: helpers.fneg(builder, x)) + elif helpers.is_vector(x) or helpers.is_2d_matrix(x): + return self._generate_unop(x, _usub_vec_mat) + + return _usub + + def visit_Name(self, node): + return self.register[node.id] + + def visit_Attribute(self, node): + val = self.visit(node.value) + return val[node.attr] + + def visit_Num(self, node): + return self.ctx.float_ty(node.n) + + def visit_Assign(self, node): + value = self.visit(node.value) + def _assign_target(target, value): + if isinstance(target, ast.Name): + id = target.id + self.register[id] = value + else: + to_store = value + if helpers.is_pointer(value): + to_store = builder.load(value) + + target = self.visit(target) + self.builder.store(to_store, target) + + for target in node.targets: + _assign_target(target, value) + + def visit_NameConstant(self, node): + val = self.name_constants[node.value] + assert val, f"Failed to convert NameConstant {node.value}" + return val + + def visit_Tuple(self, node): + elements = [self.visit(element) for element in node.elts] + + elements = [self.builder.load(element) if helpers.is_pointer(element) else element for element in elements] + + element_types = [element.type for element in elements] + ret_list = self.builder.alloca(ir.LiteralStructType(element_types)) + + for idx, element in enumerate(elements): + self.builder.store(element, self.builder.gep(ret_list, [self.ctx.int32_ty(0), self.ctx.int32_ty(idx)])) + + return ret_list + + def visit_BinOp(self, node): + operator = self.visit(node.op) + return operator(self.visit(node.left), self.visit(node.right)) + + def visit_BoolOp(self, node): + operator = self.visit(node.op) + values = [self.visit(value) for value in node.values] + ret_val = values[0] + for value in values[1:]: + ret_val = operator(ret_val, value) + return ret_val + + def visit_UnaryOp(self, node): + operator = self.visit(node.op) + return operator(self.visit(node.operand)) + + def visit_List(self, node): + elements = [self.visit(element) for element in node.elts] + element_ty = elements[0].type + assert all(element.type == element_ty for element in elements), f"Unable to convert {node} into a list! (Elements differ in type!)" + + # dereference pointers + if helpers.is_pointer(element_ty): + elements = [self.builder.load(element) for element in elements] + element_ty = elements[0].type + + ret_list = self.builder.alloca(ir.ArrayType(element_ty, len(elements))) + + for idx, element in enumerate(elements): + self.builder.store(element, self.builder.gep(ret_list, [self.ctx.int32_ty(0), self.ctx.int32_ty(idx)])) + return ret_list + + def visit_And(self, node): + def _and(x, y): + assert helpers.is_boolean(x), f"{x} is not a Boolean!" + assert helpers.is_boolean(y), f"{y} is not a Boolean!" + return self._generate_binop(x, y, lambda ctx, builder, x, y: builder.and_(x, y)) + + return _and + + def visit_Or(self, node): + def _or(x, y): + assert helpers.is_boolean(x), f"{x} is not a Boolean!" + assert helpers.is_boolean(y), f"{y} is not a Boolean!" + return self._generate_binop(x, y, lambda ctx, builder, x, y: builder.or_(x, y)) + + return _or + + def visit_Eq(self, node): + def _eq(x, y): + assert helpers.is_floating_point(x), f"{x} is not a floating point type!" + assert helpers.is_floating_point(y), f"{y} is not a floating point type!" + return self._generate_binop(x, y, lambda ctx, builder, x, y: builder.fcmp_ordered('==', x, y)) + + return _eq + + def visit_NotEq(self, node): + def _neq(x, y): + assert helpers.is_floating_point(x), f"{x} is not a floating point type!" + assert helpers.is_floating_point(y), f"{y} is not a floating point type!" + return self._generate_binop(x, y, lambda ctx, builder, x, y: builder.fcmp_ordered('!=', x, y)) + + return _neq + + def visit_Lt(self, node): + def _lt(x, y): + assert helpers.is_floating_point(x), f"{x} is not a floating point type!" + assert helpers.is_floating_point(y), f"{y} is not a floating point type!" + return self._generate_binop(x, y, lambda ctx, builder, x, y: builder.fcmp_ordered('<', x, y)) + + return _lt + + def visit_LtE(self, node): + def _lte(x, y): + assert helpers.is_floating_point(x), f"{x} is not a floating point type!" + assert helpers.is_floating_point(y), f"{y} is not a floating point type!" + return self._generate_binop(x, y, lambda ctx, builder, x, y: builder.fcmp_ordered('<=', x, y)) + + return _lte + + def visit_Gt(self, node): + def _gt(x, y): + assert helpers.is_floating_point(x), f"{x} is not a floating point type!" + assert helpers.is_floating_point(y), f"{y} is not a floating point type!" + return self._generate_binop(x, y, lambda ctx, builder, x, y: builder.fcmp_ordered('>', x, y)) + + return _gt + + def visit_GtE(self, node): + def _gte(x, y): + assert helpers.is_floating_point(x), f"{x} is not a floating point type!" + assert helpers.is_floating_point(y), f"{y} is not a floating point type!" + return self._generate_binop(x, y, lambda ctx, builder, x, y: builder.fcmp_ordered('>=', x, y)) + + return _gte + + def visit_Compare(self, node): + comp_val = self.visit(node.left) + comparators = [self.visit(comparator) for comparator in node.comparators] + ops = [self.visit(op) for op in node.ops] + for comparator, op in zip(comparators, ops): + comp_val = op(comp_val, comparator) + return comp_val + + def visit_If(self, node): + predicate = self.visit(node.test) + with self.builder.if_else(predicate) as (then, otherwise): + with then: + for child in node.body: + self.visit(child) + with otherwise: + for child in node.orelse: + self.visit(child) + + def visit_Return(self, node): + ret_val = self.visit(node.value) + arg_out = self.arg_out + + # dereference pointer + if helpers.is_pointer(ret_val): + ret_val = self.builder.load(ret_val) + + # get position in arg_out if types differ + if (helpers.is_scalar(ret_val) or helpers.is_vector(ret_val)) and helpers.is_2d_matrix(arg_out): + arg_out = self.builder.gep(arg_out, [self.ctx.int32_ty(0), self.ctx.int32_ty(0)]) + + if helpers.is_scalar(ret_val) and helpers.is_vector(arg_out): + arg_out = self.builder.gep(arg_out, [self.ctx.int32_ty(0), self.ctx.int32_ty(0)]) + + self.builder.store(ret_val, arg_out) + + def visit_Call(self, node): + call_func = self.visit(node.func) + assert callable(call_func), f"Uncallable function {node.func}!" + node_args = [self.visit(arg) for arg in node.args] + + return call_func(*node_args) + + def visit_Subscript(self, node): + node_val = self.visit(node.value) + node_slice_val = self.visit(node.slice) + return self.builder.gep(node_val, [self.ctx.int32_ty(0), node_slice_val]) + + def visit_Index(self, node): + return self.builder.fptoui(self.visit(node.value), self.ctx.int32_ty) def gen_node_wrapper(ctx, composition, node, *, tags:frozenset): assert "node_wrapper" in tags diff --git a/psyneulink/core/llvm/helpers.py b/psyneulink/core/llvm/helpers.py index 69a9e4409f9..d04a15ff8f5 100644 --- a/psyneulink/core/llvm/helpers.py +++ b/psyneulink/core/llvm/helpers.py @@ -130,19 +130,20 @@ def fneg(builder, val, name=""): def tanh(ctx, builder, x): # (e**2x - 1)/(e**2x + 1) - exp_f = ctx.get_builtin("exp", [x.type]) _2x = builder.fmul(x.type(2), x) - e2x = builder.call(exp_f, [_2x]) + e2x = exp(ctx, builder, _2x) num = builder.fsub(e2x, e2x.type(1)) den = builder.fadd(e2x, e2x.type(1)) return builder.fdiv(num, den) +def exp(ctx, builder, x): + exp_f = ctx.get_builtin("exp", [x.type]) + return builder.call(exp_f, [x]) def coth(ctx, builder, x): # (e**2x + 1)/(e**2x - 1) - exp_f = ctx.get_builtin("exp", [x.type]) _2x = builder.fmul(x.type(2), x) - e2x = builder.call(exp_f, [_2x]) + e2x = exp(ctx, builder, _2x) num = builder.fadd(e2x, e2x.type(1)) den = builder.fsub(e2x, e2x.type(1)) return builder.fdiv(num, den) @@ -150,14 +151,23 @@ def coth(ctx, builder, x): def csch(ctx, builder, x): # (2e**x)/(e**2x - 1) - exp_f = ctx.get_builtin("exp", [x.type]) - ex = builder.call(exp_f, [x]) + ex = exp(ctx, builder, x) num = builder.fmul(ex.type(2), ex) _2x = builder.fmul(x.type(2), x) - e2x = builder.call(exp_f, [_2x]) + e2x = exp(ctx, builder, _2x) den = builder.fsub(e2x, e2x.type(1)) return builder.fdiv(num, den) +def call_elementwise_operation(ctx, builder, x, operation, output_ptr): + """Recurse through an array structure and call operation on each scalar element of the structure. Store result in output_ptr""" + if isinstance(x.type.pointee, ir.ArrayType): + with array_ptr_loop(builder, x, str(x) + "_elementwise_op") as (b1, idx): + element_ptr = b1.gep(x, [ctx.int32_ty(0), idx]) + output_element_ptr = b1.gep(output_ptr, [ctx.int32_ty(0), idx]) + call_elementwise_operation(ctx, b1, element_ptr, operation, output_ptr=output_element_ptr) + else: + val = operation(ctx, builder, builder.load(x)) + builder.store(val, output_ptr) def is_close(builder, val1, val2, rtol=1e-05, atol=1e-08): diff = builder.fsub(val1, val2, "is_close_diff") @@ -190,6 +200,44 @@ def all_close(builder, arr1, arr2, rtol=1e-05, atol=1e-08): return builder.load(all_ptr) +def is_pointer(x): + type_t = getattr(x, "type", x) + return isinstance(type_t, ir.PointerType) + +def is_floating_point(x): + type_t = getattr(x, "type", x) + # dereference pointer + if is_pointer(x): + type_t = x.type.pointee + return isinstance(type_t, (ir.DoubleType, ir.FloatType, ir.HalfType)) + +def is_integer(x): + type_t = getattr(x, "type", x) + # dereference pointer + if is_pointer(x): + type_t = x.type.pointee + return isinstance(type_t, ir.IntType) + +def is_scalar(x): + return is_integer(x) or is_floating_point(x) + +def is_vector(x): + type_t = getattr(x, "type", x) + if is_pointer(x): + type_t = x.type.pointee + return isinstance(type_t, ir.ArrayType) and is_scalar(type_t.element) + +def is_2d_matrix(x): + type_t = getattr(x, "type", x) + if is_pointer(x): + type_t = x.type.pointee + return isinstance(type_t, ir.ArrayType) and is_vector(type_t.element) + +def is_boolean(x): + type_t = getattr(x, "type", x) + if is_pointer(x): + type_t = x.type.pointee + return isinstance(type_t, ir.IntType) and type_t.width == 1 def printf(builder, fmt, *args, override_debug=False): if "print_values" not in debug_env and not override_debug: diff --git a/psyneulink/core/rpc/graph.proto b/psyneulink/core/rpc/graph.proto index 535084afeac..73e37e88714 100644 --- a/psyneulink/core/rpc/graph.proto +++ b/psyneulink/core/rpc/graph.proto @@ -16,7 +16,9 @@ service ServeGraph { rpc LoadCustomPnl (PNLPath) returns (NullArgument) {} rpc LoadScript (ScriptPath) returns (ScriptCompositions) {} rpc LoadGraphics (ScriptPath) returns (StyleJSON) {} + rpc GetLoggableParameters (ComponentName) returns (ParameterList) {} rpc GetCompositions (NullArgument) returns (ScriptCompositions) {} + rpc GetComponents (GraphName) returns (ScriptComponents) {} rpc GetJSON (GraphName) returns (GraphJSON) {} rpc HealthCheck (NullArgument) returns (HealthStatus) {} rpc UpdateStylesheet (stream StyleJSON) returns (NullArgument) {} @@ -43,10 +45,22 @@ message ScriptCompositions { repeated string compositions = 1; } +message ScriptComponents { + repeated string components = 1; +} + message GraphName { string name = 1; } +message ParameterList { + repeated string parameters = 1; +} + +message ComponentName { + string name = 1; +} + message GraphJSON { string objectsJSON = 1; string styleJSON = 2; diff --git a/psyneulink/core/rpc/graph_pb2.py b/psyneulink/core/rpc/graph_pb2.py index f98898c8692..7e25a7271df 100644 --- a/psyneulink/core/rpc/graph_pb2.py +++ b/psyneulink/core/rpc/graph_pb2.py @@ -1,12 +1,3 @@ -# Princeton University licenses this file to You under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. You may obtain a copy of the License at: -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and limitations under the License. - -# ********************************** PNL ProtoBuf Server/Client Definitions ************************************** - # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: graph.proto @@ -22,13 +13,14 @@ + DESCRIPTOR = _descriptor.FileDescriptor( name='graph.proto', package='graph', syntax='proto3', serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_pb=b'\n\x0bgraph.proto\x12\x05graph\"\x0e\n\x0cNullArgument\"\x1e\n\x0cHealthStatus\x12\x0e\n\x06status\x18\x01 \x01(\t\"\x17\n\x07PNLPath\x12\x0c\n\x04path\x18\x01 \x01(\t\"\x1a\n\nScriptPath\x12\x0c\n\x04path\x18\x01 \x01(\t\"*\n\x12ScriptCompositions\x12\x14\n\x0c\x63ompositions\x18\x01 \x03(\t\"\x19\n\tGraphName\x12\x0c\n\x04name\x18\x01 \x01(\t\"3\n\tGraphJSON\x12\x13\n\x0bobjectsJSON\x18\x01 \x01(\t\x12\x11\n\tstyleJSON\x18\x02 \x01(\t\"\x1e\n\tStyleJSON\x12\x11\n\tstyleJSON\x18\x01 \x01(\t\"&\n\x07ndArray\x12\r\n\x05shape\x18\x01 \x03(\r\x12\x0c\n\x04\x64\x61ta\x18\x02 \x03(\x01\"6\n\x06Matrix\x12\x0c\n\x04rows\x18\x01 \x01(\r\x12\x0c\n\x04\x63ols\x18\x02 \x01(\r\x12\x10\n\x04\x64\x61ta\x18\x03 \x03(\x01\x42\x02\x10\x01\"s\n\x05\x45ntry\x12\x15\n\rcomponentName\x18\x01 \x01(\t\x12\x15\n\rparameterName\x18\x02 \x01(\t\x12\x0c\n\x04time\x18\x03 \x01(\t\x12\x0f\n\x07\x63ontext\x18\x04 \x01(\t\x12\x1d\n\x05value\x18\x05 \x01(\x0b\x32\x0e.graph.ndArray\"c\n\tServePref\x12\x15\n\rcomponentName\x18\x01 \x01(\t\x12\x15\n\rparameterName\x18\x02 \x01(\t\x12(\n\tcondition\x18\x03 \x01(\x0e\x32\x15.graph.serveCondition\"4\n\nServePrefs\x12&\n\x0cservePrefSet\x18\x01 \x03(\x0b\x32\x10.graph.ServePref\"\xa6\x01\n\rRunTimeParams\x12\x30\n\x06inputs\x18\x01 \x03(\x0b\x32 .graph.RunTimeParams.InputsEntry\x12%\n\nservePrefs\x18\x02 \x01(\x0b\x32\x11.graph.ServePrefs\x1a<\n\x0bInputsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.graph.Matrix:\x02\x38\x01*\x92\x01\n\x0eserveCondition\x12\x12\n\x0eINITIALIZATION\x10\x00\x12\x0e\n\nVALIDATION\x10\x01\x12\r\n\tEXECUTION\x10\x02\x12\x0e\n\nPROCESSING\x10\x03\x12\x0c\n\x08LEARNING\x10\x04\x12\x0b\n\x07\x43ONTROL\x10\x05\x12\x0e\n\nSIMULATION\x10\x06\x12\t\n\x05TRIAL\x10\x07\x12\x07\n\x03RUN\x10\x08\x32\xe3\x03\n\nServeGraph\x12\x36\n\rLoadCustomPnl\x12\x0e.graph.PNLPath\x1a\x13.graph.NullArgument\"\x00\x12<\n\nLoadScript\x12\x11.graph.ScriptPath\x1a\x19.graph.ScriptCompositions\"\x00\x12\x35\n\x0cLoadGraphics\x12\x11.graph.ScriptPath\x1a\x10.graph.StyleJSON\"\x00\x12\x43\n\x0fGetCompositions\x12\x13.graph.NullArgument\x1a\x19.graph.ScriptCompositions\"\x00\x12/\n\x07GetJSON\x12\x10.graph.GraphName\x1a\x10.graph.GraphJSON\"\x00\x12\x39\n\x0bHealthCheck\x12\x13.graph.NullArgument\x1a\x13.graph.HealthStatus\"\x00\x12=\n\x10UpdateStylesheet\x12\x10.graph.StyleJSON\x1a\x13.graph.NullArgument\"\x00(\x01\x12\x38\n\x0eRunComposition\x12\x14.graph.RunTimeParams\x1a\x0c.graph.Entry\"\x00\x30\x01\x62\x06proto3' + serialized_pb=b'\n\x0bgraph.proto\x12\x05graph\"\x0e\n\x0cNullArgument\"\x1e\n\x0cHealthStatus\x12\x0e\n\x06status\x18\x01 \x01(\t\"\x17\n\x07PNLPath\x12\x0c\n\x04path\x18\x01 \x01(\t\"\x1a\n\nScriptPath\x12\x0c\n\x04path\x18\x01 \x01(\t\"*\n\x12ScriptCompositions\x12\x14\n\x0c\x63ompositions\x18\x01 \x03(\t\"&\n\x10ScriptComponents\x12\x12\n\ncomponents\x18\x01 \x03(\t\"\x19\n\tGraphName\x12\x0c\n\x04name\x18\x01 \x01(\t\"#\n\rParameterList\x12\x12\n\nparameters\x18\x01 \x03(\t\"\x1d\n\rComponentName\x12\x0c\n\x04name\x18\x01 \x01(\t\"3\n\tGraphJSON\x12\x13\n\x0bobjectsJSON\x18\x01 \x01(\t\x12\x11\n\tstyleJSON\x18\x02 \x01(\t\"\x1e\n\tStyleJSON\x12\x11\n\tstyleJSON\x18\x01 \x01(\t\"&\n\x07ndArray\x12\r\n\x05shape\x18\x01 \x03(\r\x12\x0c\n\x04\x64\x61ta\x18\x02 \x03(\x01\"6\n\x06Matrix\x12\x0c\n\x04rows\x18\x01 \x01(\r\x12\x0c\n\x04\x63ols\x18\x02 \x01(\r\x12\x10\n\x04\x64\x61ta\x18\x03 \x03(\x01\x42\x02\x10\x01\"s\n\x05\x45ntry\x12\x15\n\rcomponentName\x18\x01 \x01(\t\x12\x15\n\rparameterName\x18\x02 \x01(\t\x12\x0c\n\x04time\x18\x03 \x01(\t\x12\x0f\n\x07\x63ontext\x18\x04 \x01(\t\x12\x1d\n\x05value\x18\x05 \x01(\x0b\x32\x0e.graph.ndArray\"c\n\tServePref\x12\x15\n\rcomponentName\x18\x01 \x01(\t\x12\x15\n\rparameterName\x18\x02 \x01(\t\x12(\n\tcondition\x18\x03 \x01(\x0e\x32\x15.graph.serveCondition\"4\n\nServePrefs\x12&\n\x0cservePrefSet\x18\x01 \x03(\x0b\x32\x10.graph.ServePref\"\xa6\x01\n\rRunTimeParams\x12\x30\n\x06inputs\x18\x01 \x03(\x0b\x32 .graph.RunTimeParams.InputsEntry\x12%\n\nservePrefs\x18\x02 \x01(\x0b\x32\x11.graph.ServePrefs\x1a<\n\x0bInputsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\x1c\n\x05value\x18\x02 \x01(\x0b\x32\r.graph.Matrix:\x02\x38\x01*\x92\x01\n\x0eserveCondition\x12\x12\n\x0eINITIALIZATION\x10\x00\x12\x0e\n\nVALIDATION\x10\x01\x12\r\n\tEXECUTION\x10\x02\x12\x0e\n\nPROCESSING\x10\x03\x12\x0c\n\x08LEARNING\x10\x04\x12\x0b\n\x07\x43ONTROL\x10\x05\x12\x0e\n\nSIMULATION\x10\x06\x12\t\n\x05TRIAL\x10\x07\x12\x07\n\x03RUN\x10\x08\x32\xe8\x04\n\nServeGraph\x12\x36\n\rLoadCustomPnl\x12\x0e.graph.PNLPath\x1a\x13.graph.NullArgument\"\x00\x12<\n\nLoadScript\x12\x11.graph.ScriptPath\x1a\x19.graph.ScriptCompositions\"\x00\x12\x35\n\x0cLoadGraphics\x12\x11.graph.ScriptPath\x1a\x10.graph.StyleJSON\"\x00\x12\x45\n\x15GetLoggableParameters\x12\x14.graph.ComponentName\x1a\x14.graph.ParameterList\"\x00\x12\x43\n\x0fGetCompositions\x12\x13.graph.NullArgument\x1a\x19.graph.ScriptCompositions\"\x00\x12<\n\rGetComponents\x12\x10.graph.GraphName\x1a\x17.graph.ScriptComponents\"\x00\x12/\n\x07GetJSON\x12\x10.graph.GraphName\x1a\x10.graph.GraphJSON\"\x00\x12\x39\n\x0bHealthCheck\x12\x13.graph.NullArgument\x1a\x13.graph.HealthStatus\"\x00\x12=\n\x10UpdateStylesheet\x12\x10.graph.StyleJSON\x1a\x13.graph.NullArgument\"\x00(\x01\x12\x38\n\x0eRunComposition\x12\x14.graph.RunTimeParams\x1a\x0c.graph.Entry\"\x00\x30\x01\x62\x06proto3' ) _SERVECONDITION = _descriptor.EnumDescriptor( @@ -86,8 +78,8 @@ ], containing_type=None, serialized_options=None, - serialized_start=817, - serialized_end=963, + serialized_start=925, + serialized_end=1071, ) _sym_db.RegisterEnumDescriptor(_SERVECONDITION) @@ -257,6 +249,38 @@ ) +_SCRIPTCOMPONENTS = _descriptor.Descriptor( + name='ScriptComponents', + full_name='graph.ScriptComponents', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='components', full_name='graph.ScriptComponents.components', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=167, + serialized_end=205, +) + + _GRAPHNAME = _descriptor.Descriptor( name='GraphName', full_name='graph.GraphName', @@ -284,8 +308,72 @@ extension_ranges=[], oneofs=[ ], - serialized_start=167, - serialized_end=192, + serialized_start=207, + serialized_end=232, +) + + +_PARAMETERLIST = _descriptor.Descriptor( + name='ParameterList', + full_name='graph.ParameterList', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='parameters', full_name='graph.ParameterList.parameters', index=0, + number=1, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=234, + serialized_end=269, +) + + +_COMPONENTNAME = _descriptor.Descriptor( + name='ComponentName', + full_name='graph.ComponentName', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='graph.ComponentName.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[ + ], + serialized_start=271, + serialized_end=300, ) @@ -323,8 +411,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=194, - serialized_end=245, + serialized_start=302, + serialized_end=353, ) @@ -355,8 +443,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=247, - serialized_end=277, + serialized_start=355, + serialized_end=385, ) @@ -394,8 +482,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=279, - serialized_end=317, + serialized_start=387, + serialized_end=425, ) @@ -440,8 +528,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=319, - serialized_end=373, + serialized_start=427, + serialized_end=481, ) @@ -500,8 +588,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=375, - serialized_end=490, + serialized_start=483, + serialized_end=598, ) @@ -546,8 +634,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=492, - serialized_end=591, + serialized_start=600, + serialized_end=699, ) @@ -578,8 +666,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=593, - serialized_end=645, + serialized_start=701, + serialized_end=753, ) @@ -617,8 +705,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=754, - serialized_end=814, + serialized_start=862, + serialized_end=922, ) _RUNTIMEPARAMS = _descriptor.Descriptor( @@ -655,8 +743,8 @@ extension_ranges=[], oneofs=[ ], - serialized_start=648, - serialized_end=814, + serialized_start=756, + serialized_end=922, ) _ENTRY.fields_by_name['value'].message_type = _NDARRAY @@ -671,7 +759,10 @@ DESCRIPTOR.message_types_by_name['PNLPath'] = _PNLPATH DESCRIPTOR.message_types_by_name['ScriptPath'] = _SCRIPTPATH DESCRIPTOR.message_types_by_name['ScriptCompositions'] = _SCRIPTCOMPOSITIONS +DESCRIPTOR.message_types_by_name['ScriptComponents'] = _SCRIPTCOMPONENTS DESCRIPTOR.message_types_by_name['GraphName'] = _GRAPHNAME +DESCRIPTOR.message_types_by_name['ParameterList'] = _PARAMETERLIST +DESCRIPTOR.message_types_by_name['ComponentName'] = _COMPONENTNAME DESCRIPTOR.message_types_by_name['GraphJSON'] = _GRAPHJSON DESCRIPTOR.message_types_by_name['StyleJSON'] = _STYLEJSON DESCRIPTOR.message_types_by_name['ndArray'] = _NDARRAY @@ -718,6 +809,13 @@ }) _sym_db.RegisterMessage(ScriptCompositions) +ScriptComponents = _reflection.GeneratedProtocolMessageType('ScriptComponents', (_message.Message,), { + 'DESCRIPTOR' : _SCRIPTCOMPONENTS, + '__module__' : 'graph_pb2' + # @@protoc_insertion_point(class_scope:graph.ScriptComponents) + }) +_sym_db.RegisterMessage(ScriptComponents) + GraphName = _reflection.GeneratedProtocolMessageType('GraphName', (_message.Message,), { 'DESCRIPTOR' : _GRAPHNAME, '__module__' : 'graph_pb2' @@ -725,6 +823,20 @@ }) _sym_db.RegisterMessage(GraphName) +ParameterList = _reflection.GeneratedProtocolMessageType('ParameterList', (_message.Message,), { + 'DESCRIPTOR' : _PARAMETERLIST, + '__module__' : 'graph_pb2' + # @@protoc_insertion_point(class_scope:graph.ParameterList) + }) +_sym_db.RegisterMessage(ParameterList) + +ComponentName = _reflection.GeneratedProtocolMessageType('ComponentName', (_message.Message,), { + 'DESCRIPTOR' : _COMPONENTNAME, + '__module__' : 'graph_pb2' + # @@protoc_insertion_point(class_scope:graph.ComponentName) + }) +_sym_db.RegisterMessage(ComponentName) + GraphJSON = _reflection.GeneratedProtocolMessageType('GraphJSON', (_message.Message,), { 'DESCRIPTOR' : _GRAPHJSON, '__module__' : 'graph_pb2' @@ -800,8 +912,8 @@ index=0, serialized_options=None, create_key=_descriptor._internal_create_key, - serialized_start=966, - serialized_end=1449, + serialized_start=1074, + serialized_end=1690, methods=[ _descriptor.MethodDescriptor( name='LoadCustomPnl', @@ -833,20 +945,40 @@ serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='GetLoggableParameters', + full_name='graph.ServeGraph.GetLoggableParameters', + index=3, + containing_service=None, + input_type=_COMPONENTNAME, + output_type=_PARAMETERLIST, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='GetCompositions', full_name='graph.ServeGraph.GetCompositions', - index=3, + index=4, containing_service=None, input_type=_NULLARGUMENT, output_type=_SCRIPTCOMPOSITIONS, serialized_options=None, create_key=_descriptor._internal_create_key, ), + _descriptor.MethodDescriptor( + name='GetComponents', + full_name='graph.ServeGraph.GetComponents', + index=5, + containing_service=None, + input_type=_GRAPHNAME, + output_type=_SCRIPTCOMPONENTS, + serialized_options=None, + create_key=_descriptor._internal_create_key, + ), _descriptor.MethodDescriptor( name='GetJSON', full_name='graph.ServeGraph.GetJSON', - index=4, + index=6, containing_service=None, input_type=_GRAPHNAME, output_type=_GRAPHJSON, @@ -856,7 +988,7 @@ _descriptor.MethodDescriptor( name='HealthCheck', full_name='graph.ServeGraph.HealthCheck', - index=5, + index=7, containing_service=None, input_type=_NULLARGUMENT, output_type=_HEALTHSTATUS, @@ -866,7 +998,7 @@ _descriptor.MethodDescriptor( name='UpdateStylesheet', full_name='graph.ServeGraph.UpdateStylesheet', - index=6, + index=8, containing_service=None, input_type=_STYLEJSON, output_type=_NULLARGUMENT, @@ -876,7 +1008,7 @@ _descriptor.MethodDescriptor( name='RunComposition', full_name='graph.ServeGraph.RunComposition', - index=7, + index=9, containing_service=None, input_type=_RUNTIMEPARAMS, output_type=_ENTRY, diff --git a/psyneulink/core/rpc/graph_pb2_grpc.py b/psyneulink/core/rpc/graph_pb2_grpc.py index 784ff03ab1a..d6bcc244fa3 100644 --- a/psyneulink/core/rpc/graph_pb2_grpc.py +++ b/psyneulink/core/rpc/graph_pb2_grpc.py @@ -1,18 +1,8 @@ -# Princeton University licenses this file to You under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. You may obtain a copy of the License at: -# http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed -# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and limitations under the License. - -# ********************************** PNL ProtoBuf Python Classes ************************************************* - - # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc -from . import graph_pb2 as graph__pb2 +from psyneulink.core.rpc import graph_pb2 as graph__pb2 class ServeGraphStub(object): @@ -39,11 +29,21 @@ def __init__(self, channel): request_serializer=graph__pb2.ScriptPath.SerializeToString, response_deserializer=graph__pb2.StyleJSON.FromString, ) + self.GetLoggableParameters = channel.unary_unary( + '/graph.ServeGraph/GetLoggableParameters', + request_serializer=graph__pb2.ComponentName.SerializeToString, + response_deserializer=graph__pb2.ParameterList.FromString, + ) self.GetCompositions = channel.unary_unary( '/graph.ServeGraph/GetCompositions', request_serializer=graph__pb2.NullArgument.SerializeToString, response_deserializer=graph__pb2.ScriptCompositions.FromString, ) + self.GetComponents = channel.unary_unary( + '/graph.ServeGraph/GetComponents', + request_serializer=graph__pb2.GraphName.SerializeToString, + response_deserializer=graph__pb2.ScriptComponents.FromString, + ) self.GetJSON = channel.unary_unary( '/graph.ServeGraph/GetJSON', request_serializer=graph__pb2.GraphName.SerializeToString, @@ -87,12 +87,24 @@ def LoadGraphics(self, request, context): context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def GetLoggableParameters(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def GetCompositions(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) context.set_details('Method not implemented!') raise NotImplementedError('Method not implemented!') + def GetComponents(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + def GetJSON(self, request, context): """Missing associated documentation comment in .proto file.""" context.set_code(grpc.StatusCode.UNIMPLEMENTED) @@ -135,11 +147,21 @@ def add_ServeGraphServicer_to_server(servicer, server): request_deserializer=graph__pb2.ScriptPath.FromString, response_serializer=graph__pb2.StyleJSON.SerializeToString, ), + 'GetLoggableParameters': grpc.unary_unary_rpc_method_handler( + servicer.GetLoggableParameters, + request_deserializer=graph__pb2.ComponentName.FromString, + response_serializer=graph__pb2.ParameterList.SerializeToString, + ), 'GetCompositions': grpc.unary_unary_rpc_method_handler( servicer.GetCompositions, request_deserializer=graph__pb2.NullArgument.FromString, response_serializer=graph__pb2.ScriptCompositions.SerializeToString, ), + 'GetComponents': grpc.unary_unary_rpc_method_handler( + servicer.GetComponents, + request_deserializer=graph__pb2.GraphName.FromString, + response_serializer=graph__pb2.ScriptComponents.SerializeToString, + ), 'GetJSON': grpc.unary_unary_rpc_method_handler( servicer.GetJSON, request_deserializer=graph__pb2.GraphName.FromString, @@ -176,6 +198,7 @@ def LoadCustomPnl(request, options=(), channel_credentials=None, call_credentials=None, + insecure=False, compression=None, wait_for_ready=None, timeout=None, @@ -184,7 +207,7 @@ def LoadCustomPnl(request, graph__pb2.PNLPath.SerializeToString, graph__pb2.NullArgument.FromString, options, channel_credentials, - call_credentials, compression, wait_for_ready, timeout, metadata) + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def LoadScript(request, @@ -192,6 +215,7 @@ def LoadScript(request, options=(), channel_credentials=None, call_credentials=None, + insecure=False, compression=None, wait_for_ready=None, timeout=None, @@ -200,7 +224,7 @@ def LoadScript(request, graph__pb2.ScriptPath.SerializeToString, graph__pb2.ScriptCompositions.FromString, options, channel_credentials, - call_credentials, compression, wait_for_ready, timeout, metadata) + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def LoadGraphics(request, @@ -208,6 +232,7 @@ def LoadGraphics(request, options=(), channel_credentials=None, call_credentials=None, + insecure=False, compression=None, wait_for_ready=None, timeout=None, @@ -216,7 +241,24 @@ def LoadGraphics(request, graph__pb2.ScriptPath.SerializeToString, graph__pb2.StyleJSON.FromString, options, channel_credentials, - call_credentials, compression, wait_for_ready, timeout, metadata) + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def GetLoggableParameters(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/graph.ServeGraph/GetLoggableParameters', + graph__pb2.ComponentName.SerializeToString, + graph__pb2.ParameterList.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def GetCompositions(request, @@ -224,6 +266,7 @@ def GetCompositions(request, options=(), channel_credentials=None, call_credentials=None, + insecure=False, compression=None, wait_for_ready=None, timeout=None, @@ -232,7 +275,24 @@ def GetCompositions(request, graph__pb2.NullArgument.SerializeToString, graph__pb2.ScriptCompositions.FromString, options, channel_credentials, - call_credentials, compression, wait_for_ready, timeout, metadata) + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def GetComponents(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/graph.ServeGraph/GetComponents', + graph__pb2.GraphName.SerializeToString, + graph__pb2.ScriptComponents.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def GetJSON(request, @@ -240,6 +300,7 @@ def GetJSON(request, options=(), channel_credentials=None, call_credentials=None, + insecure=False, compression=None, wait_for_ready=None, timeout=None, @@ -248,7 +309,7 @@ def GetJSON(request, graph__pb2.GraphName.SerializeToString, graph__pb2.GraphJSON.FromString, options, channel_credentials, - call_credentials, compression, wait_for_ready, timeout, metadata) + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def HealthCheck(request, @@ -256,6 +317,7 @@ def HealthCheck(request, options=(), channel_credentials=None, call_credentials=None, + insecure=False, compression=None, wait_for_ready=None, timeout=None, @@ -264,7 +326,7 @@ def HealthCheck(request, graph__pb2.NullArgument.SerializeToString, graph__pb2.HealthStatus.FromString, options, channel_credentials, - call_credentials, compression, wait_for_ready, timeout, metadata) + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def UpdateStylesheet(request_iterator, @@ -272,6 +334,7 @@ def UpdateStylesheet(request_iterator, options=(), channel_credentials=None, call_credentials=None, + insecure=False, compression=None, wait_for_ready=None, timeout=None, @@ -280,7 +343,7 @@ def UpdateStylesheet(request_iterator, graph__pb2.StyleJSON.SerializeToString, graph__pb2.NullArgument.FromString, options, channel_credentials, - call_credentials, compression, wait_for_ready, timeout, metadata) + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) @staticmethod def RunComposition(request, @@ -288,6 +351,7 @@ def RunComposition(request, options=(), channel_credentials=None, call_credentials=None, + insecure=False, compression=None, wait_for_ready=None, timeout=None, @@ -296,4 +360,4 @@ def RunComposition(request, graph__pb2.RunTimeParams.SerializeToString, graph__pb2.Entry.FromString, options, channel_credentials, - call_credentials, compression, wait_for_ready, timeout, metadata) + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py index 907da8c81e4..869bf125113 100644 --- a/psyneulink/library/components/mechanisms/processing/integrator/ddm.py +++ b/psyneulink/library/components/mechanisms/processing/integrator/ddm.py @@ -1077,8 +1077,8 @@ def _execute( format(self.function.name, self.name)) # Convert ER to decision variable: - threshold = float(self.function._get_current_function_param(THRESHOLD, context)) - random_state = self._get_current_mechanism_param("random_state", context) + threshold = float(self.function._get_current_parameter_value(THRESHOLD, context)) + random_state = self._get_current_parameter_value(self.parameters.random_state, context) if random_state.rand() < return_value[self.PROBABILITY_LOWER_THRESHOLD_INDEX]: return_value[self.DECISION_VARIABLE_INDEX] = np.atleast_1d(-1 * threshold) else: @@ -1185,14 +1185,14 @@ def is_finished(self, context=None): break if ( - abs(single_value) >= self.function._get_current_function_param(THRESHOLD, context) + abs(single_value) >= self.function._get_current_parameter_value(THRESHOLD, context) and isinstance(self.function, IntegratorFunction) ): logger.info( '{0} {1} has reached threshold {2}'.format( type(self).__name__, self.name, - self.function._get_current_function_param(THRESHOLD, context) + self.function._get_current_parameter_value(THRESHOLD, context) ) ) return True diff --git a/psyneulink/library/components/mechanisms/processing/transfer/kwtamechanism.py b/psyneulink/library/components/mechanisms/processing/transfer/kwtamechanism.py index b9d42f6ff00..c6087b2db2c 100644 --- a/psyneulink/library/components/mechanisms/processing/transfer/kwtamechanism.py +++ b/psyneulink/library/components/mechanisms/processing/transfer/kwtamechanism.py @@ -422,11 +422,11 @@ def _instantiate_attributes_before_function(self, function=None, context=None): self.indexOfInhibitionInputPort = len(self.input_ports) - 1 def _kwta_scale(self, current_input, context=None): - k_value = self._get_current_mechanism_param("k_value", context) - threshold = self._get_current_mechanism_param("threshold", context) - average_based = self._get_current_mechanism_param("average_based", context) - ratio = self._get_current_mechanism_param("ratio", context) - inhibition_only = self._get_current_mechanism_param("inhibition_only", context) + k_value = self._get_current_parameter_value(self.parameters.k_value, context) + threshold = self._get_current_parameter_value(self.parameters.threshold, context) + average_based = self._get_current_parameter_value(self.parameters.average_based, context) + ratio = self._get_current_parameter_value(self.parameters.ratio, context) + inhibition_only = self._get_current_parameter_value(self.parameters.inhibition_only, context) try: int_k_value = int(k_value[0]) diff --git a/psyneulink/library/components/mechanisms/processing/transfer/lcamechanism.py b/psyneulink/library/components/mechanisms/processing/transfer/lcamechanism.py index 6b314ed440d..a84e04befcb 100644 --- a/psyneulink/library/components/mechanisms/processing/transfer/lcamechanism.py +++ b/psyneulink/library/components/mechanisms/processing/transfer/lcamechanism.py @@ -595,8 +595,8 @@ def _parse_threshold_args(self, kwargs): def _get_integrated_function_input(self, function_variable, initial_value, noise, context): - leak = self._get_current_mechanism_param("leak", context) - time_step_size = self._get_current_mechanism_param("time_step_size", context) + leak = self._get_current_parameter_value(self.parameters.leak, context) + time_step_size = self._get_current_parameter_value(self.parameters.time_step_size, context) # if not self.integrator_function: if self.initialization_status == ContextFlags.INITIALIZING: diff --git a/psyneulink/library/components/mechanisms/processing/transfer/recurrenttransfermechanism.py b/psyneulink/library/components/mechanisms/processing/transfer/recurrenttransfermechanism.py index 4d399d99df0..58d5e24c0be 100644 --- a/psyneulink/library/components/mechanisms/processing/transfer/recurrenttransfermechanism.py +++ b/psyneulink/library/components/mechanisms/processing/transfer/recurrenttransfermechanism.py @@ -865,7 +865,7 @@ def _instantiate_attributes_before_function(self, function=None, context=None): self.auto = d if port is not None: self._parameter_ports[AUTO] = port - port.source = self + port.source = self.parameters.auto else: raise RecurrentTransferError("Failed to create ParameterPort for `auto` attribute for {} \"{}\"". format(self.__class__.__name__, self.name)) @@ -883,7 +883,7 @@ def _instantiate_attributes_before_function(self, function=None, context=None): context=context) if port is not None: self._parameter_ports[HETERO] = port - port.source = self + port.source = self.parameters.hetero else: raise RecurrentTransferError("Failed to create ParameterPort for `hetero` attribute for {} \"{}\"". format(self.__class__.__name__, self.name)) @@ -938,9 +938,6 @@ def _instantiate_attributes_before_function(self, function=None, context=None): else: self.combination_function = comb_fct - else: - self.combination_function = None - if self.auto is None and self.hetero is None: self.matrix = matrix if self.matrix is None: diff --git a/psyneulink/library/compositions/pytorchcomponents.py b/psyneulink/library/compositions/pytorchcomponents.py index 4ef2ebdff58..c5b83c6df3d 100644 --- a/psyneulink/library/compositions/pytorchcomponents.py +++ b/psyneulink/library/compositions/pytorchcomponents.py @@ -13,7 +13,7 @@ def pytorch_function_creator(function, device, context=None): NOTE: This is needed due to PyTorch limitations (see: https://github.com/PrincetonUniversity/PsyNeuLink/pull/1657#discussion_r437489990) """ def get_fct_param_value(param_name): - val = function._get_current_function_param( + val = function._get_current_parameter_value( param_name, context=context) if val is None: val = getattr(function.defaults, param_name) diff --git a/requirements.txt b/requirements.txt index ca63f83c76e..e1e07dc8280 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,6 @@ matplotlib networkx==1.11 numpy pillow -toposort==1.4 +toposort torch; sys_platform != 'win32' and platform_machine == 'x86_64' and platform_python_implementation == 'CPython' typecheck-decorator==1.2 diff --git a/setup.cfg b/setup.cfg index cfd05bd17ef..192250dc538 100644 --- a/setup.cfg +++ b/setup.cfg @@ -63,6 +63,7 @@ xfail_strict = True filterwarnings = error:Creating an ndarray from ragged nested sequences \(which is a list-or-tuple of lists-or-tuples-or ndarrays with different lengths or shapes\) is deprecated.*:numpy.VisibleDeprecationWarning + ignore:Multiple ParameterPorts:UserWarning [pycodestyle] # for code explanation see https://pep8.readthedocs.io/en/latest/intro.html#error-codes diff --git a/tests/composition/test_composition.py b/tests/composition/test_composition.py index 421b71f8a82..98f1516a2db 100644 --- a/tests/composition/test_composition.py +++ b/tests/composition/test_composition.py @@ -30,8 +30,9 @@ from psyneulink.core.components.ports.modulatorysignals.controlsignal import ControlSignal, CostFunctions from psyneulink.core.compositions.composition import Composition, CompositionError, NodeRole from psyneulink.core.compositions.pathway import Pathway, PathwayRole +from psyneulink.core.globals.context import Context from psyneulink.core.globals.keywords import \ - ADDITIVE, ALLOCATION_SAMPLES, BEFORE, DISABLE, INPUT_PORT, INTERCEPT, LEARNING_MECHANISMS, LEARNED_PROJECTIONS, \ + ADDITIVE, ALLOCATION_SAMPLES, BEFORE, DEFAULT, DISABLE, INPUT_PORT, INTERCEPT, LEARNING_MECHANISMS, LEARNED_PROJECTIONS, \ NAME, PROJECTIONS, RESULT, OBJECTIVE_MECHANISM, OUTPUT_MECHANISM, OVERRIDE, SLOPE, TARGET_MECHANISM, VARIANCE from psyneulink.core.scheduling.condition import AfterNCalls, AtTimeStep, AtTrial, Never from psyneulink.core.scheduling.condition import EveryNCalls @@ -6269,8 +6270,7 @@ def test_initialize_cycle_values(self): abc_Composition.run(inputs={A: [1.0, 2.0, 3.0]}, initialize_cycle_values={C: 2.0}) - abc_Composition.run(inputs={A: [1.0, 2.0, 3.0]}, - initialize_cycle_values={C: 2.0}) + abc_Composition.run(inputs={A: [1.0, 2.0, 3.0]}) # Run 1 --> Execution 1: 1 + 2 = 3 | Execution 2: 3 + 2 = 5 | Execution 3: 5 + 3 = 8 # Run 2 --> Execution 1: 8 + 1 = 9 | Execution 2: 9 + 2 = 11 | Execution 3: 11 + 3 = 14 @@ -6287,29 +6287,103 @@ def test_initialize_cycle_values_warning(self): a_Composition.run(inputs={A:[1]}, initialize_cycle_values={A:[1]}) - -class TestResetValues: - - def test_initialize_all_mechanisms(self): + @pytest.mark.parametrize("context_specified", [True, False]) + def test_initialize_cycles(self, context_specified): A = TransferMechanism(name='A') B = TransferMechanism(name='B') C = RecurrentTransferMechanism(name='C', auto=1.0) - comp = Composition(pathways=[A,B,C]) - C.log.set_log_conditions('value') - comp.run(inputs={A: [1.0, 2.0, 3.0]}, - initialize_cycle_values={C: [2.0]}) - comp.run(inputs={A: [1.0, 2.0, 3.0]}, - initialize_cycle_values={C: [2.0]}) + context = Context(execution_id='a') if context_specified else NotImplemented - assert np.allclose( - C.log.nparray_dictionary('value')[comp.default_execution_id]['value'], - # Value of C: - # Run 1 --> Execution 1: 1 + 2 = 3 | Execution 2: 3 + 2 = 5 | Execution 3: 5 + 3 = 8 - # Run 2 --> Execution 1: 8 + 1 = 9 | Execution 2: 9 + 2 = 11 | Execution 3: 11 + 3 = 14 - [[[3]], [[5]], [[8]], [[9]], [[11]], [[14]]] + abc_Composition = Composition(pathways=[[A, B, C]]) + + abc_Composition.initialize({C: 2.0}, context=context) + + abc_Composition.run(inputs={A: [1.0, 2.0, 3.0]}, context=context) + + if not context_specified: + abc_Composition.run() + + abc_Composition.run(inputs={A: [1.0, 2.0, 3.0]}, context=context) + + # Run 1 --> Execution 1: 1 + 2 = 3 | Execution 2: 3 + 2 = 5 | Execution 3: 5 + 3 = 8 + # Run 2 --> Execution 1: 8 + 1 = 9 | Execution 2: 9 + 2 = 11 | Execution 3: 11 + 3 = 14 + assert abc_Composition.results == [[[3]], [[5]], [[8]], [[9]], [[11]], [[14]]] + + def test_initialize_cycles_excluding_unspecified_nodes(self): + A = ProcessingMechanism(name='A') + B = ProcessingMechanism(name='B') + C = ProcessingMechanism(name='C') + + comp = Composition(pathways=[A, B, C, A]) + comp.run({A: 1, B: 1, C: 1}) + context = comp.most_recent_context + + assert A.parameters.value._get(context) == 1 + assert B.parameters.value._get(context) == 1 + assert C.parameters.value._get(context) == 1 + + # ALL: value of preceding node + value from input CIM == 0 + 1 == 1 + + # initialize B to 0 + comp.initialize({B: 0}, include_unspecified_nodes=False) + + assert A.parameters.value._get(context) == 1 + assert B.parameters.value._get(context) == 0 + assert C.parameters.value._get(context) == 1 + + comp.run({A: 0, B: 0, C: 0}) + + assert A.parameters.value._get(context) == 1 + assert B.parameters.value._get(context) == 1 + assert C.parameters.value._get(context) == 0 + + # A and B: value of preceding node + value from input CIM == 1 + 0 == 1 + # C: value of preceding node + value from input CIM == 0 + 0 == 0 + + def test_initialize_cycles_using_default_keyword(self): + A = ProcessingMechanism(name='A', default_variable=1) + B = ProcessingMechanism(name='B', default_variable=1) + C = ProcessingMechanism(name='C', default_variable=1) + + comp = Composition(pathways=[A, B, C, A]) + comp.run({A: 1, B: 1, C: 1}) + context = comp.most_recent_context + + assert A.parameters.value._get(context) == 2 + assert B.parameters.value._get(context) == 2 + assert C.parameters.value._get(context) == 2 + + # initialize all nodes to their default values + comp.initialize({A: DEFAULT, B: DEFAULT, C: DEFAULT}) + + assert A.parameters.value._get(context) == 1 + assert B.parameters.value._get(context) == 1 + assert C.parameters.value._get(context) == 1 + + def test_initialize_cycles_error(self): + a = ProcessingMechanism(name='mech_a') + b = ProcessingMechanism(name='mech_b') + comp = Composition(nodes=[b]) + error_text = ( + f"{a.name} [(]entry in initialize values arg[)] is not a node in '{comp.name}'" + ) + with pytest.raises(CompositionError, match=error_text): + comp.initialize({a: 1}) + + def test_initialize_cycles_warning(self): + a = ProcessingMechanism(name='mech_a') + comp = Composition(nodes=[a]) + warning_text = ( + f"A value is specified for {a.name} of {comp.name} in the 'initialize_cycle_values' " + f"argument of call to run, but it is neither part of a cycle nor a FEEDBACK_SENDER. " + f"Its value will be overwritten when the node first executes, and therefore not used." ) + with pytest.warns(UserWarning, match=warning_text): + comp.run(initialize_cycle_values={a: 1}) + +class TestResetValues: def test_reset_one_mechanism_through_run(self): A = TransferMechanism(name='A') diff --git a/tests/composition/test_show_graph.py b/tests/composition/test_show_graph.py index 7570727784c..9594c4e3fdb 100644 --- a/tests/composition/test_show_graph.py +++ b/tests/composition/test_show_graph.py @@ -195,7 +195,7 @@ def test_no_nested_and_controler_name_with_space_in_it(self): gv = comp.show_graph(show_controller=True, output_fmt='source') assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tia [color=green penwidth=3 rank=source shape=oval]\n\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t"my ocm" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t"my ocm" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t"my ocm" -> ib [label="" arrowhead=box color=purple penwidth=1]\n\tib [color=red penwidth=3 rank=max shape=oval]\n\t"my ocm" [color=purple penwidth=1 rank=min shape=doubleoctagon]\n}' gv = comp.show_graph(show_controller=True, show_node_structure=True, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tia:"OutputPort-RESULT" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"my ocm":"OutputPort-ia[noise] ControlSignal" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t"my ocm":"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t"my ocm":"OutputPort-ib[slope] ControlSignal" -> ib:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tib [label=<
RESULT
OutputPorts
Mechanism:
ib
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t"my ocm" [label=<
ia[noise] ControlSignalia[intercept] ControlSignalib[slope] ControlSignal
OutputPorts
Mechanism:
my ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tia:"OutputPort-RESULT" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"my ocm":"OutputPort-ia[noise] ControlSignal" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t"my ocm":"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t"my ocm":"OutputPort-ib[slope] ControlSignal" -> ib:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tib [label=<
RESULT
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t"my ocm" [label=<
ia[noise] ControlSignalia[intercept] ControlSignalib[slope] ControlSignal
OutputPorts
Mechanism:
my ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' def test_multiple_nesting_levels_with_control_mech_projection_one_level_deep(self): @@ -226,7 +226,7 @@ def test_multiple_nesting_levels_with_control_mech_projection_one_level_deep(sel gv = ocomp.show_graph(show_nested=INSET, output_fmt='source') assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [color=green penwidth=3 rank=source shape=oval]\n\tmcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech -> mcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa -> mcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmcomp -> ob [label="" arrowhead=normal color=black penwidth=1]\n\tob -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\tob [color=red penwidth=3 rank=max shape=oval]\n\tctl_mech [color=blue penwidth=3 rank=max shape=octagon]\n\tsubgraph cluster_mcomp {\n\t\tgraph [label=mcomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tma [color=green penwidth=3 rank=source shape=oval]\n\t\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\t\tma -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\t\ticomp -> mb [label="" arrowhead=normal color=black penwidth=1]\n\t\tmb [color=red penwidth=3 rank=max shape=oval]\n\t\tsubgraph cluster_icomp {\n\t\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\t\tedge [fontname=arial fontsize=10]\n\t\t\tia [color=green penwidth=3 rank=source shape=oval]\n\t\t\tia -> ib [label="" arrowhead=normal color=black penwidth=1]\n\t\t\tib [color=red penwidth=3 rank=max shape=oval]\n\t\t\tlabel=icomp\n\t\t}\n\t\tlabel=mcomp\n\t}\n}' gv = ocomp.show_graph(show_nested=2, show_cim=True, show_node_structure=True, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> "mcomp INPUT":"InputPort-INPUT_CIM_ma_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ma[slope] ControlSignal" -> "mcomp CONTROL":"InputPort-PARAMETER_CIM_ma_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t"mcomp OUTPUT":"OutputPort-OUTPUT_CIM_mb_RESULT" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ma[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tsubgraph cluster_mcomp {\n\t\tgraph [label=mcomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tma [label=<
RESULT
OutputPorts
Mechanism:
ma
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tma:"OutputPort-RESULT" -> "icomp INPUT":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp OUTPUT":"OutputPort-OUTPUT_CIM_ib_RESULT" -> mb:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"mcomp INPUT" [label=<
INPUT_CIM_ma_InputPort-0
OutputPorts
Mechanism:
mcomp Input_CIM
InputPorts
INPUT_CIM_ma_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"mcomp INPUT":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"mcomp CONTROL" [label=<
PARAMETER_CIM_ma_slope
OutputPorts
Mechanism:
mcomp Parameter_CIM
InputPorts
PARAMETER_CIM_ma_slope
> color=blue penwidth=1 rank=same shape=plaintext]\n\t\t"mcomp CONTROL":"OutputPort-PARAMETER_CIM_ma_slope" -> ma:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"mcomp OUTPUT" [label=<
OUTPUT_CIM_mb_RESULT
OutputPorts
Mechanism:
mcomp Output_CIM
InputPorts
OUTPUT_CIM_mb_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tmb:"OutputPort-RESULT" -> "mcomp OUTPUT":"InputPort-OUTPUT_CIM_mb_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tmb [label=<
RESULT
OutputPorts
Mechanism:
mb
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tsubgraph cluster_icomp {\n\t\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\t\tedge [fontname=arial fontsize=10]\n\t\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t\tia:"OutputPort-RESULT" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp OUTPUT" [label=<
OUTPUT_CIM_ib_RESULT
OutputPorts
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ib_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t\tib:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ib_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\tib [label=<
RESULT
OutputPorts
Mechanism:
ib
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\t\tlabel=icomp\n\t\t}\n\t\tlabel=mcomp\n\t}\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> "mcomp INPUT":"InputPort-INPUT_CIM_ma_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ma[slope] ControlSignal" -> "mcomp CONTROL":"InputPort-PARAMETER_CIM_ma_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t"mcomp OUTPUT":"OutputPort-OUTPUT_CIM_mb_RESULT" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ma[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tsubgraph cluster_mcomp {\n\t\tgraph [label=mcomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tma [label=<
RESULT
OutputPorts
Mechanism:
ma
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tma:"OutputPort-RESULT" -> "icomp INPUT":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp OUTPUT":"OutputPort-OUTPUT_CIM_ib_RESULT" -> mb:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"mcomp INPUT" [label=<
INPUT_CIM_ma_InputPort-0
OutputPorts
Mechanism:
mcomp Input_CIM
InputPorts
INPUT_CIM_ma_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"mcomp INPUT":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"mcomp CONTROL" [label=<
PARAMETER_CIM_ma_slope
OutputPorts
Mechanism:
mcomp Parameter_CIM
InputPorts
PARAMETER_CIM_ma_slope
> color=blue penwidth=1 rank=same shape=plaintext]\n\t\t"mcomp CONTROL":"OutputPort-PARAMETER_CIM_ma_slope" -> ma:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"mcomp OUTPUT" [label=<
OUTPUT_CIM_mb_RESULT
OutputPorts
Mechanism:
mcomp Output_CIM
InputPorts
OUTPUT_CIM_mb_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tmb:"OutputPort-RESULT" -> "mcomp OUTPUT":"InputPort-OUTPUT_CIM_mb_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tmb [label=<
RESULT
OutputPorts
Mechanism:
mb
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\tsubgraph cluster_icomp {\n\t\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\t\tedge [fontname=arial fontsize=10]\n\t\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\t\tia:"OutputPort-RESULT" -> ib:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp OUTPUT" [label=<
OUTPUT_CIM_ib_RESULT
OutputPorts
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ib_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t\tib:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ib_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\tib [label=<
RESULT
OutputPorts
Mechanism:
ib
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\t\t\tlabel=icomp\n\t\t}\n\t\tlabel=mcomp\n\t}\n}' def test_nested_learning(self): ia = ProcessingMechanism(name='INNER INPUT') @@ -380,17 +380,17 @@ def test_of_show_nested_show_cim_and_show_node_structure(self): gv = ocomp.show_graph(show_cim=True, show_nested=NESTED, output_fmt='source') assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [color=green penwidth=3 rank=source shape=oval]\n\toa -> "icomp INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech -> "icomp CONTROL" [label="" arrowhead=normal color=blue penwidth=1]\n\t"icomp OUTPUT" -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT" -> ob [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" -> oa [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [color=red penwidth=1 rank=same shape=rectangle]\n\tob -> "ocomp OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\toc -> "ocomp OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> "icomp CONTROL" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> "icomp CONTROL" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\tob [color=brown penwidth=3 rank=same shape=oval]\n\toc [color=red penwidth=3 rank=max shape=oval]\n\tctl_mech [color=blue penwidth=3 rank=max shape=octagon]\n\tocm [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\t"icomp INPUT" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT" -> ia [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp CONTROL" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp CONTROL" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL" -> ia [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}' gv = ocomp.show_graph(show_node_structure=True, show_nested=False, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' gv = ocomp.show_graph(show_node_structure=True, show_nested=INSET, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' gv = ocomp.show_graph(show_node_structure=True, show_nested=NESTED, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\tia:"OutputPort-RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\tia:"OutputPort-RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' gv = ocomp.show_graph(show_node_structure=True, show_cim=True, show_nested=False, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_ob_InputPort-0INPUT_CIM_oa_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_ob_RESULTOUTPUT_CIM_oc_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_ob_InputPort-0INPUT_CIM_oa_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_ob_RESULTOUTPUT_CIM_oc_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' gv = ocomp.show_graph(show_node_structure=True, show_cim=True, show_nested=INSET, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_ob_InputPort-0INPUT_CIM_oa_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_ob_RESULTOUTPUT_CIM_oc_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp CONTROL" [label=<
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
icomp Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT" [label=<
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tia:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_ob_InputPort-0INPUT_CIM_oa_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_ob_RESULTOUTPUT_CIM_oc_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp CONTROL" [label=<
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
icomp Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT" [label=<
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tia:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' gv = ocomp.show_graph(show_node_structure=True, show_cim=True, show_nested=NESTED, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> "icomp INPUT":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t"icomp OUTPUT":"OutputPort-OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_ob_InputPort-0INPUT_CIM_oa_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_ob_RESULTOUTPUT_CIM_oc_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp CONTROL" [label=<
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
icomp Parameter_CIM
InputPorts
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT" [label=<
OUTPUT_CIM_ia_RESULT
OutputPorts
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tia:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> "icomp INPUT":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t"icomp OUTPUT":"OutputPort-OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_ob_InputPort-0INPUT_CIM_oa_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_ob_RESULTOUTPUT_CIM_oc_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp CONTROL" [label=<
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
icomp Parameter_CIM
InputPorts
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT" [label=<
OUTPUT_CIM_ia_RESULT
OutputPorts
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tia:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' def test_of_show_3_level_nested_show_cim_and_show_node_structure(self): @@ -430,17 +430,17 @@ def test_of_show_3_level_nested_show_cim_and_show_node_structure(self): gv = ocomp.show_graph(show_cim=True, show_nested=NESTED, output_fmt='source') assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [color=green penwidth=3 rank=source shape=oval]\n\toa -> "midcomp INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech -> "midcomp CONTROL" [label="" arrowhead=normal color=blue penwidth=1]\n\t"midcomp OUTPUT" -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT" -> oa [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" -> ob [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tob -> "ocomp OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> "midcomp CONTROL" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> "midcomp CONTROL" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\toc [color=red penwidth=3 rank=max shape=oval]\n\tctl_mech [color=blue penwidth=3 rank=max shape=octagon]\n\tob [color=brown penwidth=3 rank=same shape=oval]\n\tocm [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph cluster_midcomp {\n\t\tgraph [label=midcomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tma [color=green penwidth=3 rank=source shape=oval]\n\t\tma -> "icomp INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp INPUT" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp INPUT" -> ma [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp CONTROL" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"midcomp CONTROL" -> "icomp CONTROL" [label="" arrowhead=normal color=blue penwidth=1]\n\t\t"midcomp CONTROL" -> "icomp CONTROL" [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp CONTROL" -> "icomp CONTROL" [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp OUTPUT" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t"icomp OUTPUT" -> "midcomp OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tsubgraph cluster_icomp {\n\t\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\t\tedge [fontname=arial fontsize=10]\n\t\t\t"icomp INPUT" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp INPUT" -> ia [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp CONTROL" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t\t"icomp CONTROL" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp CONTROL" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp CONTROL" -> ia [label="" arrowhead=box color=blue penwidth=1]\n\t\t\t"icomp OUTPUT" [color=red penwidth=1 rank=same shape=rectangle]\n\t\t\tia -> "icomp OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\t\tcolor=red\n\t\t\tlabel=icomp\n\t\t}\n\t\tlabel=midcomp\n\t}\n}' gv = ocomp.show_graph(show_node_structure=True, show_nested=False, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' gv = ocomp.show_graph(show_node_structure=True, show_nested=INSET, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_midcomp {\n\t\tgraph [label=midcomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tma [label=<
RESULT
OutputPorts
Mechanism:
ma
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\ticomp [color=red penwidth=3 rank=max shape=rectangle]\n\t\tma:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\t\tsubgraph cluster_icomp {\n\t\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\t\tedge [fontname=arial fontsize=10]\n\t\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\t\tcolor=red\n\t\t\tlabel=icomp\n\t\t}\n\t\tlabel=midcomp\n\t}\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_midcomp {\n\t\tgraph [label=midcomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tma [label=<
RESULT
OutputPorts
Mechanism:
ma
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\ticomp [color=red penwidth=3 rank=max shape=rectangle]\n\t\tma:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\t\tsubgraph cluster_icomp {\n\t\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\t\tedge [fontname=arial fontsize=10]\n\t\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\t\tcolor=red\n\t\t\tlabel=icomp\n\t\t}\n\t\tlabel=midcomp\n\t}\n}' gv = ocomp.show_graph(show_node_structure=True, show_nested=NESTED, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_midcomp {\n\t\tgraph [label=midcomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tma [label=<
RESULT
OutputPorts
Mechanism:
ma
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tma:"OutputPort-RESULT" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tsubgraph cluster_icomp {\n\t\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\t\tedge [fontname=arial fontsize=10]\n\t\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\t\tcolor=red\n\t\t\tlabel=icomp\n\t\t}\n\t\tlabel=midcomp\n\t}\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_midcomp {\n\t\tgraph [label=midcomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tma [label=<
RESULT
OutputPorts
Mechanism:
ma
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tma:"OutputPort-RESULT" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\tsubgraph cluster_icomp {\n\t\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\t\tedge [fontname=arial fontsize=10]\n\t\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\t\tcolor=red\n\t\t\tlabel=icomp\n\t\t}\n\t\tlabel=midcomp\n\t}\n}' gv = ocomp.show_graph(show_node_structure=True, show_cim=True, show_nested=False, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0INPUT_CIM_ob_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_oc_RESULTOUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> midcomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0INPUT_CIM_ob_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_oc_RESULTOUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' gv = ocomp.show_graph(show_node_structure=True, show_cim=True, show_nested=INSET, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> midcomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0INPUT_CIM_ob_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_oc_RESULTOUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_midcomp {\n\t\tgraph [label=midcomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tma [label=<
RESULT
OutputPorts
Mechanism:
ma
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\ticomp [color=red penwidth=3 rank=max shape=rectangle]\n\t\tma:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp INPUT" [label=<
INPUT_CIM_ma_InputPort-0
OutputPorts
Mechanism:
midcomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp INPUT":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp CONTROL" [label=<
PARAMETER_CIM_ia_slopePARAMETER_CIM_ia_noisePARAMETER_CIM_ia_intercept
OutputPorts
Mechanism:
midcomp Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\t\t"midcomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp OUTPUT" [label=<
Mechanism:
midcomp Output_CIM
InputPorts
OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\ticomp -> "midcomp OUTPUT":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tsubgraph cluster_icomp {\n\t\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\t\tedge [fontname=arial fontsize=10]\n\t\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp CONTROL" [label=<
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
icomp Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t\t"icomp OUTPUT" [label=<
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t\tia:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\t\tcolor=red\n\t\t\tlabel=icomp\n\t\t}\n\t\tlabel=midcomp\n\t}\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\tmidcomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> midcomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> midcomp [label="" arrowhead=normal color=black penwidth=1]\n\tmidcomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0INPUT_CIM_ob_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_oc_RESULTOUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> midcomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_midcomp {\n\t\tgraph [label=midcomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tma [label=<
RESULT
OutputPorts
Mechanism:
ma
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\ticomp [color=red penwidth=3 rank=max shape=rectangle]\n\t\tma:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp INPUT" [label=<
INPUT_CIM_ma_InputPort-0
OutputPorts
Mechanism:
midcomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp INPUT":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp CONTROL" [label=<
PARAMETER_CIM_ia_slopePARAMETER_CIM_ia_noisePARAMETER_CIM_ia_intercept
OutputPorts
Mechanism:
midcomp Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\t\t"midcomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp OUTPUT" [label=<
Mechanism:
midcomp Output_CIM
InputPorts
OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\ticomp -> "midcomp OUTPUT":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tsubgraph cluster_icomp {\n\t\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\t\tedge [fontname=arial fontsize=10]\n\t\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp CONTROL" [label=<
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
icomp Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t\t"icomp OUTPUT" [label=<
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t\tia:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\t\tcolor=red\n\t\t\tlabel=icomp\n\t\t}\n\t\tlabel=midcomp\n\t}\n}' gv = ocomp.show_graph(show_node_structure=True, show_cim=True, show_nested=NESTED, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> "midcomp INPUT":"InputPort-INPUT_CIM_ma_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "midcomp CONTROL":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t"midcomp OUTPUT":"OutputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0INPUT_CIM_ob_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_oc_RESULTOUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "midcomp CONTROL":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "midcomp CONTROL":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_midcomp {\n\t\tgraph [label=midcomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tma [label=<
RESULT
OutputPorts
Mechanism:
ma
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tma:"OutputPort-RESULT" -> "icomp INPUT":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp INPUT" [label=<
INPUT_CIM_ma_InputPort-0
OutputPorts
Mechanism:
midcomp Input_CIM
InputPorts
INPUT_CIM_ma_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp INPUT":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp CONTROL" [label=<
PARAMETER_CIM_ia_slopePARAMETER_CIM_ia_noisePARAMETER_CIM_ia_intercept
OutputPorts
Mechanism:
midcomp Parameter_CIM
InputPorts
PARAMETER_CIM_ia_slopePARAMETER_CIM_ia_noisePARAMETER_CIM_ia_intercept
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t\t"midcomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp OUTPUT" [label=<
OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT
OutputPorts
Mechanism:
midcomp Output_CIM
InputPorts
OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"icomp OUTPUT":"OutputPort-OUTPUT_CIM_ia_RESULT" -> "midcomp OUTPUT":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tsubgraph cluster_icomp {\n\t\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\t\tedge [fontname=arial fontsize=10]\n\t\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp CONTROL" [label=<
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
icomp Parameter_CIM
InputPorts
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t\t"icomp OUTPUT" [label=<
OUTPUT_CIM_ia_RESULT
OutputPorts
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t\tia:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\t\tcolor=red\n\t\t\tlabel=icomp\n\t\t}\n\t\tlabel=midcomp\n\t}\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> "midcomp INPUT":"InputPort-INPUT_CIM_ma_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "midcomp CONTROL":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t"midcomp OUTPUT":"OutputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0INPUT_CIM_ob_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_oc_RESULTOUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "midcomp CONTROL":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "midcomp CONTROL":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_midcomp {\n\t\tgraph [label=midcomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tma [label=<
RESULT
OutputPorts
Mechanism:
ma
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\t\tma:"OutputPort-RESULT" -> "icomp INPUT":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp INPUT" [label=<
INPUT_CIM_ma_InputPort-0
OutputPorts
Mechanism:
midcomp Input_CIM
InputPorts
INPUT_CIM_ma_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp INPUT":"OutputPort-INPUT_CIM_ma_InputPort-0" -> ma:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"midcomp CONTROL" [label=<
PARAMETER_CIM_ia_slopePARAMETER_CIM_ia_noisePARAMETER_CIM_ia_intercept
OutputPorts
Mechanism:
midcomp Parameter_CIM
InputPorts
PARAMETER_CIM_ia_slopePARAMETER_CIM_ia_noisePARAMETER_CIM_ia_intercept
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"midcomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t\t"midcomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1]\n\t\t"midcomp OUTPUT" [label=<
OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT
OutputPorts
Mechanism:
midcomp Output_CIM
InputPorts
OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t"icomp OUTPUT":"OutputPort-OUTPUT_CIM_ia_RESULT" -> "midcomp OUTPUT":"InputPort-OUTPUT_CIM_icomp_OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tsubgraph cluster_icomp {\n\t\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\t\tedge [fontname=arial fontsize=10]\n\t\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\t"icomp CONTROL" [label=<
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
icomp Parameter_CIM
InputPorts
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t\t"icomp OUTPUT" [label=<
OUTPUT_CIM_ia_RESULT
OutputPorts
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\t\tia:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\t\tcolor=red\n\t\t\tlabel=icomp\n\t\t}\n\t\tlabel=midcomp\n\t}\n}' def test_of_show_nested_show_cim_and_show_node_structure_with_singleton_in_outer_comp_added_last(self): @@ -478,15 +478,15 @@ def test_of_show_nested_show_cim_and_show_node_structure_with_singleton_in_outer gv = ocomp.show_graph(show_cim=True, show_nested=NESTED, output_fmt='source') assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [color=green penwidth=3 rank=source shape=oval]\n\toa -> "icomp INPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech -> "icomp CONTROL" [label="" arrowhead=normal color=blue penwidth=1]\n\t"icomp OUTPUT" -> oc [label="" arrowhead=normal color=black penwidth=1]\n\toc -> ctl_mech [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [color=green penwidth=1 rank=same shape=rectangle]\n\t"ocomp INPUT" -> oa [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" -> ob [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [color=red penwidth=1 rank=same shape=rectangle]\n\toc -> "ocomp OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tob -> "ocomp OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm -> "icomp CONTROL" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> "icomp CONTROL" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm -> oa [label="" arrowhead=box color=purple penwidth=1]\n\toc [color=red penwidth=3 rank=max shape=oval]\n\tctl_mech [color=blue penwidth=3 rank=max shape=octagon]\n\tob [color=brown penwidth=3 rank=same shape=oval]\n\tocm [color=purple penwidth=1 rank=min shape=doubleoctagon]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\t"icomp INPUT" [color=green penwidth=1 rank=same shape=rectangle]\n\t\t"icomp INPUT" -> ia [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp CONTROL" [color=purple penwidth=1 rank=same shape=rectangle]\n\t\t"icomp CONTROL" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL" -> ia [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL" -> ia [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT" [color=red penwidth=1 rank=same shape=rectangle]\n\t\tia -> "icomp OUTPUT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tia [color=brown penwidth=3 rank=same shape=oval]\n\t\tlabel=icomp\n\t}\n}' gv = ocomp.show_graph(show_node_structure=True, show_nested=False, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' gv = ocomp.show_graph(show_node_structure=True, show_nested=INSET, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' gv = ocomp.show_graph(show_node_structure=True, show_nested=NESTED, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\tia:"OutputPort-RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\tia:"OutputPort-RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' gv = ocomp.show_graph(show_node_structure=True, show_cim=True, show_nested=False, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0INPUT_CIM_ob_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_oc_RESULTOUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=box color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0INPUT_CIM_ob_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_oc_RESULTOUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=box color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n}' gv = ocomp.show_graph(show_node_structure=True, show_cim=True, show_nested=INSET, output_fmt='source') - assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0INPUT_CIM_ob_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_oc_RESULTOUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp CONTROL" [label=<
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
icomp Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT" [label=<
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tia:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' + assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\ticomp [color=pink penwidth=3 rank=same shape=rectangle]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> icomp [label="" arrowhead=normal color=blue penwidth=1]\n\toa:"OutputPort-RESULT" -> icomp [label="" arrowhead=normal color=black penwidth=1]\n\ticomp -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0INPUT_CIM_ob_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_oc_RESULTOUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> icomp [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp CONTROL" [label=<
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
icomp Parameter_CIM
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT" [label=<
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tia:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' gv = ocomp.show_graph(show_node_structure=True, show_cim=True, show_nested=NESTED, output_fmt='source') # FIX: NEEDS TO BE CORRECTED ONCE BUG IS FIXED (SEE MESSAGE FOR COMMIT eb61303808ad2a5ba46fdd18d0e583283397915c) - # assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> "icomp INPUT":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t"icomp OUTPUT":"OutputPort-OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_ob_InputPort-0INPUT_CIM_oa_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_ob_RESULTOUTPUT_CIM_oc_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp CONTROL" [label=<
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
icomp Parameter_CIM
InputPorts
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT" [label=<
OUTPUT_CIM_ia_RESULT
OutputPorts
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tia:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
integration_rate
intercept
noise
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' + # assert gv == 'digraph ocomp {\n\tgraph [label=ocomp overlap=False rankdir=BT]\n\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\tedge [fontname=arial fontsize=10]\n\toa [label=<
RESULT
OutputPorts
Mechanism:
oa
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=green penwidth=3 rank=source shape=plaintext]\n\toa:"OutputPort-RESULT" -> "icomp INPUT":"InputPort-INPUT_CIM_ia_InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\tctl_mech:"OutputPort-ia[slope] ControlSignal" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_slope" [label="" arrowhead=normal color=blue penwidth=1]\n\t"icomp OUTPUT":"OutputPort-OUTPUT_CIM_ia_RESULT" -> oc:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\toc:"OutputPort-RESULT" -> ctl_mech:"InputPort-OUTCOME" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT" [label=<
INPUT_CIM_oa_InputPort-0INPUT_CIM_ob_InputPort-0
OutputPorts
Mechanism:
ocomp Input_CIM
> color=green penwidth=1 rank=same shape=plaintext]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_oa_InputPort-0" -> oa:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp INPUT":"OutputPort-INPUT_CIM_ob_InputPort-0" -> ob:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t"ocomp OUTPUT" [label=<
Mechanism:
ocomp Output_CIM
InputPorts
OUTPUT_CIM_oc_RESULTOUTPUT_CIM_ob_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\toc:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_oc_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tob:"OutputPort-RESULT" -> "ocomp OUTPUT":"InputPort-OUTPUT_CIM_ob_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\tocm:"OutputPort-ia[noise] ControlSignal" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_noise" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-ia[intercept] ControlSignal" -> "icomp CONTROL":"InputPort-PARAMETER_CIM_ia_intercept" [label="" arrowhead=normal color=purple penwidth=1]\n\tocm:"OutputPort-oa[slope] ControlSignal" -> oa:"ParameterPort-slope" [label="" arrowhead=box color=purple penwidth=1]\n\toc [label=<
RESULT
OutputPorts
Mechanism:
oc
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=red penwidth=3 rank=max shape=plaintext]\n\tctl_mech [label=<
ia[slope] ControlSignal
OutputPorts
Mechanism:
ctl_mech
InputPorts
OUTCOME
> color=blue penwidth=3 rank=max shape=plaintext]\n\tob [label=<
RESULT
OutputPorts
Mechanism:
ob
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\tocm [label=<
ia[noise] ControlSignalia[intercept] ControlSignaloa[slope] ControlSignal
OutputPorts
Mechanism:
ocm
InputPorts
OUTCOMEOUTCOME-1
> color=purple penwidth=1 rank=min shape=plaintext]\n\tsubgraph cluster_icomp {\n\t\tgraph [label=icomp overlap=False rankdir=BT]\n\t\tnode [color=black fontname=arial fontsize=12 penwidth=1 shape=record]\n\t\tedge [fontname=arial fontsize=10]\n\t\t"icomp INPUT" [label=<
INPUT_CIM_ia_InputPort-0
OutputPorts
Mechanism:
icomp Input_CIM
InputPorts
INPUT_CIM_ia_InputPort-0
> color=green penwidth=1 rank=same shape=plaintext]\n\t\t"icomp INPUT":"OutputPort-INPUT_CIM_ia_InputPort-0" -> ia:"InputPort-InputPort-0" [label="" arrowhead=normal color=black penwidth=1]\n\t\t"icomp CONTROL" [label=<
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
OutputPorts
Mechanism:
icomp Parameter_CIM
InputPorts
PARAMETER_CIM_ia_interceptPARAMETER_CIM_ia_noisePARAMETER_CIM_ia_slope
> color=purple penwidth=1 rank=same shape=plaintext]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_intercept" -> ia:"ParameterPort-intercept" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_noise" -> ia:"ParameterPort-noise" [label="" arrowhead=box color=purple penwidth=1]\n\t\t"icomp CONTROL":"OutputPort-PARAMETER_CIM_ia_slope" -> ia:"ParameterPort-slope" [label="" arrowhead=box color=blue penwidth=1]\n\t\t"icomp OUTPUT" [label=<
OUTPUT_CIM_ia_RESULT
OutputPorts
Mechanism:
icomp Output_CIM
InputPorts
OUTPUT_CIM_ia_RESULT
> color=red penwidth=1 rank=same shape=plaintext]\n\t\tia:"OutputPort-RESULT" -> "icomp OUTPUT":"InputPort-OUTPUT_CIM_ia_RESULT" [label="" arrowhead=normal color=black penwidth=1]\n\t\tia [label=<
RESULT
OutputPorts
Mechanism:
ia
ParameterPorts
intercept
noise
offset
rate
slope
InputPorts
InputPort-0
> color=brown penwidth=3 rank=same shape=plaintext]\n\t\tlabel=icomp\n\t}\n}' diff --git a/tests/functions/test_user_defined_func.py b/tests/functions/test_user_defined_func.py index 18c169a039e..044e9f080f1 100644 --- a/tests/functions/test_user_defined_func.py +++ b/tests/functions/test_user_defined_func.py @@ -9,11 +9,186 @@ import psyneulink.core.llvm as pnlvm +class TestBinaryOperations: + @pytest.mark.parametrize("param1, param2", [ + (1, 2), # scalar - scalar + (np.ones(2), 2), # vec - scalar + (2, np.ones(2)), # scalar - vec + (np.ones((2, 2)), 2), # mat - scalar + (2, np.ones((2, 2))), # scalar - mat + (np.ones(2), np.array([1, 2])), # vec - vec + (np.ones((2, 2)), np.array([[1, 2], [3, 4]])), # mat - mat + ]) + @pytest.mark.parametrize("bin_execute", ['Python', + pytest.param('LLVM', marks=pytest.mark.llvm), + pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + ]) + @pytest.mark.benchmark(group="Function UDF") + def test_user_def_func_add(self, param1, param2, bin_execute, benchmark): + # default val is same shape as expected output + def myFunction(_, param1, param2): + # we only use param1 and param2 to avoid automatic shape changes of the variable + return param1 + param2 + + U = UserDefinedFunction(custom_function=myFunction, param1=param1, param2=param2) + if bin_execute == 'LLVM': + e = pnlvm.execution.FuncExecution(U).execute + elif bin_execute == 'PTX': + e = pnlvm.execution.FuncExecution(U).cuda_execute + else: + e = U + val = benchmark(e, 0) + assert np.allclose(val, param1 + param2) + + @pytest.mark.parametrize("param1, param2", [ + (1, 2), # scalar - scalar + (np.ones(2), 2), # vec - scalar + (2, np.ones(2)), # scalar - vec + (np.ones((2, 2)), 2), # mat - scalar + (2, np.ones((2, 2))), # scalar - mat + (np.ones(2), np.array([1, 2])), # vec - vec + (np.ones((2, 2)), np.array([[1, 2], [3, 4]])), # mat - mat + ]) + @pytest.mark.parametrize("bin_execute", ['Python', + pytest.param('LLVM', marks=pytest.mark.llvm), + pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + ]) + @pytest.mark.benchmark(group="Function UDF") + def test_user_def_func_mul(self, param1, param2, bin_execute, benchmark): + # default val is same shape as expected output + def myFunction(_, param1, param2): + # we only use param1 and param2 to avoid automatic shape changes of the variable + return param1 * param2 + + U = UserDefinedFunction(custom_function=myFunction, param1=param1, param2=param2) + if bin_execute == 'LLVM': + e = pnlvm.execution.FuncExecution(U).execute + elif bin_execute == 'PTX': + e = pnlvm.execution.FuncExecution(U).cuda_execute + else: + e = U + val = benchmark(e, 0) + assert np.allclose(val, param1 * param2) + + @pytest.mark.parametrize("op", [ # parameter is string since compiled udf doesn't support closures as of present + "AND", + "OR", + ]) + @pytest.mark.parametrize("bin_execute", ['Python', + pytest.param('LLVM', marks=pytest.mark.llvm), + pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + ]) + @pytest.mark.benchmark(group="Function UDF") + def test_user_def_func_boolop(self, op, bin_execute, benchmark): + if op == "AND": + def myFunction(variable): + var1 = True + var2 = False + # compiled UDFs don't support python bool type outputs + if var1 and var2: + return 0.0 + else: + return 1.0 + elif op == "OR": + def myFunction(variable): + var1 = True + var2 = False + # compiled UDFs don't support python bool type outputs + if var1 or var2: + return 1.0 + else: + return 0.0 + + U = UserDefinedFunction(custom_function=myFunction, default_variable=[0]) + if bin_execute == 'LLVM': + e = pnlvm.execution.FuncExecution(U).execute + elif bin_execute == 'PTX': + e = pnlvm.execution.FuncExecution(U).cuda_execute + else: + e = U + val = benchmark(e, [0]) + assert val == 1.0 + + @pytest.mark.parametrize("op", [ # parameter is string since compiled udf doesn't support closures as of present + "Eq", + "NotEq", + "Lt", + "LtE", + "Gt", + "GtE", + ]) + @pytest.mark.parametrize("bin_execute", ['Python', + pytest.param('LLVM', marks=pytest.mark.llvm), + pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + ]) + @pytest.mark.benchmark(group="Function UDF") + def test_user_def_func_cmpop(self, op, bin_execute, benchmark): + if op == "Eq": + def myFunction(variable): + var1 = 1.0 + var2 = 1.0 + if var1 == var2: + return 1.0 + else: + return 0.0 + elif op == "NotEq": + def myFunction(variable): + var1 = 1.0 + var2 = 2.0 + if var1 != var2: + return 1.0 + else: + return 0.0 + elif op == "Lt": + def myFunction(variable): + var1 = 1.0 + var2 = 2.0 + if var1 < var2: + return 1.0 + else: + return 0.0 + elif op == "LtE": + def myFunction(variable): + var1 = 1.0 + var2 = 2.0 + var3 = 1.0 + if var1 <= var2 and var1 <= var3: + return 1.0 + else: + return 0.0 + elif op == "Gt": + def myFunction(variable): + var1 = 2.0 + var2 = 1.0 + if var1 > var2: + return 1.0 + else: + return 0.0 + elif op == "GtE": + def myFunction(variable): + var1 = 3.0 + var2 = 2.0 + var3 = 3.0 + if var1 >= var2 and var1 >= var3: + return 1.0 + else: + return 0.0 + + U = UserDefinedFunction(custom_function=myFunction, default_variable=[0]) + if bin_execute == 'LLVM': + e = pnlvm.execution.FuncExecution(U).execute + elif bin_execute == 'PTX': + e = pnlvm.execution.FuncExecution(U).cuda_execute + else: + e = U + val = benchmark(e, [0]) + assert val == 1.0 + class TestUserDefFunc: @pytest.mark.parametrize("bin_execute", ['Python', pytest.param('LLVM', marks=pytest.mark.llvm), - #pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), ]) @pytest.mark.benchmark(group="Function UDF") def test_user_def_func(self, bin_execute, benchmark): @@ -21,8 +196,10 @@ def myFunction(variable, param1, param2): return variable * 2 + param2 U = UserDefinedFunction(custom_function=myFunction, default_variable=[[0, 0]], param2=3) - if (bin_execute == 'LLVM'): + if bin_execute == 'LLVM': e = pnlvm.execution.FuncExecution(U).execute + elif bin_execute == 'PTX': + e = pnlvm.execution.FuncExecution(U).cuda_execute else: e = U val = benchmark(e, [1, 3]) @@ -30,7 +207,148 @@ def myFunction(variable, param1, param2): @pytest.mark.parametrize("bin_execute", ['Python', pytest.param('LLVM', marks=pytest.mark.llvm), - #pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + ]) + @pytest.mark.benchmark(group="Function UDF") + def test_user_def_func_branching(self, bin_execute, benchmark): + def myFunction(variable, param1, param2): + if variable[0][0] > 0 and variable[0][1] > 0: + return variable * 2 + param2 + else: + return variable * -2 + param2 + + U = UserDefinedFunction(custom_function=myFunction, default_variable=[[0, 0]], param2=3) + if bin_execute == 'LLVM': + e = pnlvm.execution.FuncExecution(U).execute + elif bin_execute == 'PTX': + e = pnlvm.execution.FuncExecution(U).cuda_execute + else: + e = U + val = benchmark(e, [[1, 3]]) + assert np.allclose(val, [[5, 9]]) + val2 = e([[-1, 3]]) + assert np.allclose(val2, [[5, -3]]) + + @pytest.mark.parametrize("bin_execute", ['Python', + pytest.param('LLVM', marks=pytest.mark.llvm), + pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + ]) + @pytest.mark.benchmark(group="Function UDF") + def test_user_def_func_variable_index(self, bin_execute, benchmark): + def myFunction(variable): + variable[0][0] = variable[0][0] + 5 + variable[0][1] = variable[0][1] + 7 + return variable + + U = UserDefinedFunction(custom_function=myFunction, default_variable=[[0, 0]]) + if bin_execute == 'LLVM': + e = pnlvm.execution.FuncExecution(U).execute + elif bin_execute == 'PTX': + e = pnlvm.execution.FuncExecution(U).cuda_execute + else: + e = U + val = benchmark(e, [[1, 3]]) + assert np.allclose(val, [[6, 10]]) + + @pytest.mark.parametrize("variable", [ + (1), # scalar + (np.ones((2))), # vec-2d + (np.ones((2, 2))) # mat + ]) + @pytest.mark.parametrize("bin_execute", ['Python', + pytest.param('LLVM', marks=pytest.mark.llvm), + pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + ]) + @pytest.mark.benchmark(group="Function UDF") + def test_user_def_func_usub(self, variable, bin_execute, benchmark): + def myFunction(variable, param): + return -param + + U = UserDefinedFunction(custom_function=myFunction, default_variable=variable, param=variable) + if bin_execute == 'LLVM': + e = pnlvm.execution.FuncExecution(U).execute + elif bin_execute == 'PTX': + e = pnlvm.execution.FuncExecution(U).cuda_execute + else: + e = U + val = benchmark(e, variable) + assert np.allclose(val, -variable) + + @pytest.mark.parametrize("dtype, expected", [ # parameter is string since compiled udf doesn't support closures as of present + ("SCALAR", 1.0), + ("VECTOR", [1,2]), + ("MATRIX", [[1,2],[3,4]]), + ("BOOL", 1.0), + ("TUPLE", (1, 2, 3, 4)) + ]) + @pytest.mark.parametrize("bin_execute", ['Python', + pytest.param('LLVM', marks=pytest.mark.llvm), + pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + ]) + @pytest.mark.benchmark(group="Function UDF") + def test_user_def_func_assign(self, dtype, expected, bin_execute, benchmark): + if dtype == "SCALAR": + def myFunction(variable): + var = 1.0 + return var + elif dtype == "VECTOR": + def myFunction(variable): + var = [1,2] + return var + elif dtype == "MATRIX": + def myFunction(variable): + var = [[1,2],[3,4]] + return var + elif dtype == "BOOL": + def myFunction(variable): + var = True + return 1.0 + elif dtype == "TUPLE": + def myFunction(variable): + var = (1, 2, 3, 4) + return var + + U = UserDefinedFunction(custom_function=myFunction, default_variable=0) + if bin_execute == 'LLVM': + e = pnlvm.execution.FuncExecution(U).execute + elif bin_execute == 'PTX': + e = pnlvm.execution.FuncExecution(U).cuda_execute + else: + e = U + val = benchmark(e, 0) + assert np.allclose(val, expected) + + @pytest.mark.parametrize("op,expected", [ # parameter is string since compiled udf doesn't support closures as of present + ("TANH", [0.76159416, 0.99505475]), + ("EXP", [2.71828183, 20.08553692]), + ]) + @pytest.mark.parametrize("bin_execute", ['Python', + pytest.param('LLVM', marks=pytest.mark.llvm), + pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + ]) + @pytest.mark.benchmark(group="Function UDF") + def test_user_def_func_numpy(self, op, expected, bin_execute, benchmark): + variable = [[1, 3]] + if op == "TANH": + def myFunction(variable): + return np.tanh(variable) + elif op == "EXP": + def myFunction(variable): + return np.exp(variable) + + U = UserDefinedFunction(custom_function=myFunction, default_variable=[[0, 0]]) + if bin_execute == 'LLVM': + e = pnlvm.execution.FuncExecution(U).execute + elif bin_execute == 'PTX': + e = pnlvm.execution.FuncExecution(U).cuda_execute + else: + e = U + val = benchmark(e, variable) + assert np.allclose(val, expected) + + @pytest.mark.parametrize("bin_execute", ['Python', + pytest.param('LLVM', marks=pytest.mark.llvm), + pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), ]) @pytest.mark.benchmark(group="UDF in Mechanism") def test_udf_in_mechanism(self, bin_execute, benchmark): @@ -40,8 +358,10 @@ def myFunction(variable, param1, param2): myMech = ProcessingMechanism(function=myFunction, size=4, name='myMech') # assert 'param1' in myMech.parameter_ports.names # <- FIX reinstate when problem with function params is fixed # assert 'param2' in myMech.parameter_ports.names # <- FIX reinstate when problem with function params is fixed - if (bin_execute == 'LLVM'): + if bin_execute == 'LLVM': e = pnlvm.execution.MechExecution(myMech).execute + elif bin_execute == 'PTX': + e = pnlvm.execution.MechExecution(myMech).cuda_execute else: e = myMech.execute val = benchmark(e, [-1, 2, 3, 4]) @@ -52,11 +372,12 @@ def myFunction(variable, param1, param2): pytest.param('LLVM', marks=pytest.mark.llvm), pytest.param('LLVMExec', marks=pytest.mark.llvm), pytest.param('LLVMRun', marks=pytest.mark.llvm), - #pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + pytest.param('PTXExec', marks=[pytest.mark.llvm, pytest.mark.cuda]), + pytest.param('PTXRun', marks=[pytest.mark.llvm, pytest.mark.cuda]), ]) @pytest.mark.benchmark(group="UDF as Composition Origin") def test_udf_composition_origin(self, bin_execute, benchmark): - def myFunction(variable, params, context): + def myFunction(variable, context): return [variable[0][1], variable[0][0]] myMech = ProcessingMechanism(function=myFunction, size=3, name='myMech') @@ -69,11 +390,12 @@ def myFunction(variable, params, context): pytest.param('LLVM', marks=pytest.mark.llvm), pytest.param('LLVMExec', marks=pytest.mark.llvm), pytest.param('LLVMRun', marks=pytest.mark.llvm), - #pytest.param('PTX', marks=[pytest.mark.llvm, pytest.mark.cuda]), + pytest.param('PTXExec', marks=[pytest.mark.llvm, pytest.mark.cuda]), + pytest.param('PTXRun', marks=[pytest.mark.llvm, pytest.mark.cuda]), ]) @pytest.mark.benchmark(group="UDF as Composition Terminal") def test_udf_composition_terminal(self, bin_execute, benchmark): - def myFunction(variable, params, context): + def myFunction(variable, context): return [variable[0][2], variable[0][0]] myMech = ProcessingMechanism(function=myFunction, size=3, name='myMech') @@ -85,7 +407,7 @@ def myFunction(variable, params, context): def test_udf_with_pnl_func(self): L = Logistic(gain=2) - def myFunction(variable, params, context): + def myFunction(variable, context): return L(variable) + 2 U = UserDefinedFunction(custom_function=myFunction, default_variable=[[0, 0, 0]]) @@ -145,4 +467,4 @@ def test_autogenerated_udf_creates_parameters(self, mech_with_autogenerated_udf) def test_autogenerated_udf_parameters_states_have_source(self, mech_with_autogenerated_udf): for p in mech_with_autogenerated_udf.parameter_ports: - assert p.source is mech_with_autogenerated_udf.function + assert p.source is getattr(mech_with_autogenerated_udf.function.parameters, p.name) diff --git a/tests/llvm/test_builtins_vector.py b/tests/llvm/test_builtins_vector.py index bb8f5b18ae8..9c581c88ae1 100644 --- a/tests/llvm/test_builtins_vector.py +++ b/tests/llvm/test_builtins_vector.py @@ -42,3 +42,15 @@ def test_vector_op(benchmark, op, y, llvm_y, builtin, result, mode): benchmark(llvm_fun, ct_u, llvm_y, DIM_X, ct_res) res = llvm_res assert np.allclose(res, result) + +@pytest.mark.benchmark(group="Sum") +@pytest.mark.parametrize("mode", ['Python', + pytest.param('LLVM', marks=pytest.mark.llvm)]) +def test_vector_sum(benchmark, mode): + if mode == 'Python': + res = benchmark(np.sum, u) + elif mode == 'LLVM': + llvm_fun = pnlvm.LLVMBinaryFunction.get("__pnl_builtin_vec_sum") + benchmark(llvm_fun, ct_u, DIM_X, ct_res) + res = llvm_res[0] + assert np.allclose(res, sum(u)) diff --git a/tests/llvm/test_helpers.py b/tests/llvm/test_helpers.py index 34c9a592b6e..f64d9ec878d 100644 --- a/tests/llvm/test_helpers.py +++ b/tests/llvm/test_helpers.py @@ -220,3 +220,371 @@ def test_helper_printf(capfd, ir_argtype, format_spec, values_to_check): libc = ctypes.CDLL(libc) libc.fflush(0) assert capfd.readouterr().out == format_str % tuple(values_to_check) + +class TestHelperTypegetters: + FLOAT_TYPE = pnlvm.ir.FloatType() + FLOAT_PTR_TYPE = pnlvm.ir.PointerType(FLOAT_TYPE) + DOUBLE_TYPE = pnlvm.ir.DoubleType() + DOUBLE_PTR_TYPE = pnlvm.ir.PointerType(DOUBLE_TYPE) + DOUBLE_VECTOR_TYPE = pnlvm.ir.ArrayType(pnlvm.ir.DoubleType(), 1) + DOUBLE_VECTOR_PTR_TYPE = pnlvm.ir.PointerType(DOUBLE_VECTOR_TYPE) + DOUBLE_MATRIX_TYPE = pnlvm.ir.ArrayType(pnlvm.ir.ArrayType(pnlvm.ir.DoubleType(), 1), 1) + DOUBLE_MATRIX_PTR_TYPE = pnlvm.ir.PointerType(DOUBLE_MATRIX_TYPE) + INT_TYPE = pnlvm.ir.IntType(32) + INT_PTR_TYPE = pnlvm.ir.PointerType(pnlvm.ir.IntType(32)) + BOOL_TYPE = pnlvm.ir.IntType(1) + BOOL_PTR_TYPE = pnlvm.ir.PointerType(BOOL_TYPE) + + @pytest.mark.llvm + @pytest.mark.parametrize('mode', ['CPU', + pytest.param('PTX', marks=pytest.mark.cuda)]) + @pytest.mark.parametrize('ir_type,expected', [ + (FLOAT_TYPE, 0), + (FLOAT_PTR_TYPE, 1), + (DOUBLE_TYPE, 0), + (DOUBLE_PTR_TYPE, 1), + (DOUBLE_VECTOR_TYPE, 0), + (DOUBLE_VECTOR_PTR_TYPE, 1), + (DOUBLE_MATRIX_TYPE, 0), + (DOUBLE_MATRIX_PTR_TYPE, 1), + (INT_TYPE, 0), + (INT_PTR_TYPE, 1), + (BOOL_TYPE, 0), + (BOOL_PTR_TYPE, 1), + ], ids=str) + def test_helper_is_pointer(self, mode, ir_type, expected): + with pnlvm.LLVMBuilderContext() as ctx: + func_ty = ir.FunctionType(ir.VoidType(), [ir.IntType(32).as_pointer()]) + + custom_name = ctx.get_unique_name("is_pointer") + function = ir.Function(ctx.module, func_ty, name=custom_name) + out = function.args[0] + block = function.append_basic_block(name="entry") + builder = ir.IRBuilder(block) + + variable = builder.load(builder.alloca(ir_type)) + if pnlvm.helpers.is_pointer(variable): + builder.store(out.type.pointee(1), out) + else: + builder.store(out.type.pointee(0), out) + + builder.ret_void() + + bin_f = pnlvm.LLVMBinaryFunction.get(custom_name) + if mode == 'CPU': + res = bin_f.byref_arg_types[0](-1) + bin_f(ctypes.byref(res)) + res = res.value + else: + res = np.array([-1], dtype=np.int32) + bin_f.cuda_wrap_call(res) + res = res[0] + + assert res == expected + + @pytest.mark.llvm + @pytest.mark.parametrize('mode', ['CPU', + pytest.param('PTX', marks=pytest.mark.cuda)]) + @pytest.mark.parametrize('ir_type,expected', [ + (FLOAT_TYPE, 1), + (FLOAT_PTR_TYPE, 1), + (DOUBLE_TYPE, 1), + (DOUBLE_PTR_TYPE, 1), + (DOUBLE_VECTOR_TYPE, 0), + (DOUBLE_VECTOR_PTR_TYPE, 0), + (DOUBLE_MATRIX_TYPE, 0), + (DOUBLE_MATRIX_PTR_TYPE, 0), + (INT_TYPE, 1), + (INT_PTR_TYPE, 1), + (BOOL_TYPE, 1), + (BOOL_PTR_TYPE, 1), + ], ids=str) + def test_helper_is_scalar(self, mode, ir_type, expected): + with pnlvm.LLVMBuilderContext() as ctx: + func_ty = ir.FunctionType(ir.VoidType(), [ir.IntType(32).as_pointer()]) + + custom_name = ctx.get_unique_name("is_scalar") + function = ir.Function(ctx.module, func_ty, name=custom_name) + out = function.args[0] + block = function.append_basic_block(name="entry") + builder = ir.IRBuilder(block) + + variable = builder.load(builder.alloca(ir_type)) + if pnlvm.helpers.is_scalar(variable): + builder.store(out.type.pointee(1), out) + else: + builder.store(out.type.pointee(0), out) + + builder.ret_void() + + bin_f = pnlvm.LLVMBinaryFunction.get(custom_name) + if mode == 'CPU': + res = bin_f.byref_arg_types[0](-1) + bin_f(ctypes.byref(res)) + res = res.value + else: + res = np.array([-1], dtype=np.int32) + bin_f.cuda_wrap_call(res) + res = res[0] + + assert res == expected + + @pytest.mark.llvm + @pytest.mark.parametrize('mode', ['CPU', + pytest.param('PTX', marks=pytest.mark.cuda)]) + @pytest.mark.parametrize('ir_type,expected', [ + (FLOAT_TYPE, 1), + (FLOAT_PTR_TYPE, 1), + (DOUBLE_TYPE, 1), + (DOUBLE_PTR_TYPE, 1), + (DOUBLE_VECTOR_TYPE, 0), + (DOUBLE_VECTOR_PTR_TYPE, 0), + (DOUBLE_MATRIX_TYPE, 0), + (DOUBLE_MATRIX_PTR_TYPE, 0), + (INT_TYPE, 0), + (INT_PTR_TYPE, 0), + (BOOL_TYPE, 0), + (BOOL_PTR_TYPE, 0), + ], ids=str) + def test_helper_is_floating_point(self, mode, ir_type, expected): + with pnlvm.LLVMBuilderContext() as ctx: + func_ty = ir.FunctionType(ir.VoidType(), [ir.IntType(32).as_pointer()]) + + custom_name = ctx.get_unique_name("is_floating_point") + function = ir.Function(ctx.module, func_ty, name=custom_name) + out = function.args[0] + block = function.append_basic_block(name="entry") + builder = ir.IRBuilder(block) + + variable = builder.load(builder.alloca(ir_type)) + if pnlvm.helpers.is_floating_point(variable): + builder.store(out.type.pointee(1), out) + else: + builder.store(out.type.pointee(0), out) + + builder.ret_void() + + bin_f = pnlvm.LLVMBinaryFunction.get(custom_name) + if mode == 'CPU': + res = bin_f.byref_arg_types[0](-1) + bin_f(ctypes.byref(res)) + res = res.value + else: + res = np.array([-1], dtype=np.int32) + bin_f.cuda_wrap_call(res) + res = res[0] + + assert res == expected + + @pytest.mark.llvm + @pytest.mark.parametrize('mode', ['CPU', + pytest.param('PTX', marks=pytest.mark.cuda)]) + @pytest.mark.parametrize('ir_type,expected', [ + (FLOAT_TYPE, 0), + (FLOAT_PTR_TYPE, 0), + (DOUBLE_TYPE, 0), + (DOUBLE_PTR_TYPE, 0), + (DOUBLE_VECTOR_TYPE, 1), + (DOUBLE_VECTOR_PTR_TYPE, 1), + (DOUBLE_MATRIX_TYPE, 0), + (DOUBLE_MATRIX_PTR_TYPE, 0), + (INT_TYPE, 0), + (INT_PTR_TYPE, 0), + (BOOL_TYPE, 0), + (BOOL_PTR_TYPE, 0), + ], ids=str) + def test_helper_is_vector(self, mode, ir_type, expected): + with pnlvm.LLVMBuilderContext() as ctx: + func_ty = ir.FunctionType(ir.VoidType(), [ir.IntType(32).as_pointer()]) + + custom_name = ctx.get_unique_name("is_vector") + function = ir.Function(ctx.module, func_ty, name=custom_name) + out = function.args[0] + block = function.append_basic_block(name="entry") + builder = ir.IRBuilder(block) + + variable = builder.load(builder.alloca(ir_type)) + if pnlvm.helpers.is_vector(variable): + builder.store(out.type.pointee(1), out) + else: + builder.store(out.type.pointee(0), out) + + builder.ret_void() + + bin_f = pnlvm.LLVMBinaryFunction.get(custom_name) + if mode == 'CPU': + res = bin_f.byref_arg_types[0](-1) + bin_f(ctypes.byref(res)) + res = res.value + else: + res = np.array([-1], dtype=np.int32) + bin_f.cuda_wrap_call(res) + res = res[0] + + assert res == expected + + @pytest.mark.llvm + @pytest.mark.parametrize('mode', ['CPU', + pytest.param('PTX', marks=pytest.mark.cuda)]) + @pytest.mark.parametrize('ir_type,expected', [ + (FLOAT_TYPE, 0), + (FLOAT_PTR_TYPE, 0), + (DOUBLE_TYPE, 0), + (DOUBLE_PTR_TYPE, 0), + (DOUBLE_VECTOR_TYPE, 0), + (DOUBLE_VECTOR_PTR_TYPE, 0), + (DOUBLE_MATRIX_TYPE, 1), + (DOUBLE_MATRIX_PTR_TYPE, 1), + (INT_TYPE, 0), + (INT_PTR_TYPE, 0), + (BOOL_TYPE, 0), + (BOOL_PTR_TYPE, 0), + ], ids=str) + def test_helper_is_2d_matrix(self, mode, ir_type, expected): + with pnlvm.LLVMBuilderContext() as ctx: + func_ty = ir.FunctionType(ir.VoidType(), [ir.IntType(32).as_pointer()]) + + custom_name = ctx.get_unique_name("is_2d_matrix") + function = ir.Function(ctx.module, func_ty, name=custom_name) + out = function.args[0] + block = function.append_basic_block(name="entry") + builder = ir.IRBuilder(block) + + variable = builder.load(builder.alloca(ir_type)) + if pnlvm.helpers.is_2d_matrix(variable): + builder.store(out.type.pointee(1), out) + else: + builder.store(out.type.pointee(0), out) + + builder.ret_void() + + bin_f = pnlvm.LLVMBinaryFunction.get(custom_name) + if mode == 'CPU': + res = bin_f.byref_arg_types[0](-1) + bin_f(ctypes.byref(res)) + res = res.value + else: + res = np.array([-1], dtype=np.int32) + bin_f.cuda_wrap_call(res) + res = res[0] + + assert res == expected + + @pytest.mark.llvm + @pytest.mark.parametrize('mode', ['CPU', + pytest.param('PTX', marks=pytest.mark.cuda)]) + @pytest.mark.parametrize('ir_type,expected', [ + (FLOAT_TYPE, 0), + (FLOAT_PTR_TYPE, 0), + (DOUBLE_TYPE, 0), + (DOUBLE_PTR_TYPE, 0), + (DOUBLE_VECTOR_TYPE, 0), + (DOUBLE_VECTOR_PTR_TYPE, 0), + (DOUBLE_MATRIX_TYPE, 0), + (DOUBLE_MATRIX_PTR_TYPE, 0), + (INT_TYPE, 0), + (INT_PTR_TYPE, 0), + (BOOL_TYPE, 1), + (BOOL_PTR_TYPE, 1), + ], ids=str) + def test_helper_is_boolean(self, mode, ir_type, expected): + with pnlvm.LLVMBuilderContext() as ctx: + func_ty = ir.FunctionType(ir.VoidType(), [ir.IntType(32).as_pointer()]) + + custom_name = ctx.get_unique_name("is_boolean") + function = ir.Function(ctx.module, func_ty, name=custom_name) + out = function.args[0] + block = function.append_basic_block(name="entry") + builder = ir.IRBuilder(block) + + variable = builder.load(builder.alloca(ir_type)) + if pnlvm.helpers.is_boolean(variable): + builder.store(out.type.pointee(1), out) + else: + builder.store(out.type.pointee(0), out) + + builder.ret_void() + + bin_f = pnlvm.LLVMBinaryFunction.get(custom_name) + if mode == 'CPU': + res = bin_f.byref_arg_types[0](-1) + bin_f(ctypes.byref(res)) + res = res.value + else: + res = np.array([-1], dtype=np.int32) + bin_f.cuda_wrap_call(res) + res = res[0] + + assert res == expected + +@pytest.mark.llvm +@pytest.mark.parametrize('mode', ['CPU', + pytest.param('PTX', marks=pytest.mark.cuda)]) +@pytest.mark.parametrize('op,var,expected', [ + (pnlvm.helpers.tanh, 1.0, 0.7615941559557649), + (pnlvm.helpers.exp, 1.0, 2.718281828459045), + (pnlvm.helpers.coth, 1.0, 1.3130352854993313), + (pnlvm.helpers.csch, 1.0, 0.8509181282393215), +]) +def test_helper_numerical(mode, op, var, expected): + with pnlvm.LLVMBuilderContext() as ctx: + func_ty = ir.FunctionType(ir.VoidType(), [ctx.float_ty.as_pointer()]) + + custom_name = ctx.get_unique_name("numerical") + function = ir.Function(ctx.module, func_ty, name=custom_name) + in_out = function.args[0] + block = function.append_basic_block(name="entry") + builder = ir.IRBuilder(block) + + variable = builder.load(in_out) + result = op(ctx, builder, variable) + builder.store(result, in_out) + + builder.ret_void() + + bin_f = pnlvm.LLVMBinaryFunction.get(custom_name) + if mode == 'CPU': + res = bin_f.byref_arg_types[0](var) + bin_f(ctypes.byref(res)) + res = res.value + else: + # FIXME: this needs to consider ctx.float_ty + res = np.array([var], dtype=np.float64) + bin_f.cuda_wrap_call(res) + res = res[0] + + assert res == expected + +@pytest.mark.llvm +@pytest.mark.parametrize('mode', ['CPU', + pytest.param('PTX', marks=pytest.mark.cuda)]) +@pytest.mark.parametrize('var,expected', [ + (np.array([1,2,3], dtype=np.float), np.array([2,3,4], dtype=np.float)), + (np.array([[1,2],[3,4]], dtype=np.float), np.array([[2,3],[4,5]], dtype=np.float)), +], ids=["vector", "matrix"]) +def test_helper_elementwise_op(mode, var, expected): + with pnlvm.LLVMBuilderContext() as ctx: + arr_ptr_ty = ctx.convert_python_struct_to_llvm_ir(var).as_pointer() + + func_ty = ir.FunctionType(ir.VoidType(), [arr_ptr_ty, arr_ptr_ty]) + + custom_name = ctx.get_unique_name("elementwise_op") + function = ir.Function(ctx.module, func_ty, name=custom_name) + inp, out = function.args + block = function.append_basic_block(name="entry") + builder = ir.IRBuilder(block) + + pnlvm.helpers.call_elementwise_operation(ctx, builder, inp, + lambda ctx, builder, x: builder.fadd(x.type(1.0), x), out) + builder.ret_void() + + bin_f = pnlvm.LLVMBinaryFunction.get(custom_name) + if mode == 'CPU': + ct_vec = var.ctypes.data_as(ctypes.POINTER(bin_f.byref_arg_types[0])) + res = bin_f.byref_arg_types[1]() + bin_f(ct_vec, ctypes.byref(res)) + else: + res = copy.deepcopy(var) + bin_f.cuda_wrap_call(var, res) + + assert np.array_equal(res, expected) diff --git a/tests/log/test_log.py b/tests/log/test_log.py index faf3055784d..78c05c732aa 100644 --- a/tests/log/test_log.py +++ b/tests/log/test_log.py @@ -42,6 +42,8 @@ def test_log(self): 'mod_intercept': 'OFF', 'mod_noise': 'OFF', 'mod_slope': 'OFF', + 'mod_offset': 'OFF', + 'mod_rate': 'OFF', 'noise': 'OFF', 'num_executions_before_finished': 'OFF', 'termination_measure_value': 'OFF', @@ -76,6 +78,8 @@ def test_log(self): 'mod_intercept': 'OFF', 'mod_noise': 'OFF', 'mod_slope': 'OFF', + 'mod_offset': 'OFF', + 'mod_rate': 'OFF', 'noise': 'OFF', 'num_executions_before_finished': 'OFF', 'termination_measure_value': 'OFF', @@ -134,6 +138,8 @@ def test_log(self): 'mod_intercept': 'OFF', 'mod_noise': 'EXECUTION', 'mod_slope': 'OFF', + 'mod_offset': 'OFF', + 'mod_rate': 'OFF', 'noise': 'OFF', 'num_executions_before_finished': 'OFF', 'termination_measure_value': 'OFF', @@ -167,6 +173,8 @@ def test_log(self): 'mod_intercept': 'OFF', 'mod_noise': 'OFF', 'mod_slope': 'OFF', + 'mod_offset': 'OFF', + 'mod_rate': 'OFF', 'noise': 'OFF', 'num_executions_before_finished': 'OFF', 'termination_measure_value': 'OFF', @@ -285,6 +293,8 @@ def test_log_dictionary_without_time(self): 'mod_intercept': 'OFF', 'mod_noise': 'OFF', 'mod_slope': 'OFF', + 'mod_offset': 'OFF', + 'mod_rate': 'OFF', 'noise': 'OFF', 'num_executions_before_finished': 'OFF', 'termination_measure_value': 'OFF', @@ -318,6 +328,8 @@ def test_log_dictionary_without_time(self): 'mod_intercept': 'OFF', 'mod_noise': 'OFF', 'mod_slope': 'OFF', + 'mod_offset': 'OFF', + 'mod_rate': 'OFF', 'num_executions_before_finished': 'OFF', 'noise': 'OFF', 'termination_measure_value': 'OFF', @@ -380,6 +392,8 @@ def test_log_dictionary_without_time(self): 'mod_intercept': 'OFF', 'mod_noise': 'OFF', 'mod_slope': 'EXECUTION', + 'mod_offset': 'OFF', + 'mod_rate': 'OFF', 'noise': 'OFF', 'num_executions_before_finished': 'OFF', 'termination_measure_value': 'OFF', @@ -413,6 +427,8 @@ def test_log_dictionary_without_time(self): 'mod_intercept': 'OFF', 'mod_noise': 'OFF', 'mod_slope': 'EXECUTION', + 'mod_offset': 'OFF', + 'mod_rate': 'OFF', 'noise': 'OFF', 'num_executions_before_finished': 'OFF', 'termination_measure_value': 'OFF', @@ -532,6 +548,8 @@ def test_log_dictionary_with_time(self): 'mod_intercept': 'OFF', 'mod_noise': 'OFF', 'mod_slope': 'OFF', + 'mod_offset': 'OFF', + 'mod_rate': 'OFF', 'noise': 'OFF', 'num_executions_before_finished': 'OFF', 'termination_measure_value': 'OFF', @@ -565,6 +583,8 @@ def test_log_dictionary_with_time(self): 'mod_intercept': 'OFF', 'mod_noise': 'OFF', 'mod_slope': 'OFF', + 'mod_offset': 'OFF', + 'mod_rate': 'OFF', 'noise': 'OFF', 'num_executions_before_finished': 'OFF', 'termination_measure_value': 'OFF', @@ -603,6 +623,8 @@ def test_log_dictionary_with_time(self): 'mod_intercept': 'OFF', 'mod_noise': 'OFF', 'mod_slope': 'EXECUTION', + 'mod_offset': 'OFF', + 'mod_rate': 'OFF', 'noise': 'OFF', 'num_executions_before_finished': 'OFF', 'termination_measure_value': 'OFF', @@ -641,6 +663,8 @@ def test_log_dictionary_with_time(self): 'mod_intercept': 'OFF', 'mod_noise': 'OFF', 'mod_slope': 'EXECUTION', + 'mod_offset': 'OFF', + 'mod_rate': 'OFF', 'noise': 'OFF', 'num_executions_before_finished': 'OFF', 'termination_measure_value': 'OFF', diff --git a/tests/mechanisms/test_transfer_mechanism.py b/tests/mechanisms/test_transfer_mechanism.py index 15d04a5cd78..7f81227a8bb 100644 --- a/tests/mechanisms/test_transfer_mechanism.py +++ b/tests/mechanisms/test_transfer_mechanism.py @@ -194,7 +194,7 @@ def test_transfer_mech_array_var_normal_len_1_noise(self): ) T.reset_stateful_function_when = Never() val = T.execute([0, 0, 0, 0]) - assert np.allclose(val, [[-1.56404341, -0.51529709, -2.55352727, 0.30284592]]) + assert np.allclose(val, [[0.90104733, 0.95869664, -0.81762271, -1.39225086]]) @pytest.mark.mechanism @pytest.mark.transfer_mechanism @@ -293,7 +293,7 @@ def test_transfer_mech_normal_noise(self): ) val = T.execute([0, 0, 0, 0]) - assert np.allclose(val, [[-1.56404341, -0.51529709, -2.55352727, 0.30284592]]) + assert np.allclose(val, [[0.90104733, 0.95869664, -0.81762271, -1.39225086]]) @pytest.mark.mechanism @pytest.mark.transfer_mechanism @@ -325,7 +325,7 @@ def test_transfer_mech_exponential_noise(self): integrator_mode=True, ) val = T.execute([0, 0, 0, 0]) - assert np.allclose(val, [[0.54571315, 0.29964231, 0.71595475, 0.51908319]]) + assert np.allclose(val, [[0.7025651, 0.33105989, 1.40978493, 0.9633011]]) @pytest.mark.mechanism @pytest.mark.transfer_mechanism @@ -368,7 +368,7 @@ def test_transfer_mech_Uniform_noise(self): integrator_mode=True ) val = T.execute([0, 0, 0, 0]) - assert np.allclose(val, [[0.42057158, 0.25891675, 0.51127472, 0.40493414]]) + assert np.allclose(val, [[0.50468686, 0.28183784, 0.7558042, 0.618369]]) @pytest.mark.mechanism @pytest.mark.transfer_mechanism @@ -383,7 +383,7 @@ def test_transfer_mech_Gamma_noise(self): integrator_mode=True ) val = T.execute([0, 0, 0, 0]) - assert np.allclose(val, [[0.54571315, 0.29964231, 0.71595475, 0.51908319]]) + assert np.allclose(val, [[0.7025651, 0.33105989, 1.40978493, 0.9633011]]) @pytest.mark.mechanism @pytest.mark.transfer_mechanism @@ -398,7 +398,7 @@ def test_transfer_mech_Wald_noise(self): integrator_mode=True ) val = T.execute([0, 0, 0, 0]) - assert np.allclose(val, [[0.11902672, 0.73955962, 8.38161237, 0.49600183]]) + assert np.allclose(val, [[1.0678432, 0.34512569, 1.07265769, 1.3550318]]) class TestTransferMechanismFunctions: @@ -1563,7 +1563,7 @@ def test_transfer_mech_2d_variable_noise(self): default_variable=[[0.0, 0.0], [0.0, 0.0]] ) val = T.execute([[1.0, 2.0], [3.0, 4.0]]) - assert np.allclose(val, [[1.6136458, 7.00036006], [12.09938081, 7.56874402]]) + assert np.allclose(val, [[4.80209467, 6.91739329], [5.36475458, 6.21549828]]) @pytest.mark.mechanism @pytest.mark.transfer_mechanism diff --git a/tests/misc/test_parameters.py b/tests/misc/test_parameters.py index c1c1bba45ed..0a6b8e577e6 100644 --- a/tests/misc/test_parameters.py +++ b/tests/misc/test_parameters.py @@ -298,6 +298,8 @@ def test_values(self, obj, parameter_name, source): (recurrent_mech, 'learning_function', 'stateful'), (recurrent_mech, 'learning_function', 'loggable'), (recurrent_mech.recurrent_projection, 'auto', 'modulable'), + (recurrent_mech, 'integration_rate', 'modulable'), + (recurrent_mech, 'noise', 'modulable'), ] ) def test_param_attrs_match(self, obj, parameter_name, attr_name): diff --git a/tests/ports/test_parameter_ports.py b/tests/ports/test_parameter_ports.py index 3c568a4a05c..a34be007d94 100644 --- a/tests/ports/test_parameter_ports.py +++ b/tests/ports/test_parameter_ports.py @@ -1,10 +1,12 @@ import numpy as np +import psyneulink as pnl import pytest from psyneulink.core.components.component import ComponentError from psyneulink.core.components.functions.transferfunctions import Linear from psyneulink.core.components.mechanisms.processing.transfermechanism import TransferMechanism + class TestParameterPorts: def test_inspect_function_params_slope_noise(self): A = TransferMechanism() @@ -141,3 +143,53 @@ def test_mod_param_error(self): with pytest.raises(ComponentError) as error_text: T.mod_slope = 20.0 assert "directly because it is computed by the ParameterPort" in str(error_text.value) + + +class TestParameterPortList: + @pytest.fixture + def transfer_mech(self): + return TransferMechanism(function=pnl.Logistic) + + def test_duplicate(self, transfer_mech): + assert 'offset-function' in transfer_mech.parameter_ports + assert 'offset-integrator_function' in transfer_mech.parameter_ports + + def test_duplicate_base_access_fails(self, transfer_mech): + with pytest.raises( + pnl.ParameterPortError, + match='Did you want offset-function or offset-integrator_function' + ): + transfer_mech.parameter_ports['offset'] + + def test_duplicate_sources(self, transfer_mech): + assert transfer_mech.parameter_ports['offset-function'].source is transfer_mech.function.parameters.offset + assert transfer_mech.parameter_ports['offset-integrator_function'].source is transfer_mech.integrator_function.parameters.offset + + def test_sharedparameter_different_name(self, transfer_mech): + assert transfer_mech.parameter_ports['integration_rate'] is transfer_mech.parameter_ports['rate'] + assert transfer_mech.parameter_ports['integration_rate'].source is transfer_mech.integrator_function.parameters.rate + + def test_alias_unique(self): + mech = pnl.LCAMechanism() + + assert mech.parameter_ports['leak'] is mech.parameter_ports['integration_rate'] + + def test_alias_duplicate(self): + mech = pnl.LCAMechanism(function=pnl.ReLU) + + assert mech.parameter_ports['leak-function'].source is mech.function.parameters.leak + assert mech.parameter_ports['leak-integrator_function'] is mech.parameter_ports['integration_rate'] + assert mech.parameter_ports['leak-integrator_function'].source is mech.integrator_function.parameters.rate + + def test_alias_duplicate_base_access_fails(self): + mech = pnl.LCAMechanism(function=pnl.ReLU) + + with pytest.raises( + pnl.ParameterPortError, + match='Did you want leak-function or rate' + ): + mech.parameter_ports['leak'] + + def test_multiple_ports_warning(self): + with pytest.warns(UserWarning, match='Multiple ParameterPorts will be created'): + TransferMechanism(function=pnl.Logistic) diff --git a/tutorial/PsyNeuLink Tutorial.ipynb b/tutorial/PsyNeuLink Tutorial.ipynb index eccc70d632c..21888bace75 100644 --- a/tutorial/PsyNeuLink Tutorial.ipynb +++ b/tutorial/PsyNeuLink Tutorial.ipynb @@ -113,7 +113,7 @@ "text": [ "Requirement already satisfied: psyneulink in /usr/local/lib/python3.6/dist-packages (0.5.3.2)\n", "Requirement already satisfied: typecheck-decorator==1.2 in /usr/local/lib/python3.6/dist-packages (from psyneulink) (1.2)\n", - "Requirement already satisfied: toposort==1.4 in /usr/local/lib/python3.6/dist-packages (from psyneulink) (1.4)\n", + "Requirement already satisfied: toposort in /usr/local/lib/python3.6/dist-packages (from psyneulink) (1.5)\n", "Requirement already satisfied: llvmlite in /usr/local/lib/python3.6/dist-packages (from psyneulink) (0.28.0)\n", "Requirement already satisfied: pillow in /usr/local/lib/python3.6/dist-packages (from psyneulink) (4.3.0)\n", "Requirement already satisfied: numpy<1.16 in /usr/local/lib/python3.6/dist-packages (from psyneulink) (1.15.4)\n",